Robert Dickert

Personal blog

Set Up Navigation With Iron Router and Bootstrap

| Comments

This post is a continuation of Iron Router: First Steps. You can download the code here.

Meteor’s reactivity is one of its most powerful features but it requires a change in tactics if you are coming from a more traditional framework. Iron Router (IR) was built from the ground up to take advantage of Meteor’s strengths. We will explore some simple patterns to provide navigation and context cues to your user with very little code.

So far, we have learned how to make Meteor respond to routes to render pages. Let’s put that together with Twitter Bootstrap to add some simple navigation. First, add Bootstrap 3:

mrt add bootstrap-3

Once you restart Meteor, you should see that Bootstrap has changed your fonts, and you are ready to begin.

Add a Layout – Router.configure()

We will add Bootstrap’s navbar component for top-level navigation. Since it will live at the top of the page, we would like to have a header region that isn’t re-rendered every time we navigate. Instead of letting Iron Router render our templates directly to document.body, we will give it a layoutTemplate to wrap around our templates. In our javascript:

Specify a layout template
1
2
3
 Router.configure({
   layoutTemplate: 'layout'  //can be any template name
 });

Many of the configuration options that can be set on routes can be set as defaults in Router.configure(), including special-case templates like notFoundTemplate, loadingTemplate, and yieldTemplates. Here, we tell IR to use the template named layout for all routes (this can be overridden in a this.route() specification).

OK, now we need to create our layout:

HTML Layout
1
2
3
4
5
6
7
8
9
10
11
12
<template name="layout">
  <div class="container">
    <header>
      {{> nav }}
    </header>
    {{> yield}}
  </div>
</template>

<template name='nav'>
  Nav goes here...
</template>

All we have done is to set up a template with the name ‘layout’ (as specified in ‘layoutTemplate’–but we could use any name). We added some structure, including a <div> of class “container” so that Bootstrap will center our content. Then we have a <header> element that includes a template called nav with some dummy text, and an IR component, {{> yield}}. The yield component tells IR where to render its output. If you run your app with what we have now, it will again render each page, but the “Nav goes here…” text will remain at the top no matter which route you navigate to. It is possible to have IR render to more than one yield in a template via named yields, but we don’t need that here.

Set Up Bootstrap’s Navbar

Now let’s replace the above nav template to add the bootstrap navbar (modified from bootstrap’s example code)

Bootstrap navbar with inclusions for our customization
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template name='nav'>
  <nav class="navbar navbar-default" role="navigation">
    <div class="container-fluid">
      <!-- Brand and toggle get grouped for better mobile display -->
      <div class="navbar-header">
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        {{> navBrand}}
      </div>
      <!-- Collect the nav links, forms, and other content for toggling -->
      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
        <ul class="nav navbar-nav">
          {{> navItems}}
        </ul>
      </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
  </nav>
</template>

This mostly adds bootstrap boilerplate to build the nav. By separating {{> navBrand}} and {{> navItems}}, we can isolate our app-specific markup, making our code more readable and reusable:

Navbar customization
1
2
3
4
5
6
7
8
9
10
11
12
<template name="navBrand">
  <a class="navbar-brand" href="{{ pathFor 'home' }}">IR Demo</a>
</template>

<template name='navItems'>
  <li class="{{ activeIfTemplateIs 'articles' }}">
    <a href="{{ pathFor 'articles'}}">Articles</a>
  </li>
  <li class="{{ activeIfTemplateIs 'about' }}">
    <a href="{{pathFor 'about'}}">About</a>
  </li>
</template>

The links use the {{ pathFor }} helper we discussed in the previous article. If you try the app now, you should get a working navbar that covers three of our templates: home, articles, and about (we’ll get to fullArticle in a moment).

Reactively Set the Navbar’s “active” Class – Router.current()

Bootstrap allows us to show where we are by highlighting a navigation item if its class is set to “active”. You can see in the code above that we have a helper {{ activeIfTemplateIs 'templateName' }} ready and waiting, although it doesn’t do anything yet. We can check which template we are on by looking at the current route controller. IR makes it available via Router.current().

