Chapter 13. EmberJS

Ember.js, like Spine and Backbone.js, is a lightweight framework for helping developers build client-side applications in JavaScript using the MVC pattern.

So how is Ember.js different from the others?

While Backbone.js, for example, is a very small and unopinionated foundation upon which you are expected to build your own conventions and additions, Ember.js provides strong conventions and helpful tools for the problems that we think most developers will face when building large-scale applications.

A good way to summarize this is that Ember.js wants to eliminate trivial choices. Good developers love to argue, so when there is no official solution to a problem, every team has to spend time deciding how they should tackle it. Because everyone is doing things a little bit differently, shared knowledge is reduced, help is difficult to obtain, and time is wasted training new developers in your particular dialect.

Some examples of trivial choices that Ember.js decides for you:

  • What templating language should we use?

  • Who is responsible for memory management?

  • How should the application be structured?

  • How should classes and instances be named?

To be clear, these are all things that can be changed. For example, Ember.js can be used with a different templating language than Handlebars (the default), or none at all. But because there is a default, people tend to stick with it, and there is serious value in having the entire community on the same path.

Ember.js also enhances JavaScript in two ways:

  1. It provides implementations of features that will be in the language, but aren’t yet. Observers and maps are two good examples of this. You can start using these features right away, and Ember.js will automatically use the native implementation for you when it’s available.

  2. It provides shorthand for things that are otherwise laborious to do in vanilla JavaScript. For example, the Ember.js object system allows you to succinctly specify class hierarchies, instead of the ponderous process of manually wiring up object prototypes, as described in Chapter 1.

When it comes to application structure, libraries like Backbone.js give you the primitive types—models and views—but leave it up to you to decide how to wire them together. Ember.js enforces certain patterns through conventions. These conventions serve two purposes:

  1. The code to implement common patterns is extremely concise.

  2. Using objects differently than how they were intended feels comparatively long-winded.

If you are feeling friction when using Ember.js, it usually means you are using the framework in a way that its authors didn’t intend. This is a sign that perhaps you should step back and rethink how you’re approaching the problem!

Many developers appreciate having an opinionated framework that enforces patterns through strong conventions. It means that multiple developers can look at the same problem and have it decompose architecturally in their heads into more-or-less the same solution, which is especially important in larger applications and on teams.

Some developers, however, chafe under these conditions. They want to be able compose the primitives however they’d like, and are willing to take responsibility for the long-term maintenance of the patterns they use.

For small- to medium-scale applications, you should choose whichever feels most appropriate to you. Many developers new to web application development like to cut their teeth on libraries like Spine and Backbone.js, so they fully understand their limitations before graduating to the stronger conventions of Ember.js.

In particular, so-called “island of richness” applications are particularly well-served by Backbone.js and Spine. These are small, interactive apps that are designed to be embedded inside existing content, usually a static page like a news article or blog post.

However, for large applications, especially those whose UI takes over the entire page, you should seriously consider using an opinionated framework like Ember.js. From my experience talking to many web developers, those who think they can get away with small libraries in larger applications end up with custom, buggy, half-implemented versions of more comprehensive frameworks like Ember.js that they alone must maintain.

In this chapter, you’ll learn about a few of the core concepts in Ember.js, and how they work together. Finally, you’ll apply what you’ve learned by building a Todos application.

The Application

Every Ember.js app starts by creating an application object:

window.App = Ember.Application.create();

Calling create() on an Ember object returns a new instance of that class. Here, we’re creating a new instance of Ember.Application and assigning it to the global variable App.

The Application object serves a number of important roles in your application. The most obvious is that it serves as a namespace, into which all of the other classes you define will be placed. For example, you might have a controller called App.UsersController or a model called App.User.

Less obvious is what the Application is doing for you behind-the-scenes: creating instances of your controllers, finding templates in DOM and compiling them, and kicking off routing. While advanced users can tweak what happens here to their heart’s content, the default behavior for all of these things should be enough to get you started.

