Imediava's Blog

Just another WordPress.com site

Tutorial on ember states and routing

Single page apps, states

In the quest for improving web applications many developers are starting to consider single-page apps as a way to offer a better user experience, more similar to native applications, without the nuances of having to wait after every server request [see single apps challenges and benefits].

Even if we call them single-page apps, they are composed of different steps that would correspond to different pages in a normal app. If we imagine a blog, a list of all the posts can be a step and a post with all its comments another.

In this post we will see ember’s approach to define apps based on steps through the construction of an extremely simple single page app.

Ember’s approach

States

The different steps of an application, are called  states  in ember and are organized in layers. At any moment an application can only be in one of its leaf states. Being in a state implies being in all its direct ancestors. If we take the following example:

Hierarchy of states

An application can only be in states 1.a, 1.b and 2. However if an application is in state 1.a, it means it is also in state 1 and 0 because they are both direct ancestors of 1.a.

Ember basics

We also need to understand that ember apps are based on three main building blocks:

  • Templates:  Strings that contain variables that are replaced by values when they are evaluated. Ember uses handlebars template engine.
  • Controllers: Objects used to manipulate data in the application
  • Views: Elements responsible for: “combining templates with data to render as sections of a page’s DOM, and … responding to user-initiated events.”  Basically a view has an associated template and uses a controller to access the data it needs to render it.

I’ll call the combination of this three elements a section.

Elements that form a section

Ember tools to build multi-state apps

Apart from the main building blocks, ember provides two tools that are extremely useful when building an app based on states: the {{outlet}} tag and the Router.

  • The {{outlet}} tag allows to build a hierarchy of sections by providing a mean for a container template to include a child template
  • The Router allows to specify all the possible states of an app and map them to urls.

Joining them together we can create a tree of states, where every state has an associated section. This makes possible the construction of really complex apps where the sates are decoupled from each other while the acceptable combination of states is well defined.

Hierarchy of states associated to sections

Our simple application

Once we know the concepts behind the approach we’ll show an example of how it works with our basic application. Our example will only have a master section with a child. We will build the master section and then we will see how we can add a child section with its corresponding state. Adding subsequent children will only mean repeating the same steps adapting them to our new states.

The master view

For our master section of the application will use the following handlebar template:


<script type="text/x-handlebars" data-template-name="master">
This is the master.
  {{outlet}}
</script>

The {{outlet}} helper is the container for the child template, it will server to render the main section inside the master.

To define the controller and the view for the master section we need to pass them as the ApplicationController and ApplicationView parameters when we build Ember.Application. The need to be called like that to conform with ember’s convention:


App = Ember.Application.create({

  ApplicationController : Ember.Controller.extend({
  }),

  ApplicationView : Ember.View.extend({
    templateName: "master" // We use the master template for this view
  }),

});

To add the states we will pass a Router property of type Ember.Router to our application. The router will be in charge of representing the different application’s states and matching them to urls (see Ember.Router for a deeper explanation on how this type works). It needs a root parameter that represents the app’s root state and as we’ve said before is associated to ApplicationView and ApplicationController.


App = Ember.Application.create({

  //......

  Router : Ember.Router.extend({
    // Every rooter must have a root state
    // whose controller and view are ApplicationController and ApplicationView
    root: Ember.Route.extend({
	// here will have all the states accessible by the user
    })
  })
});

The main state

With this simple code we already have our master section working. Now we need a child section that will render inside the master.

Again we need to define a template for it:


<script type="text/x-handlebars" data-template-name="main">
My main with its context:  {{ variable }}.
</script>

We also create the controller and the view for the state. We will call them, according to ember’s convention, with the state’s name with a capital letter.


App = Ember.Application.create({

  //......

  MainView : Ember.View.extend({
    templateName: "main"
  }),

  MainController : Ember.Controller.extend({
    variable: "my main",
  }),

  //......

});

We still need to add the main section’s state to the rooter. Since the main is inside the master we will place our main state inside the root state.


  root: Ember.Route.extend({
     // here will have all the states accessible by the user
     main: Ember.Route.extend({
     }),
  })

Old single page apps didn’t allow to bookmark a state or go back to a previous state by clicking on the back button because states where not represented by an url. However this is not a problem with ember that allows assigning a mapping between an state and a url.

To make our main state available at ‘#/main’ we add the following to the state:


  root: Ember.Route.extend({
     // here will have all the states accessible by the user
     main: Ember.Route.extend({
        //main => /main/ - will be accessible at #/main/
        route: '/main/',
     })
  })

Finally we want to tell ember that when we are in the main state, the main view should be rendered inside the master. For that we need to add a callback with the connectOutlets property passed to the state. This callback must contain the following method call:


router.get('applicationController').connectOutlet('main')

Since we have followed ember’s convention this method internally creates the instance of MainView, assigns it to the MainController singleton and then passes it to the ‘applicationController’. The framework knows that it has to take MainView and MainController because they follow the naming convention for the ‘main’ state.


  root: Ember.Route.extend({
     // here will have all the states accessible by the user
     main: Ember.Route.extend({
        route: '/main/',
	connectOutlets: function(router, event) {
          router.get('applicationController').connectOutlet('main');
        }
     }),

   //........

   });

With this we finally have our final app based on ember states, with our main page accessible at http://whatevertherootis/#/main:

App = Ember.Application.create({

  ApplicationController : Ember.Controller.extend({
  }),

  ApplicationView : Ember.View.extend({
    templateName: "master"
  }),

  MainView : Ember.View.extend({
    templateName: "main"
  }),

  MainController : Ember.Controller.extend({
    variable: "my main",
  }),

  Router : Ember.Router.extend({
    root: Ember.Route.extend({
      main: Ember.Route.extend({
        route: '/main/',
        connectOutlets: function(router, event) {
          router.get('applicationController').connectOutlet('main');
        }
      })
    })
  })
});

The source code for the full application with and index.html page can be downloaded from here.In later posts I will show how to add another state and link it from our main, how to redirect the root of our app to the main state and how to add nested states to our app.

About these ads

5 responses to “Tutorial on ember states and routing

  1. Eric August 28, 2012 at 1:51 pm

    Great tutorial. It’s tough to find recent articles on Ember.js that work with the newest version. Thanks for this!

  2. Aras October 5, 2012 at 9:04 pm

    I just found this tutorial. You do a great job at explaining the difficault concepts behind Ember routing. After reading this I understand routing much better. I hope to see more Ember tutorials from you. Thank you!

  3. Pheonix January 25, 2013 at 5:35 am

    Really nice tutorial. It helped me a lot. It will be really nice if you can post some more tutorials.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: