Robert Dickert

Personal blog

Iron Router: First Steps

| Comments

If you want to make a single-page app with Meteor, you will want to use Iron Router. Iron Router is a powerful package to do client- and server-side routing for your app developed by Chris Mather of EventedMind and Tom Coleman of Percolate Studio.

This is the first of two posts on Iron Router. This guide will give you enough knowledge to use the most important features of Iron Router. The second post will show you how to set up navigation using Twitter Bootstrap’s navbar.

We will assume you have installed Meteor and Meteorite and that you understand the basics of Meteor. The code demonstrated here was written with Meteor 0.8.0 and Iron Router 0.7.0. You will need to create a project with meteor create, and you will need to add Iron Router with mrt add iron-router. Once you’ve done that, you’re ready to go. You can get a working copy of this code here.

What is routing?

Routing is the process where your app uses the path in the browser address bar to choose what information to display. For example, www.yoursite.com/articles might pull up a list of articles, and www.yoursite.com/article/145 might pull up a full article with an ID of 145. The paths of the preceding URLs are /articles and /article/145, respectively. The router must parse these paths, pull up the right templates, and provide the right data. By making your app respond to these paths, you can allow users to access (and bookmark) specific locations and data in your site, and they will be able to go back to previous locations with the browser’s back button.

How do I set up routing?

Iron Router (IR) provides quite a few capabilities, but you can get started with a small subset of its API. It’s easiest to show some simple code. The following is all you need to route a simple app. In your javascript:

Basic Routing: All the javascript you need
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Router.map(function () {
  this.route('about');  // By default, path = '/about', template = 'about'
  this.route('home', {
    path: '/',  //overrides the default '/home'
  });
  this.route('articles', {
    data: function () {return Articles.find()}  //set template data context
  });
  this.route('article', {
    path: '/article/:_id',
    data: function () {return Articles.findOne({_id: this.params._id})},
    template: 'fullArticle'
  });
});

We define four routes inside Router.map(). Each route is defined with a call to this.route(), passing the route name and (optionally) an options object. You must supply the name. IR will provide a default path and template based on the name unless you tell it otherwise. Let’s look at this one route at a time.

The first route has a name of about, and it’s first here because it’s the simplest. Iron Router will automatically assume based on the route name that the path for this is /about (in other words, you will access this route via localhost:3000/about or www.yoursite.com/about). It will also assume you have created a template named about for it to display.

The next route, home, is the first to pass options to this.route(). This is our home page, and it should come up when we first pull up the site–that is to say, at localhost:3000 or www.yoursite.com/. So although home is a good name for the route, the path needs to be / instead of the default /home. Meteor will again assume we are using a template called home.

The third route, articles, needs to display a list of articles we can view. That means it needs to be populated with data from Mongo. We could accomplish this by setting a template helper on Template.articles, but Iron Router gives us the option to do it more flexibly by setting the data property in the route options. We pass a function so that it will update reactively if we add/remove/change articles.

Finally, the article route needs to present a single article in full. To do this, it needs to know which article to fetch. We will use a special feature in the path property: Starting a path element with /: instead of / will cause Iron Router to store the text in that path element for use in this.params. In this case, /article/:_id means that a path of /article/123 would match this route and save '123' into _id. You can see that we use this.params._id to query Mongo and retreive the article we need. Although not needed here, note that the path will also accept globs (e.g., /files/*) and regular expressions. We’ll also set the template property to 'fullArticle, which will override Iron Router’s default of article.

Template HTML and Helpers for Routing

The HTML to support these routes will complete the picture:

Templates to support the routes we defined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template name="home">
  <h2>Home</h2>
  <p>You are on the home screen.</p>
</template>

<template name = 'about'>
  <h2>About this site</h2>
</template>

<template name = 'articles'>
  <h2>Articles</h2>
    <ul>
       {{#each this}} 
        <li>
          <a href="{{ pathFor 'article' }}"> {{title}} </a>
        </li>
      {{/each}}
    </ul>
</template>

<template name = 'fullArticle'>
  <h2>{{ title }}</h2>
  <p>{{ body }}</p>
</template>

This code should make sense from looking at the route definitions. There are a couple of things worth noting, however:

We didn’t set up a document <body> anywhere. Iron Router will automatically attach your routes to the body unless you specify otherwise.

In the articles template, we called #each with this. The route’s data function returns Articles.find() as the data context, which we access with this. We could have returned a more complex object with 1

1
2
3
this.route('articles', {
  data: function () {return {articles: Articles.find()};}  // return object
});

Then we could use {{ #each articles }}, more like a template helper. We could also add other data (including other queries or other reactive data), as we will see in the next post. There is no limit to the data context we can set.

We also made use of the pathFor template helper, which is provided by Iron Router, to set up the href attribute of our anchor tag. When we use {{ pathFor 'article' }}, we are telling Iron Router to return a path string for the route with a name of article. Note that we didn’t supply the _id required by the route. Based on our route definition, Iron Router knows that an _id property is expected, and it will use the _id from the current context–a nice feature. For the other routes, hard-coding (e.g., href="/about") in the HTML would also work, but the pathFor helper future-proofs your code; if you change the route name in the future, it will continue to work without refactoring.

Try it out

If you want to give this code a spin, the following javascript will set up a collection with a few dummy records for you.

Set up some dummy data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Articles = new Meteor.Collection('articles');

if (Meteor.isServer) {
  Meteor.startup(function () {
    if (! Articles.findOne()){
      var articles = [
        {title: 'Article 1', body: 'This is article 1'},
        {title: 'Article 2', body: 'This is article 2'},
        {title: 'Article 3', body: 'This is article 3'}
      ];
      articles.forEach(function (article) {
        Articles.insert(article);
      })
    }
  });
}

If you are developing locally, you can see the main pages at:

Get the code

You can clone the full code for this and the next post at the github repo for this article

Conclusion

With these basics, you can build a fairly complex single-page app. There is a lot more Iron Router can do, however. Continue to the next post to see how to make use of the structure we have generated to add navigation and highlight our position in that structure.


  1. Code corrected June 2, 2014.

Comments