Another nice feature the Application instance gives you is sane toString() semantics. As you’re developing your Ember.js application, try it in the console—it makes debugging much easier:

user.toString();
// => "<App.User:ember123>"

Models

Models are objects that represent persistent data. They are responsible for loading data from your server, making it available to templates, then sending it back to the server if there are any changes to be saved.

The Store

Before you begin defining and loading models in your Ember.js app, you must first define a store:

App.Store = DS.Store.extend();

The store is important for several reasons. First, it is responsible for instantiating the records that your application asks for. If you ever ask the store for the same record multiple times, it will always return the same record. This feature, called an identity map, means that different parts of your application don’t get out of sync when displaying the same data.

Second, the store is responsible for communicating the changes you make in your application, such as modifying attributes or creating new records, to the adapter.

The Adapter

The web is full of many different APIs. Some of them transmit JSON over HTTP; others send XML representations of the data. Additionally, newer web browsers support brand new modes of communication (like WebSockets), and can even natively handle binary data.

This means that we can no longer assume that sending JSON to RESTful HTTP endpoints is sufficient for all modern JavaScript web application.

Thankfully, Ember allows you to easily encapsulate how your application talks to your backend in an object called the adapter. The default adapter is called DS.RESTAdapter, and is designed to work with the conventions of RESTful HTTP APIs like those built using Ruby on Rails.

If you need something different, you can write a completely custom adapter that uses whatever combination of technologies you’d prefer. The best part is that because the adapter completely encapsulates the details of server communication, changing your server backend does not require any changes to the rest of your application!

To tell the store which adapter to use, just override its adapter property:

App.Store = DS.Store.extend({
  adapter: 'App.AwesomeWebSocketsAdapter'
});

Defining Models

Once you’ve defined your store, it’s time to set up your models. Each model should represent a specific kind of object, and is made up of attributes and relationships.

Start by creating a new subclass of DS.Model. To tell Ember which properties are attributes that should be fetched from the server, use the DS.attr helper:

App.User = DS.Model.extend({
  name: DS.attr('string')
});

The argument passed to DS.attr() tells Ember what type of attribute it is, so that it can be converted into the correct JavaScript type when the JSON response is received.

Sometimes, a model may have properties that can be derived from other properties. For example, imagine that you are writing a commenting system and want to give users a VIP badge once they’ve posted 100 comments.

There’s no need to save the fact that the user is a VIP to the server, since you’d just be duplicating information (the number of posts) that you already have. Instead, just define a computed property that uses the number of posts to compute whether or not the user is a VIP:

App.User = DS.Model.extend({
  name: DS.attr('string'),
  commentCount: DS.attr('number'),

  isVIP: function() {
    return this.get('commentCount') > 100;
  }.property('commentCount')
});

This defines a property called isVIP that is true when the user has more than 100 comments, or false if they have fewer than 100.

The arguments passed to the property() function tell Ember the computed property’s dependent keys. If one or more of a computed property’s dependent keys changes, Ember knows that it needs to recalculate the computed property (and update any templates on screen that may have been using it).

While computed properties really shine when used to build up higher-level properties from primitive attributes in your models, you should note that you can use computed properties everywhere, from your controllers to your views.

Once you’ve defined a model, you can create a new record and initialize its attributes:

var user = App.User.createRecord({
  name: "Tyrion Lannister",
  commentCount: 99
});

Make sure you access the properties of your models (as well as all Ember objects) using their get and set methods:

user.get('name');
assertEqual( user.get('name'), "Tyrion Lannister" );
assertEqual( user.get('isVIP'), false );

user.set('commentCount', 100);

// Now that commentCount has changed, the isVIP
// computed property updates automatically.
assertEqual( user.get('isVIP'), true );

Using these accessors ensures that templates have a chance to update themselves, computed properties can be recalculated if necessary, and any registered observers can be fired.