helpers.js
1
2
3
4
5
6
7
8
9
if (Meteor.isClient) {
  Template.navItems.helpers({
    activeIfTemplateIs: function (template) {
      var currentRoute = Router.current();
      return currentRoute &&
        template === currentRoute.lookupTemplate() ? 'active' : '';
    }
  });
}

The helper receives the template variable from our html (e.g., {{ activeIfTemplateIs 'about' }}). We can then get the currently active route controller by by calling Router.current(). This object has a lookupTemplate() method to tell us what template is currently in use (this method name may change in the future, stay tuned). Then we return ‘active’ for the class name if it’s a match, which will trigger the navbar’s highlighting. Router.current() may return null on startup, so we have to guard our lookup with currentRoute &&.

We could also have tested the path '/about' against currentRoute.path. (the reason for choosing template instead of path will be clear below). Router.current() is a reactive data source, so you can use it in template helpers and Deps.autorun() blocks to make reactive updates.

Set up Article Navigation with a Sidebar

If you have updated the code to this point, you will now have a working navbar that shows via highlight if we are currently on the “Articles” or “About” pages (the brand item, which links to home, does not highlight). What about our fullArticle template? Let’s follow a common use case and make the list a sidebar so that when we click on one of the article links on the Articles page, it will display next to the list of articles rather than replacing it. We will need to replace the articles template with the following html:

