Jakub Arnold's Blog


Ember.js: Router Request Lifecycle

Router is the core part of Ember. Every time we go to a new URL it means the route object is called with our params and stuff. These are the hooks sorted in order in which they are called

Now let’s take a look at them in more detail

activate/deactivate

These were formely known as enter/exit, which are now marked as private. activate will be executed when user enters a route, be it from a transition or from a URL directly, and deactivate is executed when user transitions away from the route.

One of the most common use cases for me is doing a transaction rollback in deactivate.

App.PostsNewRoute = Ember.Route.extend({

  deactivate: function() {
    this.modelFor("postsNew").get("transaction").rollback();
  }

});

I find this mostly useful when having a new record form (or even when editing a record), where you basically want to rollback any changes which happened when the user exits the route. It doesn’t matter if the user submits the form first, because then the transaction will be comitted and there will be nothing to rollback.

model/serialize

To allow Ember to work with dynamic segments in the URLs we need to teach it how to serialize and deserialize our models. When we enter a URL directly (or reload the page) model will be called with params from the dynamic segments. Let’s take a look at an example

App.Router.map(function() {
  this.resource("post", { path: "/:post_id" });
});

App.PostRoute = Ember.Route.extend({

  model: function(params) {
    return App.Post.find(params.post_id);
  }

});

This is exactly what Ember will auto generate for us, along with a serialize hook

App.PostRoute = Ember.Route.extend({

  model: function(params) {
    return App.Post.find(params.post_id);
  },

  serialize: function(model) {
    return { post_id: model.id };
  }

});

it is important to note here that if we’re transitioning from a different route our model hook will not be called.

setupController

One step further after model comes setupController, which is meant to set additional properties on the controller, or override it’s content.

But beware, there is no autogenerated setupController hook which sets to content, this is done even before setupController is called in the setup hook of the route. This is basically simulates the following:

setupController: function(controller, model) {
  controller.set("content", model);
}

But it also means we can set additional properties on the controller without needing to explicitly set the content

setupController: function(controller, model) {
  controller.set("foo", "bar");
}

renderTemplate

The last one of the hooks is renderTemplate where you tell which template you want to render in which outlet.

By default renderTemplate will call this.render as follows

App.PostRoute = App.Route.extend({

  renderTemplate: function() {
    this.render("post", {
      into: "application",
      outlet: "main",
      controller: "post"
    });
  }

});

In this case render will render the post template into the application template’s main outlet with the PostController.

This is the place where you can chose to render into other outlets. For example let’s say that your application template has a sidebar outlet {{outlet sidebar}}.

App.PostRoute = App.Route.extend({

  renderTemplate: function() {
    // render with the defaults
    this.render();

    // and once more for the sidebar outlet
    this.render("similarPosts", {
      into: "application",
      outlet: "sidebar"
    });
  }

});

Important notes about controllerFor and modelFor

While calling controllerFor("posts") returns an instance of PostsController, calling modelFor("posts") doesn’t return content of the PostController. Instead it looks up the PostsRoute and returns it’s currentModel which is set when we return a value from the model hook.

Let’s see an example

App.PostsRoute = Ember.Route.extend({

  setupController: function(controller) {
    controller.set("content", App.Post.find());
  }

});

This will cause issues if we decide to use modelFor later on. PostsRoute will not have anything in currentModel and modelFor will return undefined, which might look weird as the controller has a content properly set.

Related
Ember.js