Search code examples
meteormeteor-accounts

In Meteor, how to short-circuit-assign a helper variable with user object?


I'm running the latest Meteor (v1.1.0.3) on OS X 10.6.8 in Firefox 39.0. I'm using accounts-ui and accounts-google for login management. I have a hand-rolled profile form with (among other things) a 'name' field. The initial value of this field should be either the name that is already set in their profile or the one that Google supplies.

I've defined the following template helper:

Template.profile_edit.helpers({
    my_name: (function () {var u=Meteor.user(); return u.profile.name || u.services.google.name;}())
});

I use the value in my template as {{ my_name }}. When I start meteor everything compiles just fine, but when I load the profile page I get the following Javascript error:

TypeError: u is undefined
...me: (function () {var u=Meteor.user(); return u.profile.name || u.services.googl...

Not immediately relevant, but just for completeness:

  • After the page loads, the 'name' field in the form is blank.
  • I am logged in.
  • When I pull up Meteor's mongo instance in my terminal I can see my user record; the name in the profile is NOT set, the name in the services.google.name IS set.

Why is this error happening and how can I solve it?


Solution

  • The Problem

    This is a common issue that people have when first starting out with Meteor. The problem is that when this helper is executed during page load, depending on the response time for the publication from the server, the data for the currently logged in user may not be available yet. This makes it seem intermittent because at times the data is published in time, and others it's not.

    Possible Solutions

    One possible solution is to install meteorhacks:fast-render. This will publish the logged in user (due to the null publication) with the initial html for the page and guarantee that the user is available when this helper is run. Data other than the currently logged in user will need to be properly set up as subscriptions on the router for fast render to take effect.

    The other solution, and one that will work without installation of a new package is to guard against undefined. This will effectively let the helper return undefined when there is no data, but once the data is available the helper will reactively rerun and the proper data will be returned.

    Template.profile_edit.helpers({
        my_name: function () {
            var u=Meteor.user();
            if(u){
              return u.profile.name || u.services.google.name;
            }
        }
    });
    

    Aside

    In your helper I notice that you are using syntax like my_name:(function(){}()). While this will give you what seems like a desired outcome, the problem is that you are immediately invoking this function and assigning its value to the my_name helper instead of assigning it a function that can be called multiple times when the value changes. This breaks reactivity and so the second solution would not work due to it's reliance on it.

    Template.profile_edit.helpers({
        my_name: (function () {
            /*
               This is immediately invoked and basically
               like using my_name: "bob"
            */ 
        }())
    });