Note: this article was updated on April 30, 2015 to use Template.subscribe() and Template.subscriptionsReady.

It used to be so easy in the good old days: the server would sample freely from a database, format the resulting data, and push it to the client who would then obediently display the content it received.

Of course, this all changed with Meteor. Now, both server and client had access to pure, unadulterated data, and we were now in serious risk of getting hooked on autopublish and overdosing. A solution was needed.

Publications & Subscriptions

That solution, of course, was publications and subscriptions. If you need to be brought up to speed, our primer on the topic is a great place to start.

While Meteor gave us powerful tools, it didn’t really tell us how to use them. But after trying out a few different ideas, many Meteor developers came to the conclusion that the best way to deal with subscriptions was through Iron Router.

Now while there’s nothing wrong with that pattern (in fact, it’s the one we cover ourselves in Discover Meteor), today we want to talk about a different scenario: template-level subscriptions.

A Modern Web App

Let’s consider a modern web app such as Gmail. For better or for worse, Gmail has gotten pretty complex over the years. It’s not just a list of emails anymore, but has a segmented inbox, a list of labels, a chat widget…

A typical Gmail inbox.

If Gmail was built with Meteor, each of these modules might very well require its own subscription.

Yet for something this modular, Iron Router is a bit too blunt: we might not want to subscribe to all our modules’ subscriptions all at once, or maybe we don’t want to wait until they’ve all loaded before we start showing the UI.

Whatever the reason, our goal will be for each module to be self-contained, and subscribe to the proper publication on its own.

What We’re Building

Let’s build a simple module that displays a list of posts, in no particular order.

Obviously we want our modules to display data, but we’ll also make them a little fancier. Each module will also be paginated using a “load more” button, and display a loading spinner while it’s waiting for data.

Our posts module.

Follow Along With MeteorPad

We’ll use MeteorPad for this tutorial. This will let you run, fork, and modify the code right from your browser:

Launch MeteorPad

Of course, we encourage you to launch a new pad and follow along from scratch!

File Structure

MeteorPad apps follow a very simple file structure:

/main.html          // your templates markup
/common.js          // executed on both client and server
/client/app.js      // executed on client only
/server/app.js      // executed on server only
/style.css          // your stylesheet

And just to make things a tiny bit nicer, here’s a simple stylesheet you can copy/paste:

.posts{
  margin: 20px auto;
  width: 60%;
  font-family: 'Source Sans', 'Helvetica', sans-serif;
}
.post{
  padding-bottom: 10px;
  border-bottom: 1px solid #ddd;
  margin-bottom: 10px;
}
h3, p{
  margin:0px;
  font-size:13px;
}
.loading{
  position: relative;
  height: 100px;
  width: 100%;
}
.load-more{
  text-align:center;
  background:#eee;
  border: 1px solid #ddd;
  display:block;
  padding: 10px;
}

The Collection

First, let’s get the basics out of the way and set up our collection in common.js:

Posts = new Mongo.Collection("posts");

The Publication

We’ll also generate some dummy content (using the anti:fake package) and set up a simple publication in /server/app.js:

// generate dummy content
Meteor.startup(function () {
  if (Posts.find().count() === 0) {
    for (i = 0; i <= 50; i++) {
      Posts.insert({
        title: Fake.sentence(6),
        body: Fake.paragraph(3)
      });
    }
  }
});

// publish posts
Meteor.publish('posts', function(limit) {
  Meteor._sleepForMs(2000);
  return Posts.find({}, {limit: limit});
});

Note the Meteor._sleepForMs(2000); line. This is a simple debugging hack that tells Meteor to wait a little before returning our posts. This will come in handy later!

The Markup

We’ll adopt an “outside-in” approach, where we’ll code our template as if it worked first, and then worry about actually make it do something later on. So here’s what our posts module’s markup will look like:

<head>
  <title>Template-Level Subscriptions</title>
</head>

<body>
  <div class="posts">
    {{> posts}}
  </div>
</body>