Replacement for template ‘articles’
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template name = 'articles'>
  <div class='row'>
    <div class="col-sm-4">
      <h2>Articles</h2>
      <ul>
        {{#each articleList}}
          <li>
          <a href="{{ pathFor 'article' }}"> {{title}} </a>
          </li>
        {{/each}}
      </ul>
    </div>
    <div class="col-sm-8">
      {{> fullArticle selectedArticle}}
    </div>
  </div>
</template>

We have added a few bootstrap divs with classes of row and col-sm-4 and so on to format our grid (you can read Bootstrap’s docs if you don’t know how these work). The list of articles will be in a sidebar (note that we now pass articleList instead of this), and the main content area will be populated via {{> fullArticle article}}. The use of articleList and article means we expect these variables to be present in our data context. Let’s redo our routing to accomplish this:

Replacement for previous Router.map()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Router.map(function () {
  this.route('home', {
    path: '/',
  });
  this.route('about');
  this.route('articles', {
    // articles now under `articleList` instead of `this`
    data: {
      articleList: function () {return Articles.find()},
      selectedArticle: {}
    }
  });
  this.route('article', {
    path: '/article/:_id',
    // provide data for both `articleList` and `selectedArticle`
    data: function () {
      return {
        articleList: Articles.find(),
        selectedArticle: Articles.findOne({_id: this.params._id})
      }
    },
    template: 'articles'  //change template target
  });
});

The data function of both the /articles and /article/:_id paths now send both articleList and selectedArticle (which may be empty) for use in rendering. We also set the template for the full article’s route to 'articles' instead of 'fullArticle'.

Now, clicking on an article link will pull up the full article to the right. And since we keyed our {{ activeIfTemplateIs }} helper off of the template rather than the path, the navbar still highlights correctly even though the path changes.

Highlight the Selected Article in the Sidebar

It would also be nice to be able to highlight the selected article in the list on the left. You may have seen this done in the past using Meteor’s Session object, but IR’s reactive data sources make that unnecessary. First, add a selected class for the highlight in your css:

A selected class
1
2
3
.selected {
  background-color: #e7e7e7;
}

Add the class helper in the articles template:

Replacement for the #each block
1
2
3
4
5
{{#each articleList}}
  <li class="{{ maybeSelected }}"> <!-- Add "selected" class (if selected) -->
  <a href="{{ pathFor 'article' }}"> {{title}} </a>
  </li>
{{/each}}

And write your helper in js:

Selected helper
1
2
3
4
5
6
7
8
//make sure you are in the `if (Meteor.isClient)` block
Template.articles.helpers({
  maybeSelected: function () {
    var currentRoute = Router.current();
    return currentRoute &&
      this._id === currentRoute.params._id ? 'selected' : '';
  }
});

How does this work? maybeSelected is called inside an #each helper in the template, which sets the data context to the current record, giving you access that record’s properties, including _id. Meteor sets that context as this in your template helper. We can then compare it with the current route’s id by using Router.current().params._id. The params property gives you access to any named parameters by name, or globs and regexes by index number. This is the :_id portion of the parameter we originally set up in the article route (path: '/article/:_id').

Conclusion

We were able to accomplish a lot with a small amount of code. Notice that we were able to reactively track and refer to our route, template, and selected article without the use of Session.

We touched on the following parts of Iron Router. In javascript string:

Router.configure({
  layoutTemplate: 'template name'
});

Router.map(function () {
  this.route('route name', {
    path: 'path matching string',
    data: function () {return /*cursor or object*/},
    template: 'template name'
  });

Router.current().path
Router.current().template
Router.current().params

And in the templates we covered the following components:

  • {{ pathFor 'route name' }}
  • {{> yield }}

You now know enough to build a single page app quickly and easily. There are more features in Iron Router that we didn’t cover. These include:

  • Router.go() (change route via javascript)
  • The setData() method to change the data context
  • Advanced path parsing, including multiple variables, hash, query, globbing, and regular expressions
  • Server-side routing
  • Hooks (add code to run before, during, and after routing)
  • The action hook and custom rendering
  • Multiple yield regions
  • Route controllers (an object-oriented way to share route configuration between multiple routes)

Look these up when you are ready. Until then, start building your app!

Get the code

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

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.

Why Is My Meteor App Not Updating Reactively?

| Comments

Understanding Meteor’s Deps Package

Have you ever tried to add something to a Meteor template and been frustrated that it didn’t update the way you expected (or at all)? Meteor is often so “magical” that we rely on it without understanding its fundamentals. When we advance beyond simple use cases, we may wonder why the magic stopped, and it can be difficult to know where to begin looking.

Meteor is designed to allow beginners to be productive quickly while making it easy for more advanced users to access more powerful and deeper features. So let’s pay off some of that technical debt by unpacking how Meteor client-side reactivity works in detail. As we do this, we will touch on some common misconceptions.

How Meteor Reactivity Works

What is reactivity?

We will define reactivity here as the ability of Meteor (or any system) to automatically update the values of variables and objects when the information they depend on changes.

The most widely-known example or a reactive system is a spreadsheet. Let’s say you have a spreadsheet with a cell A1 that contains a number and another cell B1 that runs the formula =A1+1. If A1 is set to 1, we expect B1 to change to 2. If we then change A1 to 4, how will cell B1 show the correct value of 5? Clearly, B1 needs to run its formula again. On the other hand, running when, say, C5 changes would be ineffective and wasteful. So, we need (1) a way to run B1’s formula over and over again, and (2) a way to tell B1 when to rerun. Meteor has a very clever way of achieving this with the minimum amount of reruns. This is most visible when Meteor updates templates in real-time, but you can run any code reactively.

Which part of Meteor are we talking about?

Meteor has “full stack reactivity” as one of its Seven Principles, but it’s not a single monolithic system. Meteor is built out of independent packages that work so nicely together that they may be hard to tease apart, but reactivity is accomplished with a number of technologies, including events, callbacks (sometimes abstracted as node fibers on the server), publish/subscribe, and the Deps package for client-side reactivity. The Deps package is the focus of this article.

Meteor client-side reactivity is explicit

What does this mean? Nothing in Meteor is reactive unless it is specifically wired up to make it so. It would be easy to picture reactivity as a single watcher keeping track of all data structures and checking dependencies whenever a change is detected. This is not how Meteor works (although it may be closer to how Angular’s $watch works, by point of comparison).

Instead, Meteor sets up triggers to each data source as it is accessed. When a reactive data source changes, it tells the code that depends on it to rerun. Think mousetraps or dominoes, not a motion detector. This is known as the observer pattern.

Reactive contexts and reactive data sources

When we talk of a reactive context, we are referring to the function that needs to be rerun. Meteor uses an object called a computation to store and run this function, but we will use the terms computation and reactive context somewhat interchangeably. “Running in a reactive context” refers to a function running inside a Meteor computation. If you access a reactive data source in a reactive context, the code will rerun in response to changes to that data source.

A reactive data source is any provider of data that follows Meteor’s contract for providing reactivity. The data source is responsible for making reactivity work. You can add this capability to any object, but if you don’t, reactive updates will not occur, regardless of whether you are in a reactive context.

The data source is in charge

Here’s the clever bit: Meteor puts the data source in charge of watching these data dependencies.

In a reactive context, when you make a call to a reactive data source to read/get data, Meteor sets up callbacks to rerun the computation when you or someone else writes/sets a change to that data. It is the data source that stores and runs these callbacks. No callbacks, no reactivity.

If a reactive data source’s get method is called outside of a reactive context, it returns a result but does not store a callback. Without a computation to register, there is no way for it to know what code to rerun, as the only time we know both sides of the data source-consumer relationship is when we get from the data source. A benefit to this behavior is that you can use reactive data sources normally in non-reactive code with no side-effects.

Meteor’s reactivity contract

Reactivity in Meteor requires cooperation between the code to be rerun and the data sources that will tell it when to do so. The computation and the data source have specific roles that can be described as a contract between them. This is managed by the Deps package, which provides a Deps global object to give you the API you need. Many Meteor components follow this contract out of the box – the source of the “magic” – but it is not difficult to add your own.

To fulfill its side of the contract, the function to be rerun is placed in a computation. Some things such as templates and their helpers are automatically run in a computation, but you can put any function fn in a computation by calling Deps.autorun(fn). The computation stores the function and provides an invalidate() method. When you call the invalidate() method of a computation, it will rerun its function. If the name ‘invalidate’ sounds confusing, consider as an analogy a cache on a web server. You might cache copies of your home page to serve it faster. When your page changes at the source, the cache is said to be invalid because it no longer reflects the master and must be repopulated. So when any of a computation’s data sources change, the computation is considered “invalid,” and it must be rerun. Each computation also has a stop() method that will cause it to cease being reactive. The stop() method is important for cleaning up computations that are no longer needed.

The other side of the contract is fulfilled by the data source. It must keep a list of all the computations to rerun if it changes. These are managed with a Deps.Dependency object, which contains and manages the list of computations. It has a depends() method which adds a computation to its list. It also has a changed() method that will rerun all the functions in its list by calling their invalidate() method.

Make Any Data Source Reactive

Many developers will find they are happy with the reactive data sources already at their disposal, so you may not need to create reactive data sources. That said, learning the source’s side of the reactivity contract is easy and worthwhile. Let’s look at some code.

A reactive data source is responsible for keeping track of which computations called it. These are its dependencies (and the source of the Deps package name). Meteor will manage the list of dependent computations for you, but you must choose where to store it, when to add a dependency, and when to trigger invalidation.

Get started

Say you have a gaming smart package that can put a user into a bonusMode that can be set to ‘none’, ‘double points’, ‘speed’, or other modes. Here is a basic (non-reactive) object to accomplish this with basic get and set operations, along with a couple of autorun statements and a template helper to display results. If you run meteor create depsdemo from the command line, you can just replace the code in depsdemo.js with the following to try it out for yourself.

depsdemo.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
if (Meteor.isClient) {

  bonusMode = {
    mode: 'normal',
    get: function () {
      return this.mode;
    },
    set: function (newValue){
      this.mode = newValue;
      return this.mode;
    }
  };

  Deps.autorun(function (){
    console.log('bonusMode is now:', bonusMode.get());
  });

  handle = Deps.autorun(function (){
    console.log(bonusMode.get(), 'is your mode. (play with my handle)');
  });

  Template.hello.greeting = function () {
    console.log('template helper ran.');
    return "You are in " + bonusMode.get() + " mode.";
  };
}

Note that we are doing this only if (Meteor.isClient), as Deps doesn’t work on the server. When you refresh the page, you will see the log messages in the console and the the greeting rendered in the template. You can run bonusMode.get and bonusMode.set in the console, but the autorun code never reruns, and the template doesn’t update.

Add reactivity

Now let’s make bonusMode reactive:

Adding reactivity to bonusMode: 3 changes
1
2
3
4
5
6
7
8
9
10
11
12
13
  bonusMode = {
    mode: 'none',
    dep: new Deps.Dependency,   //save dependent computations here
    get: function () {
      this.dep.depend();  //saves the Deps.currentComputation
      return this.mode;
    },
    set: function (newValue){
      this.mode = newValue;
      this.dep.changed();  //invalidates all dependent computations
      return this.mode;
    }
  };

We had to add three lines to this code. We created a Deps.Dependency instance and stored it in bonusMode.dep (you don’t have to store dependencies on bonusMode, but it’s a logical choice). The bonusMode.dep object will manage the list of computations for us. When code in a computation calls bonusMode.get(), we need to store its computation. We do this by calling this.dep.depend(). Finally, when we call bonusMode.set() with a new value, we need to tell all the dependent computations that the value has changed and that they need to rerun. We do this by calling this.dep.changed(). That’s really all there is to it. At this point, templates and other computations will update any time bonusMode changes. Go ahead and try it out.

Digging deeper into reactivity

Once you have the above code working, you can tune how it triggers. For example, what happens if you type bonusMode.set('speed') into the console two times in a row? The log messages trigger each time – even though we “updated” to the same value the second time. We are rerunning too often. This is easily fixed by changing the set function:

1
2
3
4
5
6
7
set: function (newValue){
  if (newValue !== this.mode) {
    this.mode = newValue;
    this.dep.changed();  //invalidates all dependent computations
  }
  return this.mode;
}

What about the variable handle we set on the second autorun? Deps.autorun returns a handle to the computation itself. In the console, we can manually tell the computation to rerun:

handle.invalidate()

If you are wondering where the computations are stored, you can see it in the console. Type:

bonusMode.dep 

You will see a Deps.Dependency object which contains a _dependentsById object. Expand that object, and you will find three computations, one for each autorun, and one for the template. If you expand the handle object, you will see it is one of the computations in the the _dependentsById object (you can match handle._id to the key in bonusMode.dep._dependentsById).

We can tell all dependent computations to rerun just as the code does. Typing:

bonusMode.dep.changed()

will trigger all of them despite the fact that nothing has changed – you are essentially saying, “I changed something, so rerun your dependents.” You can see from the log statements that the template reran as well, but Meteor is smart enough to not update the template in the DOM because there are no changes. This is handled by the Spark (Meteor 6.6.x and earlier) or UI (newer versions) package.

We can also disable a computation via its handle:

handle.stop()

When you do this, Meteor will no longer rerun the function, and its dependencies will be cleaned up. Running bonusMode.set('double points') at this point will still run the first computation and update the template, but the stopped computation will no longer trigger. handle.invalidate() will also do nothing. You don’t always have to track your computations’ handles, but you will create memory leaks if you generate autoruns dynamically and don’t stop them. Meteor handles stops automatically in the computations it manages.

Troubleshooting Reactivity

To recap: Your code will run in a computation if either of these is true:

  • You are in a Meteor-defined reactive context such as a template helper
  • You pass your function to Deps.autorun()

Getting data from inside a computation will cause the computation to be registered for reactive execution if the data source follows Meteor’s reactivity contract. Reactive data sources include:

  • Session.get()
  • Meteor.Collection.find()
  • Certain Meteor app status objects (such as Meteor.userId)
  • Reactive data sources from outside packages (such as Iron Router’s Router.current() and others)
  • Any data source you make reactive (see above)

If either of these conditions is not met, you will not get reactive updates.

So this will not update reactively:

1
2
3
4
5
var a = 1;
Deps.autorun(function () {
  console.log(a); //source is not reactive
});
a = 2; //no effect

And neither will this, unless you place it in a template helper or other reactive context:

1
2
3
Session.set('a', 1);;
console.log(Session.get('a')); //not run in a reactive context
Session.set ('a', 2); //no effect

To make this code reactive, we need to meet both criteria:

1
2
3
4
5
Session.set('a', 1);
Deps.autorun(function () {
  console.log(Session.get('a')); //console logs 1
});
 //console logs 2

Quiz: will the following work reactively?

1
2
3
4
5
6
Session.set('a', 1);
var x = Session.get('a')
Deps.autorun(function () {
  console.log(x); //console logs 1
});
Session.set ('a', 2); //console logs ???

Answer: x is set using a reactive data source, and x is accessed in a reactive context, but console.log(x) will only run once. Why is this? Because the computation is registered when Session.get() is called. The line var x = Session.get('a') is called outside a reactive context, so Session does not register a callback (remember, it does nothing if it isn’t in a reactive context). x is then called in the autorun block, but it is not itself a reactive data source, and so the the computation will never rerun. A reactive data source cannot somehow pass its reactive properties to another object from outside a computation.

Am I in a computation?

If you are ever unsure, you can check Deps.active. It will be set to true when running inside a computation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Session.set('a', 1);
var x = Session.get('a')
console.log('Before autorun, Deps.active = ', Deps.active); //false
Deps.autorun(function () {
  console.log(x); //console logs 1
  console.log('Inside autorun, Deps.active = ', Deps.active); //true
});
console.log('After autorun, Deps.active = ', Deps.active); // false
Session.set ('a', 2);

Template.hello.greeting = function () {
  console.log('In helper, Deps.active = ', Deps.active); //true
  return "You are in " + bonusMode.get() + " mode.";
};

Deps.active will always tell you if calls to reactive data sources will have any effect. If you set a breakpoint and are paused in dev tools, you can find Deps under the global variable scope, or you can type Deps.active into the console.

Other Possible Surprises and Pitfalls

Computations run the whole function

Meteor intelligently updates only the parts of your template that have changes. This may mask the fact that the function in the computation behind it is running in full. All of the queries and other code in your template or other reactive environment are being rerun. It is the job of a different Meteor package (UIor Spark) to then evaluate what has changed in that data, if anything, and make the smallest possible change to the DOM. If you have performance problems, you can divide your reactive contexts and/or trigger them more carefully to avoid executing expensive code.

You need to .stop() your computations when you are done with them

Some computations are active for the whole life of the client session and can be left in place without problems. However, if you have code that is dynamically building computations with autorun, you should stop() your computations when you are done with them. This will allow the browser to garbage-collect unused objects.

Deps does not run on the server

Deps is a client-side package and is not available on the server. You must make sure your calls to autorun() are run either in a Meteor.isClient block or in code residing in the client directory.

You also should not expect Deps-style reactivity on the server. Although Meteor unites the client and server in using JavaScript, there is still a context-change in terms of the environment and functionality available. There are other tools on the server to make end-to-end reactivity work, but don’t expect server code to act like it’s in an autorun block. Reactive data sources will not rerun your server code.

For example, you cannot expect a Meteor.publish block to rerun when the result of Collection.find() changes (I made this mistake once, so it is not hypothetical!). The appropriate tool in this example is cursor.observe() or cursor.observeChanges().

Conclusion and Further Resources

If you have made it this far, well done! You now know everything you need to know to control client-side reactivity with confidence. We did not cover every aspect of Deps, but you are now in a position to easily follow the documentation. The Meteor docs are quite clear and explicit about what will run in a reactive context and what the reactive data sources are, and now you know the exact implications of that.

For more about the Deps package, have a look at Chris Mather’s excellent screencasts at EventedMind (he covers Deps here and here). You may even wish to look at the Deps package code itself. It is surprisingly concise and well-commented, and it is very nice bit of coding.

Now that you understand this part of Meteor’s magic, you are ready to make some magic of your own.

Announcing Project Quicksilver

| Comments

Single-command High-Performance Drupal/LEMP Deployment

Everyone deserves a fast server.

It is easy to think that performance is a concern mostly to large websites, but with Drupal 7, it is abundantly clear that performance matters to us all now. Drupal 7 simply performs poorly on shared hosting, even on better providers, and although it’s too early to tell, it doesn’t seem likely that Drupal 8 will improve the situation. Small users have been able to get a fairly reasonably-priced VPS for a while now, and some have done so, but this comes with a lot of extra work to set up the server. In addition, small VPSs have memory and processor limitations that can make them perform poorly on a traditional LAMP stack, so performance matters to small sites just as much as big ones.

We need a way to set up high-performance servers without spending hours manually running apt-get and editing config files.

Project Quicksilver solves these problems by giving you single-command installation of a high-performance LEMP stack, including basic tuning and best practices. The name is inspired by Project Mercury, a project that pioneered high-performance Drupal for large sites. It is intended to save you the time it would have taken you to manually set up your VPS, but leave you in full control of your setup. You can set up dev environments locally on your own machine that exactly mirror your production config. This will allow you to experiment with settings and packages that would be unsafe to try on production (want to try Redis? Varnish? Switch to MongoDB or PostgreSQL?), but also allow you to interact with your dev environment with the exact same tools and workflows you use on production.

Benefits

  • Deploy new VPSs in minutes with a repeatable process.
  • Alter your configuration quickly and reliably, including the ability to add and remove subdomains, users, and software packages.
  • Keep your server config in version control.
  • Store your complete server config with less than 1MB–no need to store and migrate full disk images.
  • Greatly improve migration and disaster recovery
  • Reliably rebuild compromised systems, when a straight backup might be tainted.

Technology Stack

Project Quicksilver uses a powerful and reliable stack.

  • Ubuntu: Ubuntu Server x64 12.04 LTS (Precise Pangolin). This is a common, well-supported release and will be supported through 2017.
  • Nginx: Nginx is simply much faster out of the box than Apache, and it is rock-solid, has a small memory footprint, and is easy to configure - perfect for small sites. It can also serve static assets directly without going to Drupal, resulting is much more efficient downloads for those resources.
  • MariaDB: MariaDB is a feature-equivalent branch of MySQL. It will behave exactly the same, but it has fewer bugs and better performance, and it is pure open source (see the Drupalcon Portland presentation).
  • PHP-FPM with APC: This version of PHP uses fastCGI and the APC cache to perform much better than stock PHP.
  • Memcached: Memcached provides a significant performance boost through caching of pages and other resources.
  • Drupal: Project Quicksilver is pretuned for a small Drupal site but is easy to customize for any size site. All of the most important settings for Nginx, FPM, and Memcached have been gathered in the single tuning.yml file, while basic server config and passwords go in the config.yml file, which is not tracked by Git to keep your passwords safe.

Server set up

phpMyAdmin is installed for gui-based management of your databases. For your site management needs, Project Quicksilver includes Git, Git-Flow, and Drush, as well as some scripts for adding and removing sites and subdomains. With these and a good remote file manager (e.g., Forklift(osx), Notepad++(win), FileZilla), you probably won’t miss CTools as much as you might imagine.

You can set timezone and locale settings with simple variables. User accounts are provisioned by default with oh-my-zsh for a pleasant shell experience. SSMTP is set up to allow you to link to an email sending service or gmail.

This technology stack makes for very snappy loading and better scaling, all wrapped up and delivered automatically to the vm of your choice in just a few minutes.

Devops Technology

  • Vagrant: Vagrant allows single-command provisioning of virtual machines, either on your computer using VirtualBox or VMWare, or on a live VPS provider like Linode or Digital Ocean. The initial implementation is for VirtualBox (free) and Digital Ocean (author’s current choice of provider, although we like Linode a lot too). As seen below, though, this project will be useful on any server, not just those with Vagrant support.
  • Ansible: Is a new entrant in the provisioning space, and like the better known Chef and Puppet, it can provision and configure a server in minutes. Ansible is already being used in the Drupal community. It has several advantages for small users:
    • The commands and API are very simple and can be learned in a few hours.
    • Ansible playbooks are written in YAML and are very human-readable. If you want to understand exactly how a configuration was done, following an Ansible playbook is like reading step-by-step directions.
    • It is designed from the ground up to work without an additional provisioning server. Ansible uses SSH to do its work, the same way you would, and it runs from your own computer. Ansible also has a very light footprint on the target system.
    • Ansible can provision on any system, so even if the Vagrantfile provided won’t allow you to build on the provider of your choice, you can always run the Ansible scripts on it after you create the VPS manually.

Drush

Project Quicksilver installs the latest version of Drush and uses it for all Drupal-related configuration. The choice of when to use a Project Quicksilver script vs. when to use Drush is simple: Project Quicksilver is there to handle the server-level tasks around site creation and destruction: Nginx config files, Drush alias files, etc. If you need a new domain or subdomain, use Project Quicksilver to establish the infrastructure for that in one command. Similarly, if you want to permanently remove a subdomain or tune server performance, use Project Quicksilver. Once the site is up and serving correctly, all you need is Drush to do any updates, imports, or Drupal configuration. Once a site is installed, Project Quicksilver scripts will ignore that site, so you can change configuration of the server without worrying that changes you made to your site with Drush (or manually) will be overwritten.

Get Started

For instructions to install and user Project Quicksilver, check it out on Github.