Ember JS: MVC in the browser. Part 1 – Routing

Ember JS is a Javascript client side MVC framework featuring all your favourite constructs such as controllers, models, views and routing. Client side means that it all runs inside the browser meaning that rendering times are extremely fast with no calls to the server to fetch pages. Ember has an optional restful data model called ember-data which handles the GET/PUT/POST/DELETE lifecycle of your models. Go to http://emberjs.com and download the starter kit which I used as the skeleton for this application or download the complete code for this part of the tutorial here. Have a look at the video tutorial as well, you will no doubt recognise some overlap with these posts. I primarily wrote this to get everything I had learned about ember in one place, I found the emberjs.com tutorials very useful but also unordered and was constantly flicking between various different sites, stack overflow etc. We will be building a simple blog application using a lot of the facts that can be found in the emberjs.com tutorials but with some pointers and helpers along the way. We will look at the ember components needed to handle the model lifecycle and views and later will write server side code to find, create and save the models. You can use any server framework you like but I prefer to use rails however ASP.net or whatever are just as good as long as they can handle REST and JSON. We will also look at using CORS or JSONP to load data directly to the browser.

For me it was easier to think of an ember app in terms of a rails one so where better to start than with the routes. Our simple application will consist of posts which can have many comments. In Rails (3.x) your routes file would consist of a root and some resources corresponding to the models. So lets say you use some scaffolding to get started

rails new blog
rails generate scaffold Post name:string title:string content:text

This gives us the following in config/routes.rb

resources :posts

We would also normally add a root after deleting public/index.html

root :to => 'welcome#index'

rake routes shows:

posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
root / welcome#index

We want to create a similar route structure in ember.

To begin with we will keep all the html and UI bits in index.html and all the application logic in app.js (look inside the js directory to find it). The ember starter pack has an example index.html, open it in your browser and you will see that it renders a simple page with 3 colours listed. Have a look at it in an editor and you can see the script blocks within the html that define the templates. There is one with an {{outlet}} and an index template. If you look at js/app.js you will see that the IndexRoute returns an array of strings which are rendered into the index template. The templates use handlebars which is similar to erb in rails but is contained within javscript <script> blocks with a type of “text/x-handlebars”. Unfortunately we don’t have the benefit of rails logging what is happening when we go to a url in our application but we can ask ember to tell us what route it is rendering, to do this we will change the main ember application javascript. Open up js/app.js in an editor and change App=Ember.Application.create() to

App=Ember.Application.create({
  LOG_TRANSITIONS: true
});

Log transitions will help us to see what the router is doing when we move from page to page. If you open up a browser console and reload the index page you will see that it says “Transitioned into ‘index'”

We will now add our own bits to the starter app. Start by removing the existing handlebars templates from index.html and add the one shown below. Remember to place it within the html <body> tag.

 

<script type="text/x-handlebars">
  <body>
    <header>
      <h1>Blog</h1>
    </header>
    <div>
      {{outlet}}
    </div>
    <footer>
      &copy;2013 Me.
    </footer>
  </body>
</script>

 

Within this script block we have defined a template which has type text/x-handlebars, this is the basic application template into which other templates will be rendered, as you saw with the index route earlier. Anything wrapped in {{ }} tells handlebars to do something. The {{outlet}} is similar to <%= yield %> in rails, the placeholder where other templates are rendered.
We also need to create our application inside app.js. Remove the section with App.IndexRoute, leaving only the App definition with the LOG_TRANSITIONS we added

App=Ember.Application.create({
  LOG_TRANSITIONS: true
)};

We will start by defining a simple router, add the following to app.js

App.Router.map(function() {
});

and then populate it with a route

App.Router.map(function() {
  this.resource('posts');
});

Refresh the page and you will see that we have transitioned into index but there is nothing there except the header and footer text we specified. That is expected since we removed the index template, we will add them back during part 2. We can use App.Router.router.recognizer.names in the web console to see all the routes. It returns a js object within which all the current routes are show. Use it and you will see that we have the routes index and posts. Seems that it does not behave like you think it would in rails and only creates 2 named routes ‘index’ and ‘posts’ ie ‘/’ and ‘/posts’. What we also need is /posts/:id. To do this we have to create a route for instances of a post.

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

Again this doesn’t quite get us what we want and only gives us the ‘index’, ‘posts’ and ‘post’ routes which will work fine but we want member routes under ‘post’ so that we can show, edit, delete etc. To begin with what we actually want is ‘index’, ‘posts’ and ‘post.index’. ‘post.index’ is ‘/posts/:post_id’ which in ember terms is the equivalent of rails route ‘show’. We want a page which shows all posts and one which shows a single post. We need to add some more to the router for this.

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

We have defined an inner function of the ‘post’ resource where the member routes will be defined. You get ‘post.index’ for free. To look at all the routes you have defined again use App.Router.router.recognizer.names in a browser console. We now have ‘index’, ‘posts’ and ‘post.index’. We have some basic routes defined, now we need some UI magic. In part 2 we will look at templates and handlebars along with the models, store and routes to hook them together.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>