Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Leo Brown
Leo Brown
6,896 Points

Dropdown menu difficulties for Activity Feed using Handlebars

I've just completed the Activity Feed from "Advanced Social Features in Ruby on Rails" with Jason Seifer. It went pretty well, especially the second time through. However, I am hoping someone can help with a couple of small issues. I am using Rails 4, but without Turbolinks.

First, when I click on the Activity Feed link in my menu, it appears, but then never goes away, no matter how many times or how hard I click. I wonder if anyone else has experienced this? I have another dropdown menu in the same nav bar that works fine.

Second, I didn't catch in the video what part of the app is supposed to make the Activity notification go away once the user has viewed it. Or maybe that is not included, and it's just designed to show the most recent 5 or so? That would be fine, except then it wouldn't really be worth having the number.

Could these issues be because I'm using a partial for my header?

For both of these issues, I'd appreciate a point in the right direction. I'll post a few code bits that may be relevant, or please let me know if there is anything else I should post.

Thank you!

Leo

application.js

//manifest file
//
//= require jquery
//= require jquery_ujs
//= require js-routes
//= require bootstrap
//= require_tree .

window.loadedActivities = [];

var addActivity = function(item) {
    var found = false;
    for (var i = 0; i < window.loadedActivities.length; i++) {
        if (window.loadedActivities[i].id == item.id) {
            var found = true;
        }
    }

    if (!found) {
        window.loadedActivities.push(item);
        window.loadedActivities.sort(function(a, b) {
            var returnValue;
            if (a.created_at > b.created_at)
                returnValue = -1;
            if (b.created_at > a.created_at)
                returnValue = 1;
            if (a.created_at == b.created_at)
                returnValue = 0;
            return returnValue;
        });
    }
    return item;
}

var renderActivities = function() {
    var source = $('#activities-template').html();
    var template = Handlebars.compile(source);
    var html = template({
        activities: window.loadedActivities, 
        count: window.loadedActivities.length
    });
    var $activityFeedLink = $('li#activity-feed');
    $activityFeedLink.empty();
    $activityFeedLink.addClass('dropdown');
    $activityFeedLink.html(html);
    $activityFeedLink.find('a.dropdown-toggle').dropdown();
}

var pollActivity = function() {
    $.ajax({
        url: Routes.activities_path({format: 'json', since: window.lastFetch}),
        type: "GET",
        dataType: "json",
        success: function(data) {
            window.lastFetch = Math.floor((new Date).getTime() / 1000);
            if (data.length > 0) {
                for (var i = 0; i < data.length; i++) {
                    addActivity(data[i]);
                }
                renderActivities();
            }
        }
    });
}

Handlebars.registerHelper('activityFeedLink', function() {
    return new Handlebars.SafeString(Routes.activities_path());
});

Handlebars.registerHelper('activityLink', function() {
    var link, path, html;
    var activity = this;
    var linkText = activity.targetable_type.toLowerCase();

    switch (linkText) {
        case "list":
            path = Routes.list_path(activity.targetable_id);
            break;
        case "user":
            path = Routes.user_path(activity.targetable_id);
            //linkText = "friend";
            break;
    }

    //if (activity.action === 'deleted') {
    //  path = '#'
    //}

    html = "<li><a href='" + path + "'>" + this.user_name + " " + this.action + " a " + linkText +".</a></li>";

    return new Handlebars.SafeString( html );
});

window.pollInterval = window.setInterval( pollActivity, 5000 );
pollActivity();

activity.rb

class Activity < ActiveRecord::Base
    belongs_to :user
    belongs_to :targetable, polymorphic: true

    self.per_page = 100

    def self.for_user(user, options={})
        options[:page] ||= 1
        following_ids = user.following.map(&:id).push(user.id)
        collection = where("user_id in (?)", following_ids).
            order("created_at desc")
        if options[:since] && !options[:since].blank?
            since = DateTime.strptime( options[:since], '%s' )
            collection = collection.where("created_at > ?", since) if since
        end
        collection.page(options[:page])
    end

    def user_name
        user.name
    end

    def as_json(options={})
        super(
            only: [:action, :id, :targetable_id, :targetable_type, :created_at, :id],
            include: :targetable,
            methods: [:user_name]
        ).merge(options)
    end
end

application.html.erb

<!DOCTYPE html>
<html>

  <head>
    <title>
      <%= full_title(yield(:title)) %>
    </title>
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
    <%= csrf_meta_tags %>
    <%= render 'layouts/shim' %>
  </head>

  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <% flash.each do |message_type, message| %>
        <div class="alert alert-<%= message_type %>">
          <%= message %>
        </div>
      <% end %>
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div>
  </body>

  <script id="activities-template" type="text/x-handlebars-template">
    <a class="dropdown-toggle" href="#">activity feed({{count}})</a>
    <ul class="dropdown-menu">
    {{#each activities}}
      {{activityLink}}
    {{/each}}
    <li class="divider"></li>
    <li><a href="{{activityFeedLink}}">Activity Feed</a></li>
    </ul>
  </script>

</html>

_header.html.erb

<header class="navbar navbar-default navbar-static-top navbar-inverse">

  <div class="container">
  <%= link_to "the nutrition algorithm", root_path, id: "logo" %>
  </div>

  <div class="container">
    <nav>
      <ul class="nav navbar-nav navbar-right", id: "menu">
                <% if logged_in? %>
          <li><%= link_to "[new list]", addlist_path %></li>
          <li><%= link_to "[featured lists]",   featured_lists_path %></li>
            <li><%= link_to "[all users]", users_path %></li>
          <li id="activity-feed"><%= link_to "activity feed", activities_path %></li>
          <li class="dropdown">
            <a href="#" class="dropdown-toggle" data-toggle="dropdown">
              Account <b class="caret"></b>
            </a>
            <ul class="dropdown-menu">
              <li><%= link_to "profile", current_user %></li>
              <li><%= link_to "Settings", edit_user_path(current_user) %></li>
              <li class="divider"></li>
              <li>
                <%= link_to "[log out]", logout_path, method: "delete" %>
              </li>
            </ul>
          </li>
        <% else %>
          <li><%= link_to "[log in]", login_path %></li>
        <% end %>
      </ul>
    </nav>
  </div>
</header>

1 Answer

Leo Brown
Leo Brown
6,896 Points

I've solved half of my problem: now, the Activity Feed dropdown appears AND goes away on click. (But please help me with the other half, how to clear the activity feed after viewing recent notifications, if possible.)

For toggle functionality, I edited just a few selectors:

  • In _header.html.erb, added class="toggle" to the activities feed li.
  • In application.html.erb (in the Handlebars template), added data-toggle="dropdown" to the <a> tag.

That's it! Now it works: appears on click and goes away on click.

By the way, in my original question, for some reason, script id="activities-template" type="text/x-handlebars-template" and the closing /script don't show up surrounding the Handlebars template in my code sample in application.html.erb, but they are there.