Template helper code review

Hi guys,

I’ve been working on this template for over a week now and there are still things that just aren’t quite clicking yet. Of course, I haven’t implemented all the functionality I want, but this is the start. I’ve made some comments in the code but some of the things that jump out at me are:

  • I’m using a meteor package that wraps the bootstrap-toggle cdn. I’m initializing bootstrapToggle in the onRendered portion of the inner template that uses it. Occasionally it comes back saying bootstrapToggle is not a function. I’ve tried several different places, and the problem still comes up rarely.

  • In that same onRendered function I’m looking at some user settings. Is there any way to optimize this so it doesn’t fire every time a top level object property is changed, and just the properties in settings.

  • I had a problem with scrolling to the bottom of the chat area for a long time, but I believe it’s solved now.

Any other pointers or tips would be greatly appreciated. And if you need to see more code, like the actual templates or what packages I’m using please let me know.

    // chat_helpers.js
    /**
     * Functions available only within the scope of this file
     */
    var scrollToBottom = function() {
      var selector = $('.chat-window');
      if (selector[0]) {
        selector.scrollTop(selector[0].scrollHeight*10000);
      }
    };

    /**
     * Sidebar Helpers
     */
    Template.chatSidebarAvailableUsers.helpers({
      usersLoggedIn: function() {
        return Meteor.users.find();
      }
    });

    Template.chatSidebarUserInfo.helpers({
      getUserProfile: function() {
        // Subscribed only to logged in users, and only able to see profile/settings information
        var profileId = Session.get('profile') || Meteor.userId();
        return Meteor.users.find({_id: profileId}).fetch();
      }
    });

    /**
     * MainPanel Helpers
     */
    Template.chatMainPanelWindow.helpers({
      dateFormat: function(date) {
        return moment(date).format('MM/DD/YYYY hh:mm A');
      },
      messages: function() {
        return Messages.find({}, {sort: {date: 1}});
      },
      isOwner: function(userId) {
        return userId == Meteor.userId();
      },
      showTimestamp: function() {
        return (Meteor.user().settings || []).timestamp !== false;
      }
    });

    /**
     * Though the chatHome template is our main chat layout and it
     * includes all of the partials, helper functions we add here will
     * NOT be available to the partials.
     */
    Template.chatHome.helpers({

    });

    /**
     * Unlike helpers, events are available to all partial templates
     * loaded within the "chatHome" template, though it may be cleaner
     * and more legible if the events were placed on their corresponding
     * partial templates. This works because events are available to newly
     * created DOM elements.
     */
    Template.chatHome.events({
      "submit .new-message": function(event) {
        // Stop the form from submitting
        event.preventDefault();
        // Grab the value at message
        var message = event.target.message.value;
        // Call the method to add a chat message
        Meteor.call('addChatMessage', message);
        // Clear the form
        event.target.message.value = "";
        // Auto scroll to the bottom of the page
        scrollToBottom();
      },
      "click .btn-scroll": function(event) {
        scrollToBottom();
      },
      "click .btn-available-users": function(event) {
        $('.side-panel').addClass('hide');
        $('.available-users').removeClass('hide');
      },
      "click .btn-user-info": function(event) {
        $('.side-panel').addClass('hide');
        $('.user-info').removeClass('hide');
      },
      "click .btn-settings": function(event) {
        $('.side-panel').addClass('hide');
        $('.settings').removeClass('hide');
      }
    });

    /**
     * The template methods "onCreated" and "onRendered" fire automatically
     * and in that order. onRendered does not mean that data is finished being
     * inserted into the DOM - use Tracker.afterFlush
     */
    Template.chatHome.onCreated(function() {
      var self = this;
      self.subscribe("onlineProfiles");
      self.subscribe("messages", function() {
        Tracker.afterFlush(function() {
          window.requestAnimationFrame(scrollToBottom);
        });
      });
    });

    /**
     * Here we're going to update the view of the settings panel
     * based on the saved user settings. If a setting changes in
     * the database, the view will be updated accordingly.
     */
    Template.chatSidebarSettings.onRendered(function() {
      var self = this;

      // PROBLEM: Sometimes this returns with bootstrapToggle is not a function
      $('.toggle').bootstrapToggle();

      self.autorun(function() {
        if (!self.subscriptionsReady()) {
          return;
        }
        var settings = Meteor.user().settings;
        // Make sure the settings object exists. Users aren't initially created with settings.
        if (settings) {
          //timestamp default is on
          $('#toggle-timestamp').bootstrapToggle(settings.timestamp !== false ? 'on' : 'off');
        } else {
          // Set default state for items that are on by default
          $('#toggle-timestamp').bootstrapToggle('on');
        }
      });
    });