Moon
Moon

Reputation: 35395

UI helper function does not have the subscription data in Meteor

I am fairly new to Meteor.

I have a template helper function which requires to work with data I am publishing from the server side. When the page loads, the value for profile below is undefined. But after that when I execute the same code snippet from browser's console, it works just fine. From what I understand, the template helper is being executed before the data is published. How can I wait until the data is published to run the UI helper?

Here is relevant code.


Helper Function

Template.header.helpers({  
  me: function() {
    var user = Meteor.users.findOne(Meteor.userId());
    if (user) {
      return Profiles.findOne({
        spid: user.profile.spid
     });
    }

    return null;
  }
});

HTML Template

<template name="header">
  <header class="ui fixed inverted menu">
    {{> thumb user=me}}
  </header>
</template>

Thumbnail Template

<template name="thumb">
  {{#with user}}
  <div class="thumb">
    <div class="text" style="background-color:{{color}}">
      {{initials name}}
    </div>
    <div class="pic" style="background-image:url('{{pic}}')"></div>
  </div>
  {{/with}}
</template>

Publications

Meteor.publish('profiles', function() {
  return Profiles.all();
});

Meteor.publish('departments', function() {
  return Departments.all();
});

Meteor.publish('requestServiceIds', function() {
  return Requests.getServiceIds();
});

Meteor.publish('relevantServices', function() {
  return Services.getRelevant(this.userId, 5);
});

Router

Router.configure({
  layoutTemplate: 'main',
  waitOn: function() {
    Deps.autorun(function() {
      Meteor.subscribe('profiles', Partitioner.group());
      Meteor.subscribe('departments', Partitioner.group());
    });
  }
});

Router.onBeforeAction(function() {
  if (this.route.getName() === 'not-authorized') return this.next();

  if (!Meteor.userId() || !Cookie.get('TKN')) {
    this.render('login');
  } else {
    this.next();
  }
});

Router.route('/', {
  name: 'home',
  waitOn: function() {
    Deps.autorun(function() {
      Meteor.subscribe('requestServiceIds', Partitioner.group());
      Meteor.subscribe('relevantServices', Partitioner.group());
    });
  }
});

---

UPDATE 1

I updated the the router a bit. But it did not seem to have had made any difference.

Router.configure({
  layoutTemplate: 'main',
  waitOn: function() {
    // Deps.autorun(function() {
    //   Meteor.subscribe('profiles', Partitioner.group());
    //   Meteor.subscribe('departments', Partitioner.group());
    // });

    return [
      Meteor.subscribe('profiles', Partitioner.group()),
      Meteor.subscribe('departments', Partitioner.group())
    ];
  }
});

Router.route('/', {
  name: 'home',
  waitOn: function() {
    // Deps.autorun(function() {
    //   Meteor.subscribe('requestServiceIds', Partitioner.group());
    //   Meteor.subscribe('relevantServices', Partitioner.group());
    // });

    return [
      Meteor.subscribe('requestServiceIds', Partitioner.group()),
      Meteor.subscribe('relevantServices', Partitioner.group())
    ];
  }
});

Upvotes: 0

Views: 95

Answers (1)

Billybobbonnet
Billybobbonnet

Reputation: 3226

Create a dumb loading template such as this one (using font awesome):

<template name="loading">
    <div class="loading">
        <i class="fa fa-circle-o-notch fa-4x fa-spin"></i>
    </div>
</template>

And try replacing your Router.configure() part with something like this:

Router.configure({
  layoutTemplate: 'main',
  action: function() {
        if(this.isReady()) { this.render(); } else {this.render("loading");}
    },
    isReady: function() {
        var subs = [
            Meteor.subscribe('profiles', Partitioner.group());
            Meteor.subscribe('departments', Partitioner.group());
        ];
        var ready = true;
        _.each(subs, function(sub) {
            if(!sub.ready())
            ready = false;
        });
        return ready;
    },

    data: function() {
        return {
            params: this.params || {},
            profiles: Profiles.find(),
            departments: Departments.find()
        };
    }
  }
});

I guess that could be achieved using waitOn, but since your way does not work, I give you a working recipe I use everyday. Code inspired from meteor Kitchen generated code.


To answer your comments:

Regarding the Action not being triggered, it might be because we tried to put in inside the Router.configure. I don't do it that way, here are some details on how I implement it.

First, I set a controller for each route inside a router.js file (where I have my Router.configure() function too. It looks like that:

Router.map(function () {
    this.route("login", {path: "/login", controller: "LoginController"});
    // other routes
}

Next, I create a controller that I store in the same folder than my client view template. It looks like that:

this.LoginController = RouteController.extend({
    template: "Login",


    yieldTemplates: {
        /*YIELD_TEMPLATES*/
    },

    onBeforeAction: function() {
        /*BEFORE_FUNCTION*/
        this.next();
    },

    action: function() {
        if(this.isReady()) { this.render(); } else { this.render("loading"); }
        /*ACTION_FUNCTION*/
    },

    isReady: function() {


        var subs = [
        ];
        var ready = true;
        _.each(subs, function(sub) {
            if(!sub.ready())
                ready = false;
        });
        return ready;
    },

    data: function() {


        return {
            params: this.params || {}
        };
        /*DATA_FUNCTION*/
    },

    onAfterAction: function() {
    }
});

So this action function is working when I extend a route using a controller. Maybe it will solve your issue.

For completeness sake, here is how my Router.configure() looks like:

Router.configure({
  templateNameConverter: "upperCamelCase",
  routeControllerNameConverter: "upperCamelCase",
  layoutTemplate: "layout",
  notFoundTemplate: "notFound",
  loadingTemplate: "loading",
 //I removed the rest because it is useless.
});

Upvotes: 0

Related Questions