Defining Relationships

You can tell Ember that two models are related to one another by using the DS.hasMany and DS.belongsTo helpers. For example, you can describe a one-to-many relationship between Posts and Comments:

App.Post = DS.Model.extend({
  comments: DS.hasMany('App.Comment')
});

App.Comment = DS.Model.extend({
  post: DS.belongsTo('App.Post')
});

Once you’ve done that, you can treat the relationships like regular properties:

var post = App.Post.createRecord();
var comment = App.Post.createRecord();

// You can set the `belongsTo` side
comment.set('post', post);

// ...or add it to the parent `hasMany` array
post.get('comments').pushObject(comment);

Whichever side of the relationship you modify, Ember will automatically keep the inverse side up-to-date.

As you can see here, hasMany relationships behave like arrays. To make changes to them, use Ember’s array mutation methods to ensure that Ember is notified of the changes you make. For the full list, see the documentation for Ember.MutableArray and Ember.MutableEnumerable.

Templates

As you’ll remember from Chapter 5, a template allows you to take a fragment of HTML annotated with template variables, combine it with a JavaScript object (called the template context), and produce a string of HTML where the template variables have been replaced with values from the object.

Ember.js uses Handlebars, a logicless JavaScript templating language. Logicless means that, unlike some other templating libraries, you cannot embed arbitrary JavaScript expressions in your template. Instead, you are only allowed to display the properties of the current template context.

While this may seem like a limitation, it is in fact what allows us to implement one of Ember’s coolest features: once you render a template, it automatically stays up-to-date as the underlying template context changes.

That means you never have to write custom render code or add model listeners in your views. In fact, you can usually skip creating views altogether. Just describe the HTML you want in a template, and it is Ember’s responsibility to set up bindings and update the DOM if the underlying object ever changes.

Here’s an example of a simple Handlebars template:

<div class="user-info">
Welcome back, {{firstName}} {{lastName}}!
</div>

Note that simple expressions are wrapped in double curly braces. In the above example, {{firstName}} means look up the firstName property from the current context and put it into the HTML.

