Search code examples
meteoriron-router

Iron Router named routes pathFor is null when using arg


The Iron Router / Meteor examples all seem to use _id in the named routes, which works. However, I am missing something in order to use a named route from another key in a collection other than _id. Specifically, I am using the tag key.

'/tags' route

This tag list route works as expected: http://localhost:3000/tags

Router.route('/tags', {
  name: 'demoTagsList',
  waitOn: function() {
    return Meteor.subscribe('tags');
  },
  data: function() {
     return Tags.find();
   }
});

The pathFor for demoTagItem works as expected

<a href="{{pathFor 'demoTagsList'}}">Tags</a>

As does Router.routes['demoTagsList'].url() --> "http://localhost:3000/tags"

'/tags/:_tag' route

This specific tag route also works as expected and returns the appropriate data: http://localhost:3000/tags/foo-tag

Router.route('/tags/:_tag', {
  name: 'demoTagItem',
  waitOn: function() {
    return Meteor.subscribe('itemsFromTag', this.params._tag);
  },
  data: function() { return Posts.find({tags: this.params._tag}); }
});

However, the pathFor for each demoTagItem route results in null. Router.routes['demoTagItem'].url() --> "http://localhost:3000null"

The template code:

<template name="demoTagsList">
  <h2>Demo Tag list</h2>
  <ul>
  {{#each tags}}
      <li class="tag"><a href="{{pathFor 'demoTagItem'}}">#{{name}}</a></li>
  {{/each}}
  </ul>
</template>

What needs to happen to set the router path so that pathFor picks up {{name}} and builds the link as tags/tag-name ?


Solution

  • If you have a look at the pathFor docs you'll see that the function requires a data context to fill in the parameters in the route. You can explicitly set the data with a template helper like so:

    <li class="tag"><a href="{{pathFor 'demoTagItem' data=getTag}}">#{{name}}</a></li>
    

    where getTag is a template helper that returns an object like {_tag: 'puppies'}.

    However, the path of least resistance is often to set the route params to have the same field name as a tag object would natively. Assuming your tags have a name property, you could modify your route like this:

    Router.route('/tags/:name', {
      name: 'demoTagItem',
      waitOn: function() {
        return Meteor.subscribe('itemsFromTag', this.params.name);
      },
      data: function() { return Posts.find({tags: this.params.name}); }
    });
    

    Now your template shouldn't require any modifications from the original code because the context is already a tag. That should be the equivalent of writing data=this where this looks like {name: 'puppies'}.