1

I have a route like this:

Router.route('/box', function () {
    this.render('boxCanvasTpl');
},{
    name: 'box',
    layoutTemplate: 'appWrapperLoggedInTpl',
    waitOn: function() {
        console.log("Box route ran ok.");
        return [
            Meteor.subscribe('item_ownership_pub', function() {
                console.log("subscription 'item_ownership_pub' is ready.");
            }),
            Meteor.subscribe('my_items', function() {
                console.log("subscription 'my_items' is ready.");
            })
        ];
    }
});

... and I am clicking a link in a Template like this:

<a href="/box?box=123" class="box-num-items">My Link</a>

I receive the 'Box route ran ok.' message, but some reason the page does not navigate to the given URL. I have added console.log code in the funciton that is run when the 'boxCanvasTpl' is rendered, but these aren't showing in the browser console. It seems that something inbetween is stopping the templkate from re-rendering, but can't put my finger on it - any ideas?

JoeTidee
  • 24,754
  • 25
  • 104
  • 149
  • Does it work if you remove both of the `subscribe` callbacks? – David Weldon Jun 13 '15 at 23:27
  • This is difficult to test as the page that contains the aforementioned link is the page I am trying to navigate to. Would this be the problem, i.e. if I am on /box?box=111 and trying to navigate to /box?box=123 - would it be that the 'onRendered' funciton would not get re-run? – JoeTidee Jun 14 '15 at 00:20
  • I'm a little confused on what you're trying to do here. You've got parameters in the URL (`?box=123`) but you don't have any code for accessing those URL parameters using `this.params` - http://stackoverflow.com/questions/23050664/how-to-get-the-query-parameters-in-iron-router And yes, if you put a `console.log` inside of `Template.boxCanvasTpl.onRendered()` and it does not show up, it means that template was not rendered (or re-rendered, in your case). Guh I hate hate hate the formatting in Stack Overflow comments... – fuzzybabybunny Jun 14 '15 at 08:33
  • @fuzzybabybunny I am using Router.current().params.query.box outside of the Router (in my Template.onRendered function), but this is irrelevant to the problem. – JoeTidee Jun 14 '15 at 09:07
  • Ah, but it is relevant, since onRendered() isn't firing and thus your params are not being recognized. – fuzzybabybunny Jun 14 '15 at 09:18
  • In this particular question, the parameters are not causing the problem. – JoeTidee Jun 14 '15 at 10:07

1 Answers1

3

There are some properties of Iron Router that you need to be aware of.

Say that the user is currently already on /boxes and there is a box template that renders for that path. If you:

  • click on a link <a href="/boxes?box=123">Click Me</a>

or

  • click on a link <a href="{{pathFor 'box'}}">Click Me</a>

Iron Router will NOT re-render the template because it already exists on the page. It will also NOT re-render the template if the box template happens to be a partial template that is already rendered on the page that you're on and also exists on the page that you want to navigate to.

Since it doesn't re-render, any code you have inside Template.box.onRendered will also not run again.

This behavior is most common in your layout, header, and footer templates. For many users, these templates are used for all of a website's pages, regardless of path. Because the layout, header, and footer template is rendered on a person's first visit to the site, they won't be re-rendered ever again if the user decides to navigate to other parts of the site using the same templates, so the code inside Template.layout/header/footer.onRendered won't fire.

Also note - even if a reactive Spacebars helper changes the physical look of the layout / header / footer, it doesn't qualify as an actual render, so reactive updates to the template do not trigger the onRendered callback.

The lack of re-rendering is what gives Meteor that "snappy" feel.

EDIT

Try to code in a reactive, event-driven style. Try not to think too much in a render / re-render sense.

  1. You go to /box
  2. You click on a link for /box?box=2342
  3. Get your params or query in Iron Router

https://github.com/iron-meteor/iron-router/blob/devel/Guide.md#route-parameters

  1. In Iron Router use the data from the params or query to set the data context for the template.

  2. Grab stuff from the data context as needed inside of the template's .onRendered, .events, and .helpers callbacks.

  3. Set Session vars as necessary and use them in helpers to give reactive changes to the page without having to re-render a template. Also use events to trigger updates to the session vars to, again, trigger reactive changes to the page.

Try this:

afterwards, go to /test?BUNNIES=lalalala

check out the console logs

test.html

<template name="test">

    {{myData}}

</template>

test.js

Template.test.helpers({

  myData: function() {
    console.log("data context accessed from test.helpers: ", this);
    console.log("this.BUNNIES accessed from test.helpers: ", this.BUNNIES);
    return this.BUNNIES;
  }

});

Template.test.onRendered(function() {

  console.log("data context accessed from test.onRendered: ", this.data);

});

Template.test.events({

  'click': function(){
    console.log("data accessed from test.events: ", this);
  }

});

router.js

Router.route('/test', function() {
  console.log("routed!");
  this.render('test');

}, {
  name: 'test',
  data: function(){
    //here I am setting the data context
    // for /test?BUNNIES=1234
    var query = this.params.query;
    console.log("query: ", query);

    return query;
  },
  waitOn: function() {
    console.log("waitOn is running (should see this message once for each subscription)");
    return [
      Meteor.subscribe('item_ownership_pub'),
      Meteor.subscribe('my_items')
    ];
  }
});

way cleaner way of writing router

Router.route('/test', {
  waitOn: function() {
    console.log("waitOn is running (should see this message once for each subscription");
    return [
      Meteor.subscribe('item_ownership_pub'),
      Meteor.subscribe('my_items')
    ];
  },
  data: function(){
    var query = this.params.query;
    console.log("query: ", query);
    return query;
  },
  action: function(){
    console.log("this will re-render if url params changed");
    this.render();
  }
})
fuzzybabybunny
  • 5,146
  • 6
  • 32
  • 58
  • Thanks for this - I had asusmed that his was the case. If I have a function that renders the page (using Blaze.renderWithData, etc.), how do I trigger that function to run from Iron Router? – JoeTidee Jun 14 '15 at 09:26
  • I can't have this template being reactive, hence I just want to trigger a fucntion that will render the template ondemand. – JoeTidee Jun 14 '15 at 10:37
  • The router as I have it set up will re-render the template each time the path changes. (ie from `/test?box=1234` to` /test?box=0987`). Whether you choose to use reactive vars is up to you. – fuzzybabybunny Jun 14 '15 at 11:24
  • I've also added a cleaner way of writing the router function since this way you can visually determine the order that Iron Router is doing things (first does the waitOn, then sets the data, and finally does the action of rendering) – fuzzybabybunny Jun 14 '15 at 11:30