There are also block helpers, which look similar to simple expressions but begin with a hash (#) in the opening expression and a slash (/) in the closing expression:

{{#if unreadPosts}}
  Unread Posts: {{unreadPosts.length}}
{{/if}}

Block helpers affect the content they enclose. In this example, information about the number of unread posts is only displayed if the template context’s unreadPosts property evaluates to a truthy value.

You can save your templates inside your application’s HTML file, by wrapping it in a <script> tag:

<script id="user-info-template" type="text/x-handlebars">
  <div class="user-info">
    Welcome back, {{firstName}} {{lastName}}!
  </div>
</script>

Note that the <script> tag includes an id attribute. You can use this identifier later to tell Ember.js which template to display. If you have a <script> tag without an id, Ember.js assumes that it is your main template and will render it immediately.

Built-in Helpers

Above, you saw the {{#if}} helper in action. The {{#if}} helper renders its content only if the value provided to it evaluates to truthy (that is, it’s not 0, false, null, undefined, an empty array or an empty string).

You can also provide an {{else}} block in cases where the value is false:

{{#if isLoaded}}
  <h1>{{title}}</h1>
{{else}}
  Loading…
{{/if}}

There is an inverse of {{#if}}, {{#unless}}, that only renders its content if the value provided is falsy:

{{#unless isAdministrator}}
  Karma: {{karma}}
{{/unless}

By default, a template’s context is the controller with which it is paired. You can change the template context inside a block by using the {{#with}} helper:

<p>Name: {{firstName}} {{lastName}}</p>

{{#with profile}}
  <p>Bio: {{bio}}</p>
  <p>Address:  {{address}}</p>
{{/with}}

Inside the {{#with}} block, the bio and address properties will be looked up on the controller’s profile object, instead of the controller itself.

Finally, the {{#each}} helper allows you to enumerate an array of objects. Inside the {{#each}} block, the context is set to each item in the array in turn:

<h1>{{blogTitle}}</h1>
<ul class="posts">
{{#each posts}}
  <li>{{title}} by {{author}}</li>
{{/each}}

Custom Helpers

You can write application-specific helpers for formatting values to be displayed in your HTML. For example, we often want to properly pluralize words based on a number associated with them. Imagine we wanted a {{pluralize}} helper that could be used like this:

Remaining: {{pluralize remainingItems}}

If the value of remainingItems was 1, it would render:

Remaining: 1 item

Otherwise, it would render:

Remaining: 16 items

If we wanted to customize which word was pluralized, we could pass the the word option:

Remaining: {{pluralize remainingItems word="post"}}

This would render something like:

Remaining: 16 posts

As it turns out, making helpers like this is extremely easy. The above example can be implemented in only five lines of code:

Ember.Handlebars.registerBoundHelper('pluralize', function(value, options) {
  var word = options.hash.word || "item";
  if (value === 1) { return '1 ' + word; }
  return value + ' ' + word + 's';
});

This registers a pluralize helper that receives the value that you pass to it. Options passed to the helper (like word="post") can be accessed via the options.hash object.

Whatever value you return from this helper will be placed into the template at the appropriate location. If the value changes (in the examples above, remainingItems), the helper will be re-run and the DOM will be updated.

Controllers

In Ember, controllers have two responsibilities:

  1. They respond to actions from templates.

  2. They present models to templates.

In many cases, you will not need a controller at all. Wait until you need to transform or augment the properties of a model, or respond to a user action, to start adding controllers to your application.

Responding to Actions

When you do need a controller, the first step is to determine what kind of model it will represent.

If it’s not representing a model at all (that is, it only responds to actions but does not augment or transform a model), define an Ember.Controller subclass:

App.AuthenticationController = Ember.Controller.extend({
  logout: function() {
    // ...put logout code here...
  }
});

You can send actions to a template’s controller using the {{action}} Handlebars helper:

<button {{action "logout"}}>Log Out</button>

By default, actions are sent when the user clicks on the element to which you have added the {{action}} helper. You can change how the action is triggered using the on attribute:

<button {{action "reload" on="doubleClick"}}>Double-click to reload</a>

You can also use this to trigger an action on a controller when the user submits a form:

<form {{action "submitForm" on="submit"}}>
  <label for="name">Name</label>
  {{view Ember.TextField valueBinding="name"}}
  <button type="submit">Submit</button>
</form>

This would trigger the submitForm action if the user clicked the Submit button, or if they hit the enter key while the form’s text field had focus.

Model Controllers

Remember that one of the responsibilities of a controller is to present a model to one or more templates. It may augment or transform the properties of a model, in order to make them more appropriate for display to the user. Let’s look at an example to illustrate exactly what this means.

If your controller presents a single model to a template, you would define a subclass of Ember.ObjectController. In this example, the PostController represents a single App.Post model:

// Model
App.Post = DS.Model.extend({
  title: DS.attr('string'),
  comments: DS.hasMany('App.Comment')
});

// Controller
App.PostController = Ember.ObjectController.extend();

When the application starts, the router will tell the PostController to represent a Post record by setting its model property. (We’ll describe the router in detail later in this chapter. For now, just know that it is responsible for connecting controllers to models and templates to controllers.)

Now, let’s say the template connected to the PostController asks for its title property:

<h2>{{title}}</h2>

Here’s where things get really cool.

You probably noticed that we have not defined a title property on the PostController. But, because it’s a subclass of ObjectController, it will automatically look up the property on its underlying model. As far as the template is concerned, the controller and the model look exactly the same!

In other words, the ObjectController acts as a proxy to its model; any request for a given property returns the value of the property with the same name on the model.

Sometimes, though, you may store information in a way that is different than how it should be presented. For example, we might store the price of an item as an integer in the database. When we present it to the user, however, we’d like to format it as currency:

var attr = DS.attr;

App.Item = DS.Model.extend({
  name: attr('string'),
  price: attr('number')
});

App.ItemController = Ember.ObjectController.extend({
  price: function() {
    var price = this.get('model.price');
    return "$" + parseFloat(price).toFixed(2);
  }.property('model.price')
});

Then, in our template:

<table>
  <tr><th>Item</th><td>{{name}}</td>
  <tr><th>Price</th><td>{{price}}</td>
</table>

When we render the template for the user, they’ll see a nicely formatted price, like $10.50, instead of the integer 1050.

As far as the template is concerned, it’s getting information directly from the model. But we can selectively transform values to make them more appropriate for our templates, without having to put view logic in the model layer.

Array Controllers

Besides the ObjectController, Ember has another model controller: Ember.ArrayController. This is used to present a collection of models to a template.

Like with ObjectController, ArrayController presents itself to templates as though it were the underlying model collection:

App.PostsController = Ember.ArrayController.extend();
<h1>Posts</h1>
{{#each post in controller}}
  <h2>{{post.title}}</h2>
{{/each}}

Perhaps the most important use of array controllers is to present aggregate information about the underlying models to the template.

For example, imagine our blog software tracks which posts have already been read by the user. We want to show a count of how many posts the user has left to read, before the supply of new posts is exhausted.

The one-two punch of array controllers and computed properties makes this extremely easy:

App.PostsController = Ember.ArrayController.extend({
  unreadCount: function() {
    return this.filterProperty('isUnread').get('length');
  }.property('@each.isUnread')
});

While not a lot of code, there is a lot going on here. Let’s break down exactly what this is doing:

  • First, we define a computed property called unreadCount.

  • Inside the computed property:

    • We use the filterProperty method to get an array of all of the models in the underlying array whose isUnread property is true.

    • We return the length of that array. This becomes the value of the computed property.

  • Finally, we tell Ember what other properties the value of this computed property relies on. In this case, we use a special property called @each. The dependent key @each.isUnread means recompute unreadCount every time the isUnread property of any of the underlying models is changed.

We can use this synthesized property just like a normal property in our templates. For example, here’s what the posts template might look like:

<h1>My Cool Blog</h1>
{{#each post in controller}}
  <h2>{{post.title}}</h2>
{{/each}}

<p>Unread Posts: {{unreadCount}}</p>

As posts are added or removed, or the isUnread property of any of the Post models changes, the unreadCount value displayed to the user will be updated automatically. Not too shabby for three lines of code and a simple Handlebars template.

Router

In Ember, the router is responsible for figuring out what templates should currently be on screen, which controllers the templates should be connected to, and which models those controllers should represent. In many ways, it is responsible for tying together all of the concepts that you’ve learned about so far in this chapter, and telling them all how to work together. If your application were a NASA space project, you could perhaps think of the router as Mission Control.

As the user interacts with your app, the router updates the templates, controllers and models on screen in response to their actions. Because it’s important for users to be able to share what they’re seeing with friends, the router also updates the URL as the UI changes.

Additionally, if the user enters the application via an existing URL, the router is responsible for rebuilding the state of the application.

For example, imagine we’re writing an application for reading blog posts. If our user visits blog.example.com/, she should see the list of blog posts available. If she clicks one of the entries that interests her, two things should happen:

  1. The template for displaying a single blog post should replace the template that displayed a list of posts.

  2. The URL should be updated to reflect what’s currently on screen. For example, the new URL may become blog.example.com/post/17.

If our user takes that URL and tweets it to her followers, they should see the same blog post when they click the link.

Defining Routes

You can list the routes in your application by calling your router’s map method.

App.Router.map(function() {
  this.route("about");
  this.route("favorites");
});

This tells Ember to render the about template when the user visits /about, and to render the favorites template when they visit /favorites. There is also an implicit route from / to the index template.

(Note that you do not need to define App.Router; one is provided for you automatically when you created your Ember.Application.)

You can configure which URL a particular template gets mapped to by passing an optional path argument:

App.Router.map(function() {
  this.route("favorites", { path: '/favs' });
});

This would tell Ember to render the favorites template when the user visits /favs.

As you’ll recall, templates are always bound to a controller which represents a model. So far, we’ve learned how to display a template when a given URL is visited. But how do we tell it what model goes with it?

To do so, we need to first define a route handler. The route handler is an object that customizes how a particular route behaves. For example, to specify the model that is associated with a template, return it from model hook:

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return App.Post.find();
  }
});

This tells Ember to render the index template with the results of App.Post.find() as its context.

We didn’t need to do any additional configuration to make the route handler start working. Just define the handler with the right name, and Ember will know what to do automatically.

The rules for figuring out the route handler name are easy: it’s the name of the route, capitalized, with “Route” at the end. For example:

App.Router.map(function() {
  this.route("favorites");
  this.route("mostPopular");
});

These routes would have the following route handlers associated:

  • App.IndexRoute (the implicit route for /)

  • App.FavoritesRoute

  • App.MostPopularRoute

Resource Routes

Most of the routes in your application will usually be associated with a specific model. For example, you might have a /posts route, which displays a collection of posts, and a /comments route, which displays a collection of comments.

When a route corresponds to a particular model, use the resource() method instead of route():

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

Resource routes can contain nested routes. This is useful for filtering a collection of models, for example.

Imagine that in our blog application we wanted to users to be able to filter posts by whether they were unread, or whether the user had favorited them:

App.Router.map(function() {
  this.resource('posts', function() {
    this.route('unread');
    this.route('favorites');
  });
});

The above route definition would create the following URLs, with associated controller, route handler, and template names:

  • /posts

    • App.PostsIndexController

    • App.PostsIndexRoute

    • posts/index

  • /posts/unread

    • App.PostsUnreadController

    • App.PostsUnreadRoute

    • posts/unread

  • /posts/favorites

    • App.PostsFavoritesController

    • App.PostsUnreadRoute

    • posts/favorites

Here is a good rule of thumb for figuring out which kind of route to create:

  • If it’s a noun, use resource().

  • If it’s an adjective or an adverb, use route().

Dynamic Segments

Often, parts of the URL will contain information (typically the ID) about the model that should be displayed. For example, if you are viewing the post with ID of 17, the URL might be /post/17. Obviously, you don’t want to have to enumerate a different route for each post in your database!

Instead, you can define a route with a dynamic segment. Dynamic segments allow you to match any URL that follows a pattern, and extract information about the model out of it:

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

Note that the path for our post resource contains a dynamic segment: :post_id. Dynamic segments should start with a colon (:), contain the model name, and end with _id.

Instead of having to manually configure which model is associated with this route by defining a model hook on a route handler, Ember is smart enough to know that it should look for the App.Post model with the given ID.

Changing Routes

Use the Handlebars {{linkTo}} helper to create a link that your user can click on to change what appears on screen. Imagine we’re creating a simple personal website that contains several different static pages that we want users to be able to switch between. Our routes look like this:

App.Router.map(function() {
  this.route('about');
  this.route('contact');
  this.route('resume');
});

In our about template, we can link to the contact and resume routes like so:

Welcome to my personal site!

Please {{#linkTo "resume"}}read my resume{{/linkTo}} or
{{#linkTo "contact"}}contact me!{{/linkTo}}

As the user clicks these links, the old template will be removed and the new one will be put in its place. This happens extremely quickly, because all of the templates have already been loaded and the browser does not need to talk to the server to display the new template.

Stitching together static pages like this is a great way to quickly prototype web applications, while writing practically no code.

What happens if we want to link to a route with a dynamic segment? How do we tell it which model that route should represent?

That turns out to be easy too: just pass the model as an argument to the {{linkTo}} helper.

For example, imagine we have the following route definitions:

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

The posts template displays a list of the blog posts the app knows about. Just include the current post as the second argument to {{linkTo}}, and Ember will take care of the rest:

<h1>My Cool Blog</h1>
<ul>
{{#each post in controller}}
  <li>{{#linkTo "post" post}}{{post.title}}{{/linkTo}}</li>
{{/each}}
</ul>

Building a To-Do List

Now that you’re familiar with the core concepts that make an Ember.js app tick, let’s put them all together to build a simple todo application.

Unlike the previous versions of this application that you’ve built, you won’t be writing a lot of JavaScript. Instead, you’ll describe what you want to happen using templates, and Ember will automatically keep them up-to-date for you.

Let’s start with the minimum needed to get a simple page that loads in all of our dependencies:

<html>
  <head>
    <title>Ember.js Todos</title>
    <link href="todos.css" media="all" rel="stylesheet" type="text/css"/>
    <script src="lib/handlebars-1.0.rc.2.js"></script>
    <script src="lib/jquery-1.8.3.js"></script>
    <script src="lib/ember.js"></script>
    <script src="lib/ember-data.js"></script>
    <script src="todos.js"></script>
  </head>
  <body>
  </body>
</html>

Now, let’s move on to todos.js, which contains all of the JavaScript necessary to run the application.

First, remember that every Ember application starts with an instance of Ember.Application. Let’s add it to the top of todos.js now:

App = Ember.Application.create();

This creates a namespace for all of the other classes we’ll define, as well as generating all of the infrastructure necessary to get the app to running.

Next, we’ll tell Ember that we’d like our application to have a data store by defining a subclass of DS.Store:

App.Store = DS.Store.extend({
  revision: 11,
  adapter: 'DS.FixtureAdapter'
});

The revision specifies the current Ember Data API revision, and the adapter tells the store where to go to load and save records. In this case, we’re using revision 11 of Ember Data, and we’re telling the store that it should try to fetch records from fixture data.

Because Ember Data is frequently being improved, specifying the revision number means that the library authors can alert you when the API changes. This makes it easy to stay up-to-date with improvements as they happen, without having to trudge through mysterious errors.

Now that we have a store, we can describe the models in our application. Because this is a simple application, we only have one: the Todo.

App.Todo = DS.Model.extend({
  isDone: DS.attr('boolean'),
  content: DS.attr('string')
});

This defines a model called App.Todo that has two attributes:

  • A Boolean, isDone

  • A string, content, which contains the contents of the todo

Now it’s time to tell Ember what template we want to display when our user loads the application. Using the built-in router, we’ll tell Ember that it should render the todos template when the user visits /.

App.Router.map(function() {
  this.route('todos', { path: '/' });
});

If we don’t do anything else, Ember will automatically render the contents of the todos template as soon as the user loads the page. However, this wouldn’t really qualify as an application — it’s just a static page.

In order to make the page dynamic, we need to tell Ember what models should be displayed by the template. We do that using route handlers. In this case, because we’re displaying the todos template, we can customize the App.TodosRoute handler to specify which model gets rendered:

App.TodosRoute = Ember.Route.extend({
  model: function() {
    return App.Todo.all();
  }
});

By returning App.Todo.all() from the route handler’s model hook, we’re telling Ember: When the user visits our web application at /, find all of the Todo models you know about and render them using the todos template.

Finally, we’ll define a controller that is responsible for:

  • Handling actions sent from the template (for example, the controller is responsible for deleting a todo if the user clicks the X button).

  • Present aggregate information about the todos to the template (for example, it is the controller’s responsibility to calculate the number of todos remaining).

Here’s the code for the controller:

App.TodosController = Ember.ArrayController.extend({
  newTodo: function(title, textField) {
    App.Todo.createRecord({
      content: title
    });

    textField.set('value', '');
  },

  destroyTodo: function(todo) {
    todo.deleteRecord();
  },

  destroyCompleted: function() {
    this.get('completed').invoke('deleteRecord');
  },

  remaining: function() {
    return this.get('length') - this.get('completedCount');
  }.property('length', 'completedCount'),

  completed: function() {
    return this.filterProperty('isDone');
  }.property('@each.isDone'),

  completedCount: function() {
    return this.get('completed.length');
  }.property('completed')
});

As you can see, there is not much JavaScript code here at all.

It implements three action handlers and three computed properties. In particular, it handles actions sent when:

  • Creating a new todo.

  • Destroying a todo.

  • Destroying all completed todos.

It also implements three computed properties:

remaining

The number of todos remaining.

completed

An array of todos that have been completed.

completedCount

The number of todos that have been completed.

Lastly, let’s switch back to index.html and implement the template that will render the user interface for our app.

Remember that Handlebars templates that you keep in your HTML file need to be wrapped in <script> tags, to prevent the browser from trying to treat them as HTML. Our <script> tag will look like this:

<script type="text/x-handlebars" data-template-name="todos">

</script>

This todos template will be the template for the entire application. If we were to add features to the app, we would probably add more templates to go along with this one. But for now, you can put all of the Handlebars markup inside this one <script> tag.

Here’s what the entire template looks like, all together:

<div id="todoapp">
  <div class="title">
    <h1>Todos</h1>
  </div>

  <div class="content">
    <div id="create-todo">
      {{view Ember.TextField id="new-todo" action="newTodo" 
        placeholder="What needs to be done?"}}
    </div>

    <div id="todos">
      <ul id="todo-list">
        {{#each todo in controller}}
        <li>
          <div {{bindAttr class=":todo todo.isDone:done"}}>
            <div class="display" title="Double click to edit...">
              {{view Ember.Checkbox class="check" checkedBinding="todo.isDone"}}
              <div class="todo-content">{{todo.content}}</div>
              <span class="todo-destroy" {{action destroyTodo todo}}></span>
            </div>
            <div class="edit">
              <input class="todo-input" type="text" value="${content}" />
            </div>
          </div>
         </li>
        {{/each}}
      </ul>
    </div>

    <div id="todo-stats">
      {{#if length}}
        <span class="todo-count">
          <span class="number">{{pluralize remaining}}</span>
        </span>
      {{/if}}
      {{#if length}}
        <span class="todo-clear">
          <a href="#" {{action destroyCompleted}}>
            Clear {{pluralize completedCount word="completed item"}}</span>
          </a>
        </span>
      {{/if}}
    </div>
  </div>
</div>

There’s a lot going on here, so let’s break it into pieces.

First, we want to create a text field that, when the user hits the enter key, sends an action to the controller telling it to create a new todo:

<div id="create-todo">
  {{view Ember.TextField id="new-todo" action="newTodo" 
    placeholder="What needs to be done?"}}
</div>

Next, we loop over all of the todos that the application knows about and create an <li> for each:

<ul id="todo-list">
  {{#each todo in controller}}
  <li>
    <!-- todo template here -->
   </li>
  {{/each}}
</ul>

Inside the {{#each}} block, we put the template that should be rendered once per todo.

<li>
  <div {{bindAttr class=":todo todo.isDone:done"}}>
    <div class="display" title="Double click to edit...">
      {{view Ember.Checkbox class="check" checkedBinding="todo.isDone"}}
      <div class="todo-content">{{todo.content}}</div>
      <span class="todo-destroy" {{action destroyTodo todo}}></span>
    </div>
    <div class="edit">
      <input class="todo-input" type="text" value="${content}" />
    </div>
  </div>
 </li>

First, we bind the containing <div>’s class attribute to a static value, as well as to the Todo model’s isDone property. If the model’s isDone is true, the done class will be added to the HTML element. Otherwise, it will be removed.