Search code examples
javascriptmeteoriron-routermeteor-blaze

IronRouter get data in layout


We have a layout that needs the current data that is defined in our controller like this:

TripController = RouteController.extend({
    layoutTemplate: 'tripLayout',
    data: function () {
        return Trips.findOne({_id: this.params._id});
    }
});

Our problem seems like a data-race:

Most of the times Template.currentData() is null but sometimes (mostly while debugging) it is our data that we defined. The problem only occurs when we reload the page while we are in the tripLayout, but not when we enter the trip layout from another page.

Note that we thing that the `TripController is loaded, as the controller params is correctly set.

Template.tripLayout.onRendered(function() { // note, this is the layout template
    Iron.controller.params._id // correct id
    Template.currentData() // null
});

What we are trying to achieve is a split layout where the right side is always the same and the left side is filled by yield (see screenshot)layout

UPDATE (THIS UPDATE IS WRONG)

I think I found the error:

waitOn: function () {
    return [Meteor.subscribe('public-trips'),
        Meteor.subscribe('my-trips')];
}

As soon as I remove the array (so only one subscription), the

data: function () {
    return Trips.find({}).fetch();
}

does not return 0 anymore. I will look into it a bit more.

UPDATE2

So my initial assumption was wrong, subscribing to only one does not help. It is really just a race condition.

UPDATE3

I managed it to reproduce it: meteorpad

In the alerts, it shows the number of players it has. First time it is 0 and then 6. But the 0 should not appear, as we 'waitOn' it?!


Solution

  • We managed to solve the 'problem' (It is expected behaviour, but we found a workaround)! Details at https://github.com/iron-meteor/iron-router/issues/1464#issuecomment-158738943:

    Alright, here's how we fixed that problem:

    The first important thing is to to include the action function like so:

    action: function() {
        if (this.ready()) {
           this.render();
        }
    }
    

    That worked for almost everything, but we still had some issues. In our case, it was about templates that aren't rendered in using yield, and are thus out of Iron Router's control. Switch these over to content regions (using {{yield 'contentRegionName'}}) and then explicitly render them in the action function like so:

    action: function() {
        if (this.ready()) {
            this.render();
            this.render('contentRegionTemplate', {to: 'contentRegionName'});
        } 
    }
    

    You need these action functions, since the waitOn only means that the subs defined there will be added to a waitlist, but not that that waitlist will be waited on. Essentially, what happens now is that your action function is called twice, thanks to reactivity, and the templates are only rendered once the data is ready.