iron:router will not re-render after route change with same template
Asked Answered
T

2

9

How can I make Iron:router re-render a template?

I have this html:

<head>
</head>

<body>
</body>

<template name="mainLayout">
  <a href="{{pathFor route='list'}}">list</a>
  <a href="{{pathFor route='find' query='q=xxx'}}">find</a>
  {{> yield}}
</template>


<template name="listTemplate">
  <p>list</p>
</template>

and this js:

Router.configure({
    layoutTemplate: 'mainLayout'
});

Router.route('/list', {
    name: 'list',
    template: 'listTemplate'
});

Router.route('/find', {
    name: 'find',
    template: 'listTemplate',
    data: function () {
        return this.params.query;
    }
});


if (Meteor.isClient) {
    Template.listTemplate.rendered = function () {
        if (this.data)
            console.log('find ' + this.data.q);
        else
            console.log('list all');
    };    
}

When I click on the links to switch views (simulated here with console.log), the route does change, but the template is not re-rendered. Is there a way to force iron:router to re-render?

Tetreault answered 17/11, 2014 at 10:41 Comment(7)
You said: "when I click on the links to switch views", but your not actually switching views because both routes are using the exact same template. We need to know exactly what your trying to do here.Tortuosity
I think I am switching views: the Template.rendered function uses the data provided by the router to display a different view, with different data. (I am simulating this in the example code, but the "real" rendered function has a lot of logic to show different views). I therefore also gave the route a different name and different url. It does this by using the same template with different parameters. I can see the url change as I am switching routes, if I use a route onBeforeAction for example, it does get called, but I can't get the template to re-render with the new data and parameters.Tetreault
You're really leaving out to much code for me to be able to help you. I can't think of a good reason to force a re-render on template. Generally speaking, you want to provide your template with reactive data and reactive helpers to control what's displayed. If you update your question and show me actually what you're trying to do, I might be able to provide you with a more concrete answer.Tortuosity
Indeed I have a template with reactive data and helpers (and lots of other templates with data & helpers). Now I want to create a menu system (navbar with dropdowns) that direct the user to these templates. However, for some menu-items it makes sense to use the same template, only with different data and parameters. Think of menu items like: "Show all posts", "Show own posts", "Show recent posts", "Show filtered by ... posts", etc. The problem is that if I am showing one these options and want to show another using the same template, nothing happens because the router will not re-render.Tetreault
So I think I must make some menu logic to determine after a menu selection if the desired template is already onscreen and then adjust the data & parameters. This is just a bit more work and unnecessary logic while it could be very simple if the router would be so kind to just re-render...Tetreault
So there is no point to add more code, I did my best to remove as much code as possible to show the actual problem ;) BTW thanks for you trouble to look into this. I really appreciate it.Tetreault
Maybe related: github.com/iron-meteor/iron-router/issues/1232Territorial
C
2

Setting the router controller state did not work for me. The answer Antônio Augusto Morais gave in this related github issue worked. Using the Session to store the reactive var to trigger the autorun reactiveness. It's a hack, but it works.

## router.coffee
Router.route '/profile/:_id',
  name: 'profile'
  action: ->
    Session.set 'profileId', @params._id
    @render 'profile'

## profile.coffee
Template.profile.onCreated ->
  @user = new ReactiveVar
  template = @

  @autorun ->
    template.subscription = template.subscribe 'profile', Session.get 'profileId'

    if template.subscription.ready()
      template.user.set Meteor.users.findOne _id: Session.get 'profileId'
    else
      console.log 'Profile subscription is not ready'

Template.profile.helpers
  user: -> Template.instance().user.get()

## profile.html
<template name="profile">
  {{#if user}}
    {{#with user.profile}}
      <span class="first-name">{{firstName}}</span>
      <span class="last-name">{{lastName}}</span>
    {{/with}}
  {{else}}
    <span class="warning">User not found.</span>
  {{/if}}
</template>
Columbian answered 26/10, 2016 at 17:2 Comment(1)
This is what I ended up doing because I couldn't seem get router state defined inside of the onRendered function.Dumbstruck
R
1

You can try something like this:

Router.configure({
  layoutTemplate: 'mainLayout'
});

Router.route('/list', {
  name: 'list',
  template: 'listTemplate',
  action: function() {
    this.state.set('query', this.params.query);
  }
});

Router.route('/find', {
  name: 'find',
  template: 'listTemplate',
  data: function() {
    return this.params.query;
  },
  action: function() {
    this.state.set('query', this.params.query);
  }
});


if (Meteor.isClient) {
  Template.listTemplate.rendered = function() {
    this.autorun(
      function() {
        if (this.state.get('query'))
          console.log('find ' + this.data.q);
        else
          console.log('list all');
      }
    );
  };
}

The rendered method isn't reactive, that's why you need an autorun. The template "this.data" isn't reactive so you're gonna need a reactive var to do that, either a Session variable, a controller state, or some kind of reactive var.

You may need to add the reactive-var package depending on what approach you take.

Rebatement answered 11/5, 2015 at 0:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.