<template name="posts">
  {{#each posts}}
    <div class="post">
      <h3>{{title}}</h3>
      <p>{{body}}</p>
    </div>
  {{/each}}
  {{#if Template.subscriptionsReady}}
    {{#if hasMorePosts}}
      <a class="load-more" href="#">Load More</a>
    {{/if}}
  {{else}}
    <div class="loading">{{>spinner}}</div>
  {{/if}}
</template>

First, we’re looping over each post. Then, if our data is ready we’ll check if there are still posts left to be loaded, and if so we’ll display a “load more” button.

If our data is not ready, we’ll display a spinner (provided by the sacha:spin package). This is where the _sleepForMs() function comes in handy: thanks to the delay, we’ll be able to clearly see if our spinner is showing up or not.

The Event Handler

Now for the interesting part, the code that actually hooks everything up. From now on, everything will happen inside /client/app.js.

This code will be split into three parts: the onCreated callback, the helpers, and the event handler. Let’s get the easy part out of the way, and deal with the event handler first:

Template.posts.events({
  'click .load-more': function (event, instance) {
    event.preventDefault();

    // get current value for limit, i.e. how many posts are currently displayed
    var limit = instance.limit.get();

    // increase limit by 5 and update it
    limit += 5;
    instance.limit.set(limit);
  }
});

OK, I lied. This isn’t that easy after all. Right off the bat, we’re encountering a few special concepts.

First, instance. In the onCreated, rendered, and destroyed callbacks, the current template instance is available as the value of this.

But in other helpers and events handlers, this actually points to the data context. So when this won’t do the trick, you can access that instance with either Template.instance(), or, for event handlers like this one, with the second argument of the handler function.

So it follows that instance.limit will point to a limit variable assigned to the template instance. But what about get()?

Introducing Reactive Variables

You’re probably familiar with Session variables, which you can set with Session.set('foo', 'bar') and retrieve with Session.get('foo').

Reactive variables (provided by the reactive-var package) are a neat way to roll out your own “session variables” while controlling their scope to (for example) limit it to the current template instance.

In other words, they’re a convenient way to make a plain old JavaScript variable or object reactive, without having to pollute the session’s global namespace. If you want to learn more about them, David Burles wrote a great introduction on the topic.

And just like session variables, you get and set them with get() and set(). So our code is in effect:

  • Getting the current value of the template instance’s limit reactive variable.
  • Increasing that value by 5.
  • Setting limit to this new value.

If you need to brush up on reactivity in general, make sure you also check out our introduction to reactivity basics before reading on.

The Helpers

Let’s keep going backwards from the event handler. The next layer of functionality is made of our template helpers, which we are calling from the template itself. Namely, posts and hasMorePosts.

Template.posts.helpers({
  // the posts cursor
  posts: function () {
    return Template.instance().posts();
  },
  // are there more posts to show?
  hasMorePosts: function () {
    return Template.instance().posts().count() >= Template.instance().limit.get();
  }
});

Template.subscriptionsReady

It used to be you had to define your own isReady helper. But Meteor now provides a Template.subscriptionsReady that automatically tracks the ready state of your template-level subscriptions, so we’ll just use that.

Let’s take a look at our two helpers:

posts

Instead of using Posts.find() directly, we’re calling a function named posts() set on the template instance.

Cursors are naturally reactive so posts() doesn’t need to be a reactive variable (note the lack of get()), but it does need to be a function for the reactivity to kick in.

hasMorePosts

Here, we’re comparing how many posts we’re currently displaying (using count() on that posts() function from before) with the limit template instance reactive variable (let’s call them TIRVs for short).

That limit variable represents how many post we asked for. So if we ask for 20 posts but only receive 17 to actually display, we can reasonably conclude that we’ve reached the end of the Posts collection, and can stop displaying that “load more” button.

Note that this won’t work great when we ask for 20 posts and it so happens there are exactly 20 posts in the database. But it would be quite complicated to work around this little quirk, so we’ll just leave it be for today.

Initializing The Reactive Variables

It’s now time for the last (and most complex) piece of the puzzle: the onCreated callback.

That callback will do three things:

  1. Initialize the reactive variables we’re using.
  2. Use an autorun block to reactively re-subscribe whenever limit changes.
  3. Update the cursor we’re using to display our list of posts.

Let’s get started. First, initializing the variables:

Template.posts.onCreated(function () {

  // 1. Initialization

  var instance = this;

  // initialize the reactive variables
  instance.loaded = new ReactiveVar(0);
  instance.limit = new ReactiveVar(5);

  // ...
});

Remember that since we’re within a onCreated callback, we can access the template instance at this. The value of this will change when we’re inside the autorun block though, so let’s store it in instance to make sure it’s always available. We then initialize our two reactive variables.

Pagination Problems

Wait, two?? Where did this loaded come from?!

We’ve seen before that we’re using limit to keep track of how many posts should be displayed, but that’s not entirely true. limit actually tells us how many posts the client should ask for. But it should not try to display them until it knows that the posts are loaded.

To understand why, imagine that you have two slightly different posts modules, one showing the five highest rated posts, and one showing the five newest posts. This means you’re storing 10 posts on the client in total, and not just 5.

Now what happens when someone tries to load 5 more of the “highest rated” posts? If we’re naively increasing the number of posts displayed as soon as the user clicks, we’ll end up displaying the 5 newest posts in our highest rated widget.

For that reason, we want to make sure we wait until we’ve loaded these five additional “highest rated” posts before we actually try to display them. Thus the need for two separate variables.

The Autorun Block

autorun() is one of Meteor’s core utilities, and it lets you define special reactive zones which will re-run anytime something inside them changes (provided that “something” is itself reactive).

In this case, we’re setting it up directly on the template instance using instance.autorun instead of the more traditional Tracker.autorun.

This is a nice way to ensure that the autorun will be stopped when the template is destroyed and we no longer need it:

Template.posts.onCreated(function () {

  // 1. Initialization

  // ...

  // 2. Autorun

  // will re-run when the "limit" reactive variables changes
  instance.autorun(function () {

    // get the limit
    var limit = instance.limit.get();

    console.log("Asking for "+limit+" posts…")

    // subscribe to the posts publication
    var subscription = instance.subscribe('posts', limit);

    // if subscription is ready, set limit to newLimit
    if (subscription.ready()) {
      console.log("> Received "+limit+" posts. \n\n")
      instance.loaded.set(limit);
    } else {
      console.log("> Subscription is not ready yet. \n\n");
    }
  });
});

First, we’re retrieving the limit TIRV. Since A) we’re inside an autorun and B) limit is reactive, we can conclude that the whole block will rerun anytime limit changes.

We’re then subscribing to the posts publication using the limit we just obtained. Note that we’re not calling the usual Meteor.subscribe(), but instead instance.subscribe(), where instance refers to the current template.

This is a special version of Meteor.subscribe() that is automatically stopped when the template is destroyed, and also enables the Template.subscriptionsReady helper.

Finally, we’re finding out whether or not that subscription is done loading by checking the value of subscription.ready() (note that if we had more that one subscription, we could instead check instance.subscriptionsReady()).

If it has, we’re setting loaded to the value of limit (in essence saying “go ahead, you can now safely display more posts”).

The Cursor

We now come to the last part of our onCreated callback, the posts function which will return our cursor:

Template.posts.onCreated(function () {

  // 1. Initialization

  // ...

  // 2. Autorun

  // ...

  // 3. Cursor

  instance.posts = function() { 
    return Posts.find({}, {limit: instance.loaded.get()});
  }

});

Now at first glance it doesn’t seem like this would be reactive, since it lives outside the autorun, after all.

But the posts() function is actually a reactive data source, since it’s a function that calls another reactive data source (loaded.get()).

So when we call it from a template helper (which is itself a reactive context), it will re-run whenever its data sources reactively change.

The Whole Thing

Here is the whole onCreated callback once again:

Template.posts.onCreated(function () {

  // 1. Initialization

  var instance = this;

  // initialize the reactive variables
  instance.loaded = new ReactiveVar(0);
  instance.limit = new ReactiveVar(5);

  // 2. Autorun

  // will re-run when the "limit" reactive variables changes
  instance.autorun(function () {

    // get the limit
    var limit = instance.limit.get();

    console.log("Asking for "+limit+" posts…")

    // subscribe to the posts publication
    var subscription = instance.subscribe('posts', limit);

    // if subscription is ready, set limit to newLimit
    if (subscription.ready()) {
      console.log("> Received "+limit+" posts. \n\n")
      instance.loaded.set(limit);
    } else {
      console.log("> Subscription is not ready yet. \n\n");
    }
  });

  // 3. Cursor

  instance.posts = function() { 
    return Posts.find({}, {limit: instance.loaded.get()});
  }

});

Practical Uses

There are a couple reasons why you might want to adopt this pattern.

First of all, you might not even have routes in your app but you still need a clean way to manage subscriptions. Or maybe you do have routes, but you don’t want to wait on subscriptions there.

Telescope presents another interesting use case. Since Telescope is an open-source app, one of the project’s goals is to make it easy to extend the app through additional packages, without having to modify the core codebase.

This does present a problem when it comes to managing subscriptions with Iron Router though: how do you add new data to an existing page without modifying its route?

The template-level subscription pattern provides an answer by letting users easily add their own self-contained modules, and you can see it in action on Telescope’s user profile screen:

Telescope user profile screen. Each module has its own independant subscription.

Conclusion

One of the best (and worst!) things about Meteor is that there is often no “right” way to do things. So the best you can hope for is to familiarize yourself with an array of different techniques, and pick the right one at the right time.

So hopefully this article will help add one more pattern to your arsenal!

Kudos to Daniel Phillips from Meteor strategy game Dominus for his excellent blog post about Meteor tips which inspired me to play around with reactive variables.