Github small
Interested in Ember training? I'll arrange a session if I get enough people.

Introduction

Welcome to my Ember Tutorial!

My intent with this guide is to provide enough instruction on how to use Ember that you could start writing your own app when you’re done. I have aimed for my writing to be simple, friendly and concise. I’m very interested in getting feedback and improving this tutorial so please don’t hesitate to send me feedback if you have any suggestions.

Important Notes on Ember CLI

This tutorial was written primarily in May/June of 2014. A lot has changed in Ember-land since then. The biggest change is that Ember CLI is now considered the “proper” platform for building your Ember app. Ember CLI has its own learning curve, but it is considered the way forward.

The Ember team is still committed to providing a standalone ember.js distribution that you can use without Ember CLI. So all you really need to run ember is ember.js, jquery, handlebars, and your own ember javascripts. You can throw them all together, point Ember to a back-end, and you’re up and running. That being said, if you’re starting from scratch, you should probably use Ember CLI, which works well with Rails via Ember-CLI-Rails.

The concepts and ideas in this tutorial are still valid, but it really should be ported to Ember-CLI. I’m pretty busy with other projects right now, so if you or someone you know would be interested in doing the port, please have them contact me. This tutorial is also on GitHub, so technically anyone can make a pull request.

As far as I know this tutorial is still the most complete tutorial that covers both the concepts and implementation of Ember, so almost everything you learn here will serve you well even if you are using Ember CLI. So if you’re down to learn Ember, you can sit back and enjoy the ride.

What is Ember JS

Ember is a front-end Javascript framework. You can use it to write complex, front-end heavy web apps. It gives you an organized structure, conventions, and built-in ways to do some of the hard things.

Like Angular, Backbone and Knockout, Ember arrived recently to help developers build great front-end applications while maintaining a clean code base.

Why Choose Ember

I can’t speak for every framework out there, but I can tell you what’s great about Ember.

Logical Code Organization. If you come from a Rails background you will appreciate Ember’s naming conventions. For example a Users Controller looks for a Users View and a Users Template.

Easy Persistence. Once you have your back-end communicating with Ember, saving a record as easy as calling user.save(). Want to delete this user? Call user.destroyRecord().

Auto-Updating Templates. Create a property and display it with {{ myProperty }} in your Handlebars template. Any updates will appear instantly. Handlebars might not be beautiful, but your templates can look pretty darn good if you use Emblem, a templating language for Handlebars that’s like Slim.

Helpful Object APIs. Ember implements its own set of objects, each of which comes with a really friendly API. For example Ember has an Array object with methods like contains, filterBy, and sortBy. These come in handy all the time.

I think Ember is absolutely worth learning if you find yourself needing to build complex front-end applications. Ember may seem big, but it’s really not that complicated. Once you learn how the various Ember objects interact and get a handle on the basics of the API you’ll be coding glorious front-end apps in no time.

Ember is Fun

My takeaway from working in Ember is that it’s really fun. It opens up new potential for developers to write the crazy front-end apps they’ve always dreamed of while still maintaining a clean, readable codebase. Also, the Ember core team does a great job of consistently releasing bug fixes and improvements, while keeping a stable codebase.

At the end of the day, I reckon that if you’re going to choose a front-end framework to learn, you can’t go wrong if you pick Ember.

Tutorial Requirements

This tutorial is designed for developers with basic knowledge of Javascript and Ruby on Rails. You should have a Rails development environment setup on your computer if you want to follow along.

There are both Javascript and CoffeeScript Versions

I originally wrote this tutorial in CoffeeScript because I use CoffeeScript when I write Ember. I think it results in cleaner code. If you’re going to do full-time development with Ember then I feel like you are better off with CoffeeScript.

However, I know that there are many people who don’t know or like CoffeeScript, so I went ahead and made a Javascript version too. Just use the toggle in the upper right of the page to switch languages.

As a heads up for CoffeeScript learners, JS2Coffee.org can convert any CoffeeScript code to Javascript and vice versa.

Testing

While I typically use TDD and don’t ship code without tests, I’m not covering testing in this tutorial for a couple of reasons.

First, there’s a lot to learn here as is, and throwing testing into the mix would make this tutorial just that much more complicated.

Second, you can write integration tests for Ember with Cucumber (or Rspec Integration), and this can be better than using Ember’s built in testing helpers because you can test persistence. However, I think you still should write unit tests for your Ember code. I recommend Ember Qunit for that. There’s also an Ember guide on testing that you can read after this tutorial.

If there’s a lot of demand for help with testing then I can write another section on how to do it, just let me know.

Questions & Mistakes

If you have a question or find a mistake then please email me at vic@vicramon.com or submit a pull request.

Hello World!

Let’s build a Hello World app first. We can ensure that we have our development environment setup and get a high level view of how Ember works.

Create a New Rails App

The app we’re going to build in this tutorial is a CRM. We can reuse this hello world code when we start the app, so let’s name our Rails app ember-crm.

First create new rvm gemset to sandbox our gems:

rvm gemset create ember-crm
rvm gemset use ember-crm
gem install rails

Now generate the Rails app:

rails new ember-crm -d postgresql
cd ember-crm
bundle
rake db:create

If you run rails s and visit localhost:3000 then you should see the Rails Welcome Aboard page.

Remove Turbolinks

You’ll need to remove Turbolinks because it conflicts with Ember. Make sure to remove it from all of the following places:

Add Ember Rails

I am going to use Ember Rails for this tutorial. It’s stable and works great. I think Ember CLI may replace Ember Rails as the default Rails/Ember integration gem, but it’s pre 1.0 right now so I’m going with Ember Rails for this tutorial.

Add following gems to your Gemfile:

gem 'ember-rails'
gem 'ember-source', '~> 1.8.1'
gem 'emblem-rails'

And bundle:

bundle

Ember Rails provides a generator that will create a skeleton for our Ember app. The -n flag tells it to name the Ember app App, which is the typical convention.

For Javascript:

rails g ember:bootstrap -n App --javascript-engine js

If you want the generated files to use CoffeeScript then add a flag:

rails g ember:bootstrap -n App --javascript-engine coffee

Ember Rails comes with default Ember versions, but let’s explicitly install Ember 1.5.0 and Ember Data 1.0.0 beta 7 so that your version is the same as mine. They will be installed to vendor/assets/ember.

rails g ember:install --tag=v1.5.0 --ember
rails g ember:install --tag=v1.0.0-beta.7 --ember-data

Add the following lines to your environment files. These tell Ember Rails which version of ember.js to use in each environment. The production version is minified and has no logging, while the development version is not minified and allows for logging.

# config/environments/test.rb
config.ember.variant = :development

# config/environments/development.rb
config.ember.variant = :development

# config/environments/production.rb
config.ember.variant = :production

Ember Rails generates an application.js.coffee for us, so lets use that. Delete application.js. The generated application.js.coffee requires jquery, but not jquery_ujs, so make sure to require it right below jquery:

# app/assets/javascripts/application.js.coffee
#= require jquery_ujs

Making Ember Work

We’ll need a basic Rails controller and view so that we can output something from Ember. I’m going to make a controller named HomeController with an index view and make it the root path.

Add the route:

# config/routes.rb
root to: 'home#index'

Create the controller:

# app/controllers/home_controller.rb
class HomeController < ApplicationController
end

Create an index view. It’s going to be blank:

<!-- app/views/home/index.html.erb -->

Last but not least, we need to create a template for Ember to render. Ember looks for an application template by default, so all we need to do is create it:

// app/assets/javascripts/templates/application.js.emblem
h1 Hello World
outlet

Restart your server then visit http://localhost:3000. You should see ‘Hello World’ printed on the screen. If you see it then congratulations! You’re one step closer to being an Embereño. Yes, Embereño is a thing, though I kind of like Emberista.

If you don’t see Hello World, you should clone my hello world repo and see what you’ve done differently.

Very Basic Debugging

If you open your console you should see output that looks like this:

DEBUG: ------------------------------- ember.js?body=1:3522
DEBUG: Ember      : 1.5.0 ember.js?body=1:3522
DEBUG: Handlebars : 1.3.0 ember.js?body=1:3522
DEBUG: jQuery     : 1.11.0 ember.js?body=1:3522
DEBUG: -------------------------------

If you don’t see this output then your Ember javascripts are not being loaded properly.

There’s a whole page on debugging Ember in the guides. I suggest that you check it out if you ever get stuck.

The first thing I usually do if things aren’t working is place a debugger in the code and open Chrome dev tools. If that doesn’t help then the next thing I’ll do is log my route transitions to get more insight:

# app/assets/javascripts/application.js.coffee
window.App = Ember.Application.create
  LOG_TRANSITIONS: true
  LOG_TRANSITIONS_INTERNAL: true
  LOG_VIEW_LOOKUPS: true
// app/assets/javascripts/application.js
window.App = Ember.Application.create({
  LOG_TRANSITIONS: true,
  LOG_TRANSITIONS_INTERNAL: true,
  LOG_VIEW_LOOKUPS: true
})

Beyond that I suggest reading in the guides for more detailed tips on debugging, and of course using the Ember Inspector…

The Ember Inspector

The Ember Inspector is an invaluable tool for debugging Ember. It’s a Chrome Extension that helps you see what’s going on in your app. You can get it here.

Once it’s installed refresh your browser and open Chrome dev tools. You should see a tab titled Ember. Inside are all sorts of helpful tools. View Tree will show you exactly what’s being rendered and where it came from. Routes show you all the routes in your app, and what other objects each one looks for. The route you are currently on will be bold. Data will show you all the active records in your app. You can click on a record to view all of its attributes.

I can’t say enough good things about the Ember Inspector. It makes the inner workings of your app very visible.

Conclusion

As you’ve seen it doesn’t take much time to get our Ember App up and running. Most of the work is in preparing the Rails app.

If you’ve still got issues getting this working then please post your issue in the comments below. This hello world app is also on GitHub if you want to look at it.

Ember Concepts

We could start coding right away, but I think that if you haven’t seen Ember-flavored javascript before then the code might not make much sense. So before we dive into building out the app I’m going to cover some core Ember concepts and a few of the Ember objects that you’ll be seeing.

If you are dying to code you can skip directly the Our App chapter, but if you want to know what’s going on then I recommend that you stick around.

The following few chapters will be a whirlwind tour of Ember Objects, Routing, Routes, Controllers, Views, and Templates. I’m only going to cover the basics so we can start coding as soon as possible. You can do a deep dive through the Ember Guides once you’ve completed this tutorial.

The Ember Object

Ember implements its own object system. The base object is Ember.Object. All of the other objects in Ember extend Ember.Object.

Most of the time you will be using an object that extends Ember.Object like Ember.Controller or Ember.View, but you can also use Ember.Object itself. One common use for Ember.Object is to create service objects that handle some specific logic.

If you open your browser’s console in your Hello World app you’ll be able to follow along with these commands, though you’ll need to convert the CoffeeScript to Javascript (or just click the toggle at the top of the page).

You can instantiate a basic object like this:

user = Ember.Object.create()
var user = Ember.Object.create();

Initialize it with properties by just passing them to create:

user = Ember.Object.create({ firstName: 'Sam', lastName: 'Smith' })
var user = Ember.Object.create({ firstName: 'Sam', lastName: 'Smith' });

You can get a property from the object by calling .get on it and passing the string name of the property:

user = Ember.Object.create({ firstName: 'Sam', lastName: 'Smith' })
user.get('firstName') is 'Sam' #=> true
user.get('lastName') is 'Smith' #=> true
var user = Ember.Object.create({ firstName: 'Sam', lastName: 'Smith' });
user.get('firstName') == 'Sam' //=> true
user.get('lastName') == 'Smith' //=> true

Inquire about the object with .toString(). In this case we see that it’s just Ember.Object.

user = Ember.Object.create();
user.toString() #=> <Ember.Object:ember{objectId}>
var user = Ember.Object.create();
user.toString() //=> <Ember.Object:ember{objectId}>

Defining Objects

So far we’ve just been using Ember.Object. You can create a “subclass” of Ember.Object by using extend. Say we want to make a user Object “class”:

App.User = Ember.Object.extend()
App.User = Ember.Object.extend();

Now you can instantiate a user with create:

user = App.User.create()
var user = App.User.create();

Note that I’m putting this User object inside App. Ember needs to place all of the app data inside a variable, and Ember devs typically use App. So when you define some kind of Object in ember you always want to have it on App.

Now what we’ve done is great and all, but we probably want to add things to our User object.

Objects can have three types of things inside them: properties, functions, and observers. I’ll cover each of these.

Properties

Here’s how you could define a property:

App.User = Ember.Object.extend
  isHuman: true
  temperature: 98.6
  favoriteDirector: 'Tarantino'
App.User = Ember.Object.extend({
  isHuman: true,
  temperature: 98.6,
  favoriteDirector: 'Tarantino'
})

isHuman, temperature, and favoriteDirector would now be accessible with .get.

user = App.User.create()
user.get('isHuman') #=> true
user.get('favoriteDirector') #=> "Tarantino"
user.get('temperature') #=> 98.6
var user = App.User.create();

user.get('isHuman'); //=> true
user.get('favoriteDirector'); //=> "Tarantino"
user.get('temperature'); //=> 98.6

These are the basic versions of Ember properties. We can also create computed properties that actually do some work and call other properites:

App.User = Ember.Object.extend

  fullName: ( ->
    @get('firstName') + ' ' + @get('lastName')
  ).property('firstName', 'lastName')
App.User = Ember.Object.extend({

  fullName: function() {
    return this.get('firstName') + ' ' + this.get('lastName')
  }.property('firstName', 'lastName')

})

Let’s dissect this.

First, we specify the property name with fullName:.

Second, we pass it a function that will return the value we want. We can get at other properties in our object by doing this.get('propertyName'), or in CoffeeScript, @get('propertyName').

Third, we call .property() on our function to tell Ember that it’s a computed property.

Finally, we have to tell property() which other properties this property depends on. It expects a list of the property names as strings. In this case, fullName should change any time firstName or lastName changes, so it needs to watch both of them.

Functions

Functions are quite simple:

App.User = Ember.Object.extend

  showMessage: (message) -> alert(message)

  showName: -> @showMessage(@get('fullName'))
App.User = Ember.Object.extend({

  showMessage: function(message) { alert(message) },

  showName: function() { this.showMessage(this.get('fullName')) }

})

Our showMessage function takes one argument: the message we want to alert. The showName function calls the showMessage function with the fullName of our user (assuming we’ve implemented the fullName property).

Here’s how you actually call a function:

user = App.User.create()
user.showMessage('it works')
var user = App.User.create()
user.showMessage('it works')

Observers

Observers are functions that fire whenever any of the things they observe change. They look like properties but they end with observes() instead of property()

App.User = Ember.Object.extend

  weightChanged: ( ->
    alert('yikes') if @get('weight') > 400
  ).observes('weight')
App.User = Ember.Object.extend({

  weightChanged: function() {
    if (this.get('weight') > 400) alert('yikes')
  }.observes('weight')

})

The above code would fire an alert saying “yikes” whenever the weight property on this user changes and is greater than 400. You can observe as many things as you’d like:

App.User = Ember.Object.extend

  bodyObserver: ( ->
    alert("You've changed. I feel like I don't even know you anymore.")
  ).observes('weight', 'height')
App.User = Ember.Object.extend({

  bodyObserver: function() {
    alert("You've changed. I feel like I don't even know you anymore.");
  }.observes('weight', 'height')

})

This would fire every time weight or height changed.

Extend Existing Objects

The Ember Object system is really great. It makes it easy to write modular, reusable code.

You can extend any existing object in your app like this:

App.Animal = Ember.Object.extend
  likesFood: true

App.Human = App.Animal.extend()

human = App.Human.create()

human.get('likesFood') #=> true
App.Animal = Ember.Object.extend({
  likesFood: true
})

App.Human = App.Animal.extend()

var human = App.Human.create()

human.get('likesFood') //=> true

Properties, functions, and observers in the child object will override those in the parent:

App.Animal = Ember.Object.extend
  likesFood: true

App.Bird = App.Animal.extend
  likesFood: false

bird = App.Bird.create()

bird.get('likesFood') #=> false
App.Animal = Ember.Object.extend({ likesFood: true })

App.Bird = App.Animal.extend({ likesFood: false })

var bird = App.Bird.create()

bird.get('likesFood') //=> false

Extending objects is a pattern you will use all the time while developing in Ember. You can use it to extract out common functionality or pull in functionality from some other object.

Init

All ember objects call an init function when they are first initialized. You can use this to do setup work.

App.Human = Ember.Object.extend

  init: -> alert("I think, therefore I am")
App.Human = Ember.Object.extend({

  init: function() { alert("I think, therefore I am"); }

})

This is fine with basic Ember Objects, but if you are using other, more specific Ember Objects like Route or Controller, then you should try to avoid using init and instead opt for other Ember conventions. If you must use it in one of these objects make sure that you call @_super() in the init function, otherwise you may break things.

This is fine with basic Ember Objects, but if you are using other, more specific Ember Objects like Route or Controller, then you should try to avoid using init and instead opt for other Ember conventions. If you must use it in one of these objects make sure that you call this._super() in the init function, otherwise you may break things.

Reopening Objects

You can go back and add more properties, functions, and observers by calling reopen on the object:

App.Human = Ember.Object.extend()

App.Human.reopen
  name: 'Señor Bacon'

francis = App.Human.create()
francis.get('name') #=> 'Señor Bacon'
App.Human = Ember.Object.extend();

App.Human.reopen({ name: 'Señor Bacon' });

var francis = App.Human.create();
francis.get('name') //=> 'Señor Bacon'

As you’ve seen, base objects can function like classes do in other languages. You can even define a sort of class method on objects by calling reopenClass:

App.Human = Ember.Object.extend()

App.Human.reopenClass
  sayUncle: -> alert("uncle")
App.Human = Ember.Object.extend();

App.Human.reopenClass({
  sayUncle: function() { alert("uncle"); }
})

Then call the class method on the object definition itself:

App.Human.sayUncle()
App.Human.sayUncle();

Ember Objects provide us with the ability to write object-oriented javascript. This is one of Ember’s best features.

Routing in Ember

Everything in Ember starts with routes. If you’re familiar with routing systems in other frameworks then I don’t think Ember’s will cause you much trouble.

Note: do not add the following code to your app – the code in the next few chapters is here just to show you how things work.

Location APIs

First, let’s cover the mechanics of routing in Ember. By default Ember uses the hashchange event in the browser to know when you’ve changed routes. It implements its own HashLocation object to handle this.

With HashLocation, an Ember route will be visible after the # in your url. For example, your routes might look like:

http://myemberapp.com/#/

http://myemberapp.com/#/about

http://myemberapp.com/#/users/1

http://myemberapp.com/#/users/1/edit

You may not want to serve your Ember app directly from your root url. In this case, just tell Ember what the root url should be:

# app/assets/javascripts/router.js.coffee
App.Router.reopen
  rootURL: '/some/path/'
// app/assets/javascripts/router.js
App.Router.reopen({
  rootURL: '/some/path/'
})

Now your users index route would look like this:

http://myemberapp.com/some/path/#/users

Some of you may think that hashes look ugly. There’s a solution to that! Ember also implements a HistoryLocation class which will handle routes by using your browser’s history API.

Here’s how to use HistoryLocation instead of HashLocation:

# app/assets/javascripts/router.js.coffee
App.Router.reopen
  location: 'history'
// app/assets/javascripts/router.js
App.Router.reopen({
  location: 'history'
})

Boom, it’s that simple.

Not all browsers implement the history API. Luckily Ember comes to the rescue again with AutoLocation, which will use HistoryLocation if the user’s browser supports it, otherwise it will use HashLocation.

# app/assets/javascripts/router.js.coffee
App.Router.reopen
  location: 'auto'
// app/assets/javascripts/router.js
App.Router.reopen({
  location: 'auto'
})

Both HashLocation and HistoryLocation implement Ember’s Location API. You could write your own Location class if you wanted to use something other than hashes or history, it would just need to respond properly to the API.

Writing Routes

A set of CRUD routes might look like this:

# app/assets/javascripts/router.js.coffee
App.Router.map ->
  @resource 'users'
  @route 'user.new', path: '/users/new'
  @resource 'user', path: '/users/:id', ->
    @route 'edit'
// app/assets/javascripts/router.js
App.Router.map(function() {
  this.resource('users');
  this.route('user.new', { path: '/users/new' });
  this.resource('user', { path: '/users/:id' }, function () {
    this.route('edit');
  })
})

This would generate the following routes:

/users (index)

/users/new (new)

/users/:id (show)

/users/:id/edit (edit)

Delete and create would be handled by custom actions so they don’t need to be routes.

There are two functions in use here: resource and route. The difference between them is important.

You can use resource to take in a param in order to get a specific record. You can nest things under resource.

You use route to specify some new UI that doesn’t need a specific record. route is a dead end – you cannot nest things under it. It can not take in params.

The . that you see is simply an alternative to using camel case. user.new could just as well be userNew. Both of these will look for a series of objects who’s names start with UserNew.

Nested Routes Means Nested UI

In Ember, the UI for any active route will be visible by default. Take for example the following routes:

# app/assets/javascripts/router.js.coffee
App.Router.map ->
  @resource 'posts', path: '/posts', ->
    @route 'new', path: '/new'
// app/assets/javascripts/router.js
App.Router.map(function() {
  this.resource('posts', { path: '/posts' }, function() {
    this.route('new', { path: '/new' });
  })
})

When you visit http://localhost:3000/posts/new, you will see both posts template and the posts/new template. The posts template will need an outlet tag inside itself to specify where posts/new will appear.

This is very different from server-side development where every route can have totally different UI. If you see a route in Ember in the url bar, that means that it is active and its UI should be visible. This is a feature. It allows you to compartmentalize UI that builds on top of other UI, so this pattern makes a lot of sense for the front-end.

See Your Routes

I recommend playing around with the router in your hello world app. It’s located in app/assets/javascripts/router.js. Once you’ve added routes you can refresh your page and look at the Ember inspector to see what routes Ember has generated. The Ember Inspector will also show you which Route, Controller, View, and Template that route will look for. This is extremely useful for making sure your object names match up.

Conclusion

If you’re interested in learning more about routes I recommend the Ember docs section on routing. There are also two tables in the Defining your Routes guide that really illuminate how you can use route and resource to create you your ideal routes.

In the next chapter we’ll go over what the route actually does when you hit it.

Ember Object Flow

(Again, you don’t need to add the following code to your app. You can add it just to play with it, but then delete it.)

Let’s say we have a route called about.

# app/assets/javascripts/router.js.coffee
App.Router.map ->
  @route 'about'
// app/assets/javascripts/router.js
App.Router.map(function() {
  this.route('about');
})

What actually happens when you go to it? It will instantiate a series of objects with the same name as the route… kind of like Rails!

When a route is activated Ember flows downwards from Route, to Controller, to View, to Template. In this case our about route will look for the following in this order: AboutRoute, AboutController, AboutView, and a template named about.js.emblem (or about.hbs if you’re using Handlebars).

When it finds each object it will call specific functions, or hooks on that object. I’m going to cover those hooks later, so for now I’m just going to place a console log in init so you can see that these objects are instantiated.

When you go to the about route, which would be http://localhost:3000/#/about, all of these console logs will get called, in this order:

# app/assets/javascripts/routes/about.js.coffee
App.AboutRoute = Ember.Route.extend
  init: ->
    @_super()
    console.log 'route called'

# app/assets/javascripts/controllers/about.js.coffee
App.AboutController = Ember.Controller.extend
  init: ->
    @_super()
    console.log 'controller called'


# app/assets/javascripts/views/about.js.coffee
App.AboutView = Ember.View.extend
  init: ->
    @_super()
    console.log "view called"

# app/assets/javascripts/templates/about.js.emblem
h1 Template rendered!
// app/assets/javascripts/routes/about.js
App.AboutRoute = Ember.Route.extend({
  init: function() {
    this._super();
    console.log('route called');
  }
})

// app/assets/javascripts/controllers/about.js
App.AboutController = Ember.Controller.extend({
  init: function() {
    this._super();
    console.log('controller called');
  }
})


// app/assets/javascripts/views/about.js
App.AboutView = Ember.View.extend({
  init: function() {
    this._super();
    console.log('view called');
  }
})

// app/assets/javascripts/templates/about.js.emblem
h1 Template rendered!

Ember actually won’t complain if it can’t find any of these objects. Instead it will just create them for you in memory. So if you don’t need to do anything in the AboutController, AboutView, or AboutRoute then just don’t create them.

This is what I call the Ember Object Flow. When a route is activated it flows downwards to its associated objects.

Now that you understand the flow of objects in the Ember system I’m going to provide brief overview of each one.

Ember Route

The first object we’ll be covering is the Ember Route object. This is different from the Router. The Router creates named url routes. A Route object is a specific type of Ember object that helps you setup and manage what happens when you visit that url route.

Let’s say we want to show a list of users. First we would add a line to the router:

App.Router.map ->
  @resource 'users'
App.Router.map(function() {
  this.resource('users');
})

Now when you visit /users, Ember will look for a UsersRoute object. Here’s how that could look:

App.UsersRoute = Ember.Route.extend

  model: -> @store.find 'user'
App.UsersRoute = Ember.Route.extend({

  model: function() { this.store.find('user') }

})

model is a function hook that’s called upon entering a route. The result of the model function is then accessible by other objects.

The store is an Ember data construct that you go through when dealing with persisted records. find fetches records of the type you pass it. It returns a promise, which in this case will return a DS.RecordArray object once you call then on it. DS.RecordArray is essentially an array of models.

Route Hooks

Route objects are really all about using hooks to prepare data and perform any setup actions required for the controller.

model is just one of a series of hooks that are called when you enter a route. Here are a few more that you’ll get to know, listed in order of when they are called.

App.UsersRoute = Ember.Route.extend

  beforeModel: (transition) ->

  model: (params, transition) ->

  afterModel: (model, transition) ->

  activate: ->

  setupController: (controller, model) ->
    controller.set 'model', model
    #or @_super(arguments...)

  deactivate: ->
App.UsersRoute = Ember.Route.extend({

  beforeModel: function(transition) { },

  model: function(params, transition) { },

  afterModel: function(model, transition) { },

  activate: function() { },

  setupController: function(controller, model) {
    controller.set('model', model)
    // or this._super(arguments...)
  },

  deactivate: function() { }

})

beforeModel is called immediately before model is called.

model we just went over.

afterModel is called after the model is resolved (i.e. either pulled down from the server or pulled out of the store).

activate is called after all of the model hooks have completed, meaning that the route is now active.

setupController is where you would do any controller setup. You get access to the controller itself as an argument. Note that if you implement setupController you will need to set the model property of the controller to the model argument, because this hook overrides the parent. If you don’t do this then the controller will not have its model property set. You could also call @_super(arguments...) to accomplish the same thing.

deactivate is called when you exit the route. It will not get called if you just change the model but stay on the same route. For example deactivate would not get called if you changed from /users/1 to /users/2

The Transition Object

The transition argument being passed into the route model hooks can be used to abort the transition. For example, say we have a HouseRoute and you don’t like red houses:

App.HouseRoute = Ember.Route.extend

  afterModel: (model, transition) ->
    transition.abort() if model.get('color') is 'red'
App.HouseRoute = Ember.Route.extend({

  afterModel: function(model, transition) {
    if (model.get('color') == 'red') {  transition.abort() }
  }

})

Here I’m using afterModel, because the model is resolved and I can ask for its color property. If you abort, Ember will just go back to whatever route you came from. This can be handy for error checking, confirmation dialogs, and locking certain parts of your app depending on state.

Grabbing Other Objects

Routes are the one place where you can reach across your app. Usually you do this to give the controller the information it needs.

this.modelFor('routeName') will return the current model for that route.

this.controllerFor('controllerName') will get that controller object. Sometimes you may want to get the model for that controller, in which case you would do this.controllerFor('controllerName').get('model').

Route objects are your friend. You’ll find that all of their hooks are extremely handy when you start trying to do fancy things in your app.

Ember Controller

Controllers handle non-persisted logic related to a specific piece of UI. They typically wrap a model or an array of models. You can put functions, properties, and observers on controllers. They function much like normal Ember Objects with a couple of exceptions.

First we’ll look at the three different types of controllers.

A Story of Three Controllers

Ember provides you with three types of controllers: ObjectController, ArrayController, and Controller. You use ObjectController whenever that controller’s route fetches a single model. Use ArrayController when the route fetches an array of models. And finally use Controller when the route isn’t fetching any models at all.

Properties, Observers, and Functions

Controllers don’t have any specific hooks that are called upon entering them. You can use init if you really need to, but you shouldn’t. All setup work for a controller should be done in the route, usually in the route’s setupController hook.

So what’s left? Properties, observers, and functions. These are your bread and butter.

# app/assets/javascripts/controllers/user.js.coffee
App.UserController = Ember.ObjectController.extend

  someFunction: -> alert('so functional')

  someProperty: ( ->
    if @get('model.firstName') is "Gregory"
      "Hey Gregory"
    else
      "Hey, you're not Gregory"
  ).property('model.firstName')

  someObserver: ( ->
    alert "You changed your name? I don't really see you as a #{@get('model.firstName')}."
  ).observes('model.firstName')
// app/assets/javascripts/controllers/user.js
App.UserController = Ember.ObjectController.extend({

  someFunction: function() { alert('so functional') },

  someProperty: function() {
    if (this.get('model.firstName') == "Gregory") {
      return "Hey Gregory"
    } else {
      return "Hey, you're not Gregory"
    }
  }.property('model.firstName'),

  someObserver: function() {
    alert("You changed your name? I don't really see you as a " + this.get('model.firstName'));
  }.observes('model.firstName')

})

You can put as many of these as you like in your controller. Any controller properties will be available to the template and view.

Mixins

If you find yourself duplicating a lot of logic you can extract your code to an Ember.Mixin and have your controller extend it like so:

# app/assets/javascripts/mixins/excited.js.coffee
App.Excited = Ember.Mixin.create
  levelOfExcitement: "I'm so freaking excited right now!!!"

# app/assets/javascripts/controllers/user.js.coffee
App.UserController = Ember.ObjectController.extend App.Excited,
  # your controller code
// app/assets/javascripts/mixins/excited.js
App.Excited = Ember.Mixin.create({
  levelOfExcitement: "I'm so freaking excited right now!!!"
})

// app/assets/javascripts/controllers/user.js
App.UserController = Ember.ObjectController.extend(App.Excited, {
  // your controller code
})

Now UserController would have the levelOfExcitement property. Also notice that mixins are created with .create rather than .extend.

Executing Template Actions

Another major use for controllers is handling actions from templates. For example, a template may have a submit button or a delete link. These actions would be handled like so:

# app/assets/javascripts/controllers/user.js.coffee
App.UserController = Ember.ObjectController.extend

  actions:

    deleteUser: -> @get('model').destroyRecord()

    saveChanges: -> @get('model').save()
// app/assets/javascripts/controllers/user.js
App.UserController = Ember.ObjectController.extend({

  actions: {

    deleteUser: function() { this.get('model').destroyRecord() },

    saveChanges: function() { this.get('model').save() }

    }
})

All action handlers must go inside an actions object in the controller. This is an Ember convention to help keep your code organized.

Ember View

The View is one of the most powerful objects in Ember. You can think of a view as a wrapper for a template. It contains all the javascript you might want to execute on the template and manages the logic around attributes and class names.

Talking to the Controller

The view can get the controller through this.get('controller'). The view does not have the current model by default, so to get the model you’ll have to do this.get('controller.model'). To get a property on the controller you would do this.get('controller.myProperty').

View Hooks

Like Routes, views have a series of hooks that you can use. Here are three of the more common ones you’ll use, in order of when they are called.

App.UserView = Ember.View.extend

  willInsertElement: ->

  didInsertElement: ->

  willDestroyElement: ->
App.UserView = Ember.View.extend({

  willInsertElement: function() {  },

  didInsertElement: function() {  },

  willDestroyElement: function() {  }
})

willInsertElement is called before the view is inserted into the DOM.

didInsertElement is called immediately after the view is inserted into the DOM. This is what you’ll use most often for running any template-specific javascript. It’s also helpful for debugging – you can place a debugger or console.log in didInsertElement to get more insight if things aren’t working.

willDestroyElement is called when view is about to be removed from the DOM. You can use this for any teardown you need to do.

Computed Aliases

This isn’t related specifically to views, but I’m about to use a computed alias so I need to explain what they are. Computed aliases are essentially shorthand for grabbing properties from other objects.

You just pass Em.computed.alias the string name of the property you want to look up. (Em. is an alias for Ember.). This will look up the property and watch it at the same time. For example:

App.MyController = Ember.Controller.extend
  name: 'Zelda'

App.MyView = Ember.View.extend

  # this is kind of lame
  name: ( ->
    @get('controller.name')
  ).property('controller.name')

  # instead, use a computed alias
  name: Em.computed.alias 'controller.name'
App.MyController = Ember.Controller.extend({
  name: 'Zelda'
})

App.MyView = Ember.View.extend({

  // this is kind of lame
  name: function() {
    this.get('controller.name')
  }.property('controller.name'),

  // instead, use a computed alias
  name: Em.computed.alias('controller.name')

})

Ember provides a whole variety of computed functions that let you create these kind of shorthand properties. For example, Em.computed.not returns the inverse of a boolean value. You can view the full list in the Ember API.

Defining the Element

When I said views wrap templates, I meant it quite literally. A view will by default wrap the template in a div with a generated ember id, like ember45.

We can customize this wrapping element very easily.

App.UserView = Ember.View.extend
  tagName: 'article'
  classNames: ['myClass', 'anotherClass']
App.UserView = Ember.View.extend({
  tagName: 'article',
  classNames: ['myClass', 'anotherClass']
})

tagName specifies the html element that the view uses.

classNames lets you add any css class names you want on the element.

If you really need to set an id on an element, you can use elementId: 'myId', but I recommend avoiding that if you can. Views are designed to be reused, so you may have more than one instance of the same view present on the page, and id’s are meant for single elements.

Class Bindings

Now comes the fancy stuff. What if you want to dynamically assign class names? Ember has got your back.

App.AnimalView = Ember.View.extend
  classNameBindings: ['active']

  active: Em.computed.alias 'controller.model.isActive'
App.AnimalView = Ember.View.extend({
  classNameBindings: ['active'],
  active: Em.computed.alias('controller.model.isActive')
})

classNameBindings will look for the property you named and execute it. In this case a class name of active will appear if the active property returns true, otherwise it won’t appear.

But what if you need more control than that? Try this:

App.AnimalView = Ember.View.extend
  classNameBindings: ['soundClass']

  soundClass: ( ->
    if @get('model.kind') is "cat"
      "meow"
    else
      "woof"
  ).property('model.kind')
App.AnimalView = Ember.View.extend({
  classNameBindings: ['soundClass']

  soundClass: function() {
    if (this.get('model.kind') == "cat") {
      return "meow"
    } else {
      return "woof"
    }
  }.property('model.kind')

In this case, since the return value is not a boolean, the return value will be used as the class name. If the return value is undefined then it won’t do anything. Here, a class of meow will be applied if the model is a cat, and woof if it’s a dog.

There’s actually an abbreviated version of this that you can use:

App.AnimalView = Ember.View.extend
  classNameBindings: ['isCat:meow:woof']

  isCat: Em.computed.equal 'model.kind', 'cat'
App.AnimalView = Ember.View.extend({
  classNameBindings: ['isCat:meow:woof'],
  isCat: Em.computed.equal('model.kind', 'cat')
})

The first value after the colon will be applied if the property returns true, otherwise the second value will be applied. Note that you can omit the second value if only want a class to be applied in the first case.

Attribute Bindings

Attribute bindings work similarly, though with some differences. Let’s say we’re trying to show an image, and assume that the model has an attribute called src:

App.ImageView = Ember.View.extend
  tagName: 'img'
  attributeBindings: ['src']

  src: Em.computed.alias 'controller.model.src'
App.ImageView = Ember.View.extend({
  tagName: 'img',
  attributeBindings: ['src'],
  src: Em.computed.alias('controller.model.src')
})

This is the simplest case of attribute bindings. Ember will create an attribute with the name of the property (src), and the value of the attribute will be the return value of the property.

There are often cases where you want the property to have a different name than the attribute. In this case just do propertyName:attributeName like so:

App.ImageView = Ember.View.extend
  tagName: 'img'
  attributeBindings: ['srcProperty:src']

  srcProperty: Em.computed.alias 'controller.model.src'
App.ImageView = Ember.View.extend({
  tagName: 'img',
  attributeBindings: ['srcProperty:src'],
  srcProperty: Em.computed.alias('controller.model.src')
})

Now there will be a src attribute that’s set to the result of srcProperty.

Specifying a Different Template

A UserView will look for a user template, but if you want to use a different one you can specify templateName:

App.UserView = Ember.View.extend
  templateName: 'someOtherTemplate'
App.UserView = Ember.View.extend({
  templateName: 'someOtherTemplate'
})

Getting the Current Element

Often times you’ll need the current element. It’s available to you as element, like this:

App.UserView = Ember.View.extend

  didInsertElement: ->
    console.log @get('element')
App.UserView = Ember.View.extend({

  didInsertElement: function() {
    console.log this.get('element')
  }

})

That would log the plain element.

Need to get the element and run some jQuery on it? You could do $(this.get('element')), but that’s pretty long. Ember gives you a shortcut: this.$().

App.UserView = Ember.View.extend

  didInsertElement: ->
    @$('.someClass').fadeOut()
App.UserView = Ember.View.extend({

  didInsertElement: function() {
    this.$('.someClass').fadeOut()
  }

})

This would look for .someClass inside your current view and fade it out.

Handling Events

UI events like click, doubleClick, and mouseEnter are accessible by the view. Just define a function with the event name and it will get called when the event occurs:

App.UserView = Ember.View.extend

  click: -> console.log 'clicked'
  mouseEnter: -> console.log 'mouse entered'
  mouseLeave: -> console.log 'mouse left'
App.UserView = Ember.View.extend({
  click: function() { console.log('clicked') },
  mouseEnter: function() { console.log('mouse entered') },
  mouseLeave: function() { console.log('mouse left') }
})

The event listeners will be applied to the entire view, so clicking anywhere inside it would trigger the click function.

A full list of view events can be found in the Ember docs.

That’s it for views. Next I’ll cover templates. It’s the last chapter before we start actually building stuff, so hang in there!

Ember Template

Ember templates are simply Handlebars files. I’m going to cover some of the basics of Handlebars and some of the unique Handlebars helpers that Ember provides.

In our app we’re going to use Emblem, which compiles to Handlebars. To help aid your learning I will show these examples in both Handlebars and Emblem.

Outputting Controller Properties

Templates have direct access to controller properties. Just throw ‘em in:

Handlebars:

Name: {{name}}

Emblem:

| Name:
name

Emblem expects the first word on each line to be either a controller property, a Handlebars helper, or an html tag, so if you want to immediately output plain text then use a pipe: |. Emblem parses everything after a pipe as a string.

Outputting View Properties

The template also has access to view properties, but you must prefix calls to them with view. For example:

Handlebars:

Name: {{view.someViewProperty}}

Emblem:

| Name:
view.someViewProperty

If, Else, Unless

Handlebars gives you if, else and unless. They only accept a single argument:

Handlebars:

{{#if isBirthday}}
  <div class="celebrate">Happy Birthday!</div>
{{else}}
  <div class="too_bad">Nope.</div>
{{/if}}

Emblem:

if isBirthday
  .celebrate Happy Birthday!
else
  .too_bad Nope.

There are no ands or ors allowed in Handlebars. It is designed to contain zero application logic. If you need some kind of combined boolean then you need to do it in the controller.

You can use nested if statements, but that gets ugly quickly.

The above example really shows why I prefer Emblem. That’s 112 characters for the Handlebars version and 57 for Emblem. That’s almost one half the code! Less code, as long as it’s readable, is always a win in my book.

Loops

Handlebars offers two ways to loop through things. The first gives you access to the current object as this:

Handlebars:

{{#each users}}
  {{this.name}}
{{/each}}

In Emblem the call to this is implicit. However, you still have access to this if you need it.

each users
  name

The second way to do loops is to name the object:

Handlebars:

{{#each user in users}}
  {{user.name}}
{{/each}}

Emblem:

each user in users
  user.name

If you are iterating over records from an ArrayController, which is extremely common, you just pass it controller:

Emblem:

each user in controller
  user.name

I think by now you may be getting the idea of Handlebars, so I’m going to switch to just showing Emblem.

Render, View, and Partial Helpers

Templates come with helpers that allow you to render another controller, view or template. This helps you reuse and compartmentalize logic.

The render helper calls a controller:

render 'user'

This will look for UserController and instantiate it. The controller will then look for UserView, and a user template, as per the usual Ember Object Flow.

You can optionally pass the render method a model object:

render 'user' model

The view helper calls a view:

view 'user'

This would look for UserView, which would then look for a template named user. Using the view helpers means that Ember will not instantiate a controller, it will skip it.

The partial helper only calls a template:

partial 'user'

This would render the user template inside the current template. It would not use the controller or view. Unlike Rails, Ember does not expect your template to be prefixed with an underscore.

As you can see, the render, view, and partial helpers enable you to compartmentalize code as much as you need. If you only need to show additional markup, just use partial. If you need markup with some javascript attached, use view. If you need markup that has access to its own set of properties and actions, then you’ll need to use render.

The Ember docs provide a great comparison table that helps explain how these helpers differ.

Actions

You can specify what are called actions on any element in a template. Actions will call a function in the controller of the same name:

h1 click="tickle" Tickle Me

This would call a tickle method in the controller when a user clicks on the h1. The method must be defined inside an actions object:

App.MyController = Ember.Controller.extend

  actions:
    tickle: -> alert('hahaha')
App.MyController = Ember.Controller.extend({

  actions: {
    tickle: function() { alert('hahaha') }
  }

})

The Link-To Helper

Ember provides a link-to helper that will transition you to a different route. You pass it the name of the route and any models that you need to send along.

Say you had an array controller that gave you a list of users, and you wanted to provide a link to each one. Here’s how you’d do that:

each userRecord in controller
  link-to 'user' userRecord
    userRecord.name

Ok, now that we’ve covered Routes, Controllers, Views and Templates, we can actually build something!

Our App

Now we’re actually going to code.

We are going to build a CRM. This is a good project for Ember because we’ll be searching, listing, showing, creating, and editing records. If you can build this app and understand how it works then you will be well on your way to developing your own complex front-end apps.

You can look at a completed version of the app to see what you’ll be building. The code is also on GitHub in case you need a reference: CoffeeScript Code, Javascript Code.

The main object in this system is the lead. If you don’t know the term, a lead is the same thing as a potential customer.

Preparing The App

You can use the Hello World app we built earlier as the base for this app. I’m going to provide you with styles so that you can just focus on the Ember stuff:

Download this stylesheet and save it to app/assets/stylesheets/application.css, replacing your existing application.css.

Vim Projections

If you use Vim, there’s a very useful plugin called vim-projectionist that provides some help in navigating files. Vim projections are great for quickly opening and creating Ember files.

Here’s are Gists with the projections that I use: Coffeescript Projections or Javascript Projections. Once you’ve got vim-projectionist installed, just copy that file into config/projections.json and restart Vim.

These projections use Ej as a prefix for all things Ember, so you have Ejmodel, Ejcontroller, Ejview, etc. Ejini will open your Ember router, which is consistent with Eini opening your Rails router.

If you don’t use Vim, consider learning it. It will probably improve your programming experience. You can open a terminal and type vimtutor to get an instant tutorial. You might also check out VimGenius, a little app I built to help you remember commands. Lastly, I wrote a Vim Tutorial that will help you get to an intermediate level.

Preparing Routes

Let’s use the AutoLocation API to get rid of hashes in our urls. We also need to set the rootURL to '/' so Ember knows where to start parsing the url from.

Open your router and add the following to the top:

# app/assets/javascripts/router.js.coffee
App.Router.reopen
  location: 'auto'
  rootURL: '/'
// app/assets/javascripts/router.js
App.Router.reopen({
  location: 'auto',
  rootURL: '/'
})

We also need to create a catch-all Rails route to handle whatever arbitrary routes we create in Ember, otherwise we’ll get a 404 when we try to reload the page on an Ember subroute:

# config/routes.rb

# add this to the bottom of your Rails routes
get '*path', to: 'home#index'

Creating the Rails API

We need a Rails API for Ember to communicate with in order to store and retrieve data.

The Active Model Adapter

When we used the Ember Rails generate command to setup Ember it added what’s called the Active Model Adapter. You’ll see this if you open the store file:

# app/assets/javascripts/store.js.coffee
App.Store = DS.Store.extend()
App.ApplicationAdapter = DS.ActiveModelAdapter.extend()
// app/assets/javascripts/store.js
App.Store = DS.Store.extend({});
App.ApplicationAdapter = DS.ActiveModelAdapter.extend({});

If you don’t see this code then replace whatever is in your store with this.

The Active Model Adapter enables Ember to communicate with your Rails backend through Active Model Serializers. Normally you would need to include the active_model_serializers gem, but Ember Rails already has it a as a dependency.

Namespace API Requests

We need to tell Ember to prepend all API requests with api/v1/, as we’ll be versioning our API. Add these two lines to the top of your store file:

# app/assets/javascripts/store.js.coffee
DS.RESTAdapter.reopen
  namespace: 'api/v1'
// app/assets/javascripts/store.js
DS.RESTAdapter.reopen({
  namespace: 'api/v1'
})

Modeling Leads in Rails

First let’s create our leads:

rails g migration create_leads first_name:string last_name:string email:string phone:string status:string notes:text

Open up the migration and make sure to add timestamps:

class CreateLeads < ActiveRecord::Migration
  def change
    create_table :leads do |t|
      t.string :first_name
      t.string :last_name
      t.string :email
      t.string :phone
      t.string :status
      t.text :notes

      t.timestamps
    end
  end
end

Run the migration:

rake db:migrate

Now create the Rails model:

# app/models/lead.rb
class Lead < ActiveRecord::Base
end

The Rails Serializer

Add the serializer. You need to list out all the attributes you want to serialize into JSON and send to Ember:

# app/serializers/lead_serializer.rb
class LeadSerializer < ActiveModel::Serializer
  attributes :id, :first_name, :last_name, :email, :phone, :status, :notes
end

The API Controller

Now that we have our model and serializer, we can create the API controller.

First we need routes for the API controller. Add them to the top of your Rails router:

# config/routes.rb
namespace :api do
  namespace :v1 do
    resources :leads
  end
end

Now create the controller. The actions here are fairly standard:

# app/controllers/api/v1/leads_controller.rb
class Api::V1::LeadsController < ApplicationController
  respond_to :json

  def index
    respond_with Lead.all
  end

  def show
    respond_with lead
  end

  def create
    respond_with :api, :v1, Lead.create(lead_params)
  end

  def update
    respond_with lead.update(lead_params)
  end

  def destroy
    respond_with lead.destroy
  end

  private

  def lead
    Lead.find(params[:id])
  end

  def lead_params
    params.require(:lead).permit(:first_name, :last_name, :email, :phone, :status, :notes)
  end

end

See it in Action

Let’s create some records and see our API actually work.

First add the ffaker gem:

# Gemfile
gem 'ffaker'

Then create a populate task:

# lib/tasks/populate.rake
namespace :db do
  task populate: :environment do

    Lead.destroy_all

    def random_status
      ['new', 'in progress', 'closed', 'bad'].sample
    end

    20.times do
      Lead.create(
        first_name: Faker::Name.first_name,
        last_name: Faker::Name.last_name,
        email: Faker::Internet.email,
        phone: Faker::PhoneNumber.phone_number,
        status: random_status,
        notes: Faker::HipsterIpsum.words(10).join(' ')
        )
    end

  end
end

I’m using Hipster Ipsum for the notes to spice things up.

Run the populate task:

rake db:populate

Restart the server, and now you should be able to visit http://localhost:3000/api/v1/leads.json and see the JSON output for all leads. http://localhost:3000/api/v1/leads/1.json should show you the first lead.

Puma

While we’re fiddling with Rails let’s switch out Webrick for Puma. It’s much faster and it’s multithreaded. All you have to do is add Puma to your Gemfile:

# Gemfile
gem 'puma'

Bundle and restart your server.

That’s it for the Rails side! Now we can get to the fun stuff.

Creating the Layout

We need to create our layout so we can build stuff inside it. Let’s do that now.

Rails Layout

Your Rails layout should look like this to start:

<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
  <head>
    <title>Ember CRM</title>
    <%= stylesheet_link_tag    'application'  %>
    <%= javascript_include_tag 'application' %>
    <%= csrf_meta_tags %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

Ember will insert itself at the end of your body tag by default. However, you can tell Ember to render into a specific element. Let’s do that:

First, create a div with the id ember-app.

<body>
  <%= yield %>
  <div id="ember-app"></div>
</body>

Now, open up application.js.coffee and tell Ember that the rootElement is ember-app:

Now, open up application.js and tell Ember that the rootElement is ember-app:

window.App = Ember.Application.create(rootElement: '#ember-app')
App = Ember.Application.create({rootElement: '#ember-app'})

Now Ember will render everything inside the ember-app div.

Rails View

The Rails view you created for Hello World should still exist but remain empty.

Ember Layout

Now we face an interesting question. You will face questions like this often as you build with Ember inside Rails. Should we build the layout in Ember or in Rails? This really depends.

If your Ember app is just on a single page, then it might make sense to build the layout in Rails so that it can be used on other non-Ember pages. But if your Ember app truly has multiple pages, and the layout links to these other Ember pages, then it makes sense to do it in Ember.

In our app we only have a single page with Ember, so we could build our layout in Rails and be just fine. However, this tutorial is all about learning Ember, so let’s do it in Ember anyway.

Ember always renders the application template, so we’ll use that for the layout:

// app/assets/javascripts/templates/application.js.emblem
header
  article
    .logo
      h1
        a href="#" Ember CRM

section#main
  = outlet

footer

Now if you refresh you should see the orange Ember CRM banner across the top. Next we’ll be dealing with getting and showing data.

Modeling Leads

To handle data we’ll be using Ember Data. We already installed it in Hello World so we don’t have do anything to start using it. Although there are a few different data adapters for Ember, Ember Data is the standard.

We have a Lead model in Rails but Ember needs to know about leads too. To do that we’ll create a lead model in Ember. Ember Data gives us the DS.Model object which we’ll extend:

# app/assets/javascripts/models/lead.js.coffee
App.Lead = DS.Model.extend
  firstName: DS.attr('string')
  lastName: DS.attr('string')
  email: DS.attr('string')
  phone: DS.attr('string')
  status: DS.attr('string', defaultValue: 'new')
  notes: DS.attr('string')
// app/assets/javascripts/models/lead.js
App.Lead = DS.Model.extend({
  firstName: DS.attr('string'),
  lastName: DS.attr('string'),
  email: DS.attr('string'),
  phone: DS.attr('string'),
  status: DS.attr('string', { defaultValue: 'new' }),
  notes: DS.attr('string'),
})

Ember will automatically read in the json api’s first_name to firstName, and etc. across the rest of our attributes. We’re only using the string data type here. The other ones available to you are number, boolean, and date.

An Aside about DS.Model

DS.Model gives you a variety of useful methods and properties. Here are a few methods that you will use often:

model.save() # save changes to the database
model.rollback() # wipe clean any unsaved changes
model.destroyRecord() # delete a record from the database
model.save() // save changes to the database
model.rollback() // wipe clean any unsaved changes
model.destroyRecord() // delete a record from the database

Note that DS.Model extends Ember.Object, so all of the things you learned about Ember Objects will still work with DS.Model. For example, you can set an arbitrary property on a DS.Model instance like so:

model.set('myProperty', 'hello!')
model.set('myProperty', 'hello!');

The main difference between DS.Model instances and regular Ember.Object instances is that you cannot create them the same way. To create a new DS.Model instance you have to go through the store:

@store.createRecord('modelName', firstName: 'John', lastName: 'Snow')
this.store.createRecord('modelName', { firstName: 'John', lastName: 'Snow' });

This is because the store encapsulates your app’s knowledge of all the active DS.Model instances, and the store has to do additional work when you create a model instance.

Listing Leads

The first thing we’ll do on the Ember side is list out all of our leads.

Add a Route

Everything always starts with routes. Open up your Ember Router and create a leads resource with the path set to root:

# app/assets/javascripts/router.js.coffee
App.Router.map ->
  @resource 'leads', path: '/'
// app/assets/javascripts/router.js
App.Router.map(function() {
  this.resource('leads', { path: '/' })
})

Create a Route Object

Next we need to fetch all lead records. Let’s create a LeadsRoute:

# app/assets/javascripts/routes/leads.js.coffee
App.LeadsRoute = Ember.Route.extend

  model: -> @store.find 'lead'
// app/assets/javascripts/routes/leads.js
App.LeadsRoute = Ember.Route.extend({
  model: function() { return this.store.find('lead') }
})

Remember that model is a hook that’s called whenever the route is entered. The result of the model function is then available to the controller, view, and template.

To be sure this is working properly, simply visit your root route and look at the “Data” tab in the Ember Inspector. You should see all of your leads.

Create the Template

Now that we have our leads we need to show them. Let’s create a template:

// app/assets/javascripts/templates/leads.js.emblem
article#leads
  h1 Leads
  ul
    each lead in controller
      li= lead.firstName

Now refresh the page and you should see your leads' listed on the left. Cool, right!?

Show Full Names

Let’s create a property to display the leads' full names. Open the lead model and add it:

# app/assets/javascripts/models/lead.js.coffee
fullName: ( ->
  @get('firstName') + ' ' + @get('lastName')
).property('firstName', 'lastName')
// app/assets/javascripts/models/lead.js
fullName: function() {
  return this.get('firstName') + ' ' + this.get('lastName')
}.property('firstName', 'lastName')

Then modify the template:

li= lead.fullName

Sorting Leads

Our leads are not in any particular order. This is chaos! Let’s sort them by name.

You can sort arrays of models by specifying sortProperties in the controller. We don’t have a controller yet, so let’s make one:

# app/assets/javascripts/controllers/leads.js.coffee
App.LeadsController = Ember.ArrayController.extend
  sortProperties: ['firstName', 'lastName']
// app/assets/javascripts/controllers/leads.js
App.LeadsController = Ember.ArrayController.extend({
  sortProperties: ['firstName', 'lastName']
})

sortProperties takes an array of strings. These strings are the properties you want to sort by with the highest priority first.

Note that I’ve made this controller an ArrayController. If you remember from the Controllers chapter, this is because the controller wraps an array of leads. Ember expects you to do this because ArrayController defines certain things like sortProperties that are not available on regular Controller instances.

Refresh the page and marvel at the beauty of your sorted leads.

Next we’ll show each lead’s data when when we click on it.

Showing a Lead

When we click on a lead we should see it appear on the right. Let’s do it.

Add a Resource

First we’ll need to add a resource to show a specific lead. Since we want the list of leads to still remain present on the page when we show a lead, this resource should be nested under the leads resource.

# app/assets/javascripts/router.js.coffee
App.Router.map ->
  @resource 'leads', path: '/', ->
    @resource 'lead', path: '/leads/:id'
// app/assets/javascripts/router.js
App.Router.map(function() {
  this.resource('leads', { path: '/' }, function() {
    this.resource('lead', { path: '/leads/:id' });
  })
})

Make sure to add , -> after the leads resource.

Create a Route Object

We need a Route Object to pull down our specific lead.

# app/assets/javascripts/routes/lead.js.coffee
App.LeadRoute = Ember.Route.extend

  model: (params) -> @store.find 'lead', params.id
// app/assets/javascripts/routes/lead.js
App.LeadRoute = Ember.Route.extend({

  model: function(params) { return this.store.find('lead', params.id) }

})

We have access to id through the params argument.

The this.store.find function gets a record by its id. It actually returns a promise which Ember will attempt to resolve.

Create the Template

Our template will show the information about our lead.

// app/assets/javascripts/templates/lead.js.emblem
article#lead
  h1= model.fullName

  p
    ' Name:
    model.fullName

  p
    ' Email:
    model.email

  p
    ' Phone:
    model.phone

The single quote ' leaves a trailing whitespace after the line.

Link to Each Lead

Open your leads template so you can create a link to each lead:

// app/assets/javascripts/templates/leads.js.emblem
article#leads
  h1 Leads
  ul
    each lead in controller
      link-to 'lead' lead tagName="li"
        lead.fullName

outlet

Two things are happening here.

First, our li became a link-to. We passed it tagName="li" so that the html element will be an li. You can set any property on a view this same way, propertyName="value", when you are using the view helper.

Second, we’ve placed an outlet tag at the end of the template. Since the lead route is nested under leads, its content will appear in this outlet. In this case the markup is on the same level in the DOM so this doesn’t present a problem. If you had a situation where you needed to output the content elsewhere on the page you could use the renderTemplate method in the route to specify an outlet somewhere else.

One nice thing about the link-to helper is that it automatically adds a class of active to the element when you are on the route it’s linking to. Our stylesheet takes advantage of this to highlight the currently selected lead.

Now refresh the page and click on a lead. You should see that lead’s information show up on the right, and it should be snappy.

Next we’ll create form elements and save data.

Editing a Lead Part I

There are two places to edit a lead in the app. We have status and notes which are immediately visible on lead show, and we can also click an “edit” link that will let us edit the lead’s basic info like name, email, and phone.

Let’s start with the status and notes. These will be additions to our existing lead template.

Add the Status Select

First we need to tell the app about the different statuses a lead can have. The Lead model should know about this, but it’s more class knowledge than instance knowledge, so lets use reopenClass to add it to the class.

Add this code after your Lead model in the same file:

# app/assets/javascripts/models/lead.js.coffee
App.Lead.reopenClass
  STATUSES: ['new', 'in progress', 'closed', 'bad']
// app/assets/javascripts/models/lead.js
App.Lead.reopenClass({
  STATUSES: ['new', 'in progress', 'closed', 'bad']
});

You will now be able to get to this array through App.Lead.STATUSES.

Open the lead template and add the following to the bottom, in line with the other p tags:

// app/assets/javascripts/templates/lead.js.emblem
p
  label Status:
  '
  view Ember.Select content=App.Lead.STATUSES value=model.status

Ember provides a built in select view which we can use. We point content to our status array, and we point value to our lead’s status. Now this select will be automatically bound to the lead model’s status.

Add the Notes

Add this code after the status select:

// app/assets/javascripts/templates/lead.js.emblem
p
  label Notes:
  br
  view Ember.TextArea value=model.notes

Here we use Ember’s text area view and bind it to model.notes.

Add a Submit Button

Add a submit button below the notes field:

// app/assets/javascripts/templates/lead.js.emblem
p
  input type='submit' value='Save Changes' click='saveChanges'

The click attribute specifies a function to call when you click this button. Ember expects the controller to define this function.

Save the Record

Now everything in our form is wired up except the submit button.

Since we’re in the middle of the lead object flow, Create a LeadController to handle the saveChanges click action:

# app/assets/javascripts/controllers/lead.js.coffee
App.LeadController = Ember.ObjectController.extend

  actions:
    saveChanges: -> @get('model').save()
// app/assets/javascripts/controllers/lead.js
App.LeadController = Ember.ObjectController.extend({

  actions: {
    saveChanges: function() {
      this.get('model').save();
    }
  }

});

Note that this controller is an ObjectController because it wraps a single lead model.

Our submit button will call the saveChanges function. This function must be within actions since it is being called from a template action. This is an Ember convention to help keep your code organized.

save() will actually send an ajax put request to our Rails API, and everything should just work. Try it out in your browser. Edit a record, click “Save Changes”, and look at the Network tab in the Chrome console. You should see a put request to api/v1/leads/(id of record). If you refresh the page the saved record should show your changes.

Note: If you get a 422 error stating “Invalid Authenticity Token” then check to make sure that you’ve required jquery_ujs in your application javascript.

Saving is that easy! Just bind and call save(). It’s fun and it’s healthy.

Ember will always send an API request on save, even if the record isn’t dirty. You can prevent this behavior with a simple if:

saveChanges: -> @get('model').save() if @get('model.isDirty')
saveChanges: function() {
  if (this.get('model.isDirty')) this.get('model').save();
}

Show Helpful Feedback

Now we’re going to get fancy and give the user some nice feedback around saving. When there are unsaved changes we will show a message saying “unsaved changes”, and when the record is actually saving we’ll show “saving…”.

Add these messages immediately below the submit button:

// app/assets/javascripts/templates/lead.js.emblem
p
  input type='submit' value='Save Changes' click="saveChanges"
  if isDirty
    .unsaved unsaved changes
  if isSaving
    .saving saving...

Ember will look in the controller to find isDirty and isSaving. If the controller doesn’t find them it will look in the model. Both isSaving and isDirty come built-in with DS.Model, so we are good.

Try it out it in the browser. You should see “unsaved changes” appear in red next to the save button if you add anything to notes or change the status of a lead. Click “save changes” to see “saving…” in green. It will probably appear only briefly – since you are in development the server is going to be quick.

Prettifying the Feedback

We have a little problem here. “unsaved changes” is still visible while you are saving a record. You can see this more easily if you add sleep 1 to the top of your update action in the leads API controller. This is because the record remains dirty until saving is completed, as you would expect. Let’s do some work to hide this text while saving.

We’ll make a property called showUnsavedMessage. Replace isDirty with showUnsavedMessage in the template:

// app/assets/javascripts/templates/lead.js.emblem
if showUnsavedMessage
  .unsaved unsaved changes

Open the controller and add the property:

# app/assets/javascripts/controllers/lead.js.coffee
App.LeadController = Ember.ObjectController.extend

  showUnsavedMessage: ( ->
    @get('isDirty') and !@get('isSaving')
  ).property('isDirty', 'isSaving')
// app/assets/javascripts/controllers/lead.js
App.LeadController = Ember.ObjectController.extend({

  showUnsavedMessage: function() {
    return this.get('isDirty') && !this.get('isSaving')
  }.property('isDirty', 'isSaving'),

  // actions, etc...

})

Our new logic will return false when the record is saving, giving us the result we want.

That’s it for editing part one. Onwards and upwards!

Editing a Lead Part II

Now let’s finish the second half of editing. In this scenario a user clicks on the “edit” link and sees fields to edit the lead’s name, phone, and email.

This UI will need to replace the UI that shows the lead, so we’ll have to do some special work to handle that.

Add a Route

As always, add a route first. We will place an edit route under our existing lead resource. The lead resource handles fetching the lead. We shouldn’t repeat that work if we don’t need to.

# app/assets/javascripts/router.js.coffee
@resource 'lead', path: 'leads/:id', ->
  @route 'edit'
// app/assets/javascripts/router.js
this.resource('lead', { path: 'leads/:id' }, function() {
  this.route('edit')
})

Make sure to add , -> after the lead resource.

This route is going to look for a LeadEdit controller, view, and template.

Create the Template

I’m going to add the template first because it will inform us about what actions we need to handle in the controller.

Since this route is nested inside a resource, Ember expects the template to be inside a subdirectory with the name of the resource. So this template will be app/assets/javascripts/templates/lead/edit.js.emblem.

If you’re not sure where to place a template just look in the Ember Inspector’s “Routes” tab.

// app/assets/javascripts/templates/lead/edit.js.emblem
article#lead
  h1
    model.fullName

  form
    fieldset
      dl
        dt: label First Name:
        dd: view Ember.TextField value=model.firstName

      dl
        dt: label Last Name:
        dd: view Ember.TextField value=model.lastName

      dl
        dt: label Email:
        dd: view Ember.TextField value=model.email

      dl
        dt: label Phone:
        dd: view Ember.TextField value=model.phone

    fieldset.actions
      input type='submit' value='Save Changes' click="saveChanges"
      a.cancel href="#" click="cancel" cancel

You can use a colon : in Emblem to nest elements on the same line.

Ember gives you the Ember.TextField view which renders a text input. Just assign value to the property you want to bind to.

The form tag doesn’t actually do anything, it’s just there for markup.

This template has two actions: saveChanges and cancel. Let’s implement them now.

Create the Controller

# app/assets/javascripts/controllers/lead_edit.js.coffee
App.LeadEditController = Ember.ObjectController.extend

  actions:

    saveChanges: ->
      @get('model').save().then =>
        @transitionToRoute 'lead'

    cancel: ->
      @get('model').rollback()
      @transitionToRoute 'lead'
// app/assets/javascripts/controllers/lead_edit.js
App.LeadEditController = Ember.ObjectController.extend({

  actions: {

    saveChanges: function() {
      var self = this;
      this.get('model').save().then(function() {
        self.transitionToRoute('lead');
      })
    },

    cancel: function() {
      this.get('model').rollback();
      this.transitionToRoute('lead');
    }

  }

})

This part is fairly simple, though you might not be familiar with .then. save() returns a Promise object, which we can call .then on to execute code when the promise is resolved. Basically what this means is that transitionToRoute won’t be called until the server has confirmed that the model was saved.

Add the Edit Link

We need a way to get to our new route. Add a link inside the h1 tag in the lead template. Don’t worry, the stylesheet will make it look pretty.

// app/assets/javascripts/templates/lead.js.emblem
h1
  model.fullName
  link-to 'edit' 'lead.edit' model classNames='edit'

The first argument, 'edit', is the link text. The second, 'lead.edit', is the route name.

Try It

Open the browser and try clicking the edit link. You should see the URL change, but nothing else should happen. Can you figure out why?

Outlets, don’t forget about them. If a template doesn’t appear, always ask yourself: did I add an outlet?! If you’re like me, you’ll forget one at some point and be super annoyed that your template isn’t showing up.

Our edit route is nested under lead so Ember is trying to render the template into an outlet tag inside the lead template. Since it can’t find it, nothing happens.

Add an outlet to the top of the lead template:

// app/assets/javascripts/templates/lead.js.emblem
outlet

Now try it. It should work, but now we have a new problem: the show UI for a lead is still present. That’s because nested routes means nested UI. Since the lead resource is still active, the UI is still active.

There’s a simple fix to this – we’ll just hide the show UI when we’re editing.

I Heard You Like Editing

Now we’ll set an isEditing property on the lead controller to hide the UI we don’t want to see when it’s true.

First add unless isEditing to the template and indent all the show UI under it:

// app/assets/javascripts/templates/lead.js.emblem
outlet

unless isEditing
  article#lead
    h1
      fullName
      link-to 'edit' 'lead.edit' model classNames='edit'
  // etc...

Now whenever we visit the edit route we need to set isEditing to true. We can do that inside the LeadEdit route. We haven’t made one yet, so do it now:

# app/assets/javascripts/routes/lead_edit.js.coffee
App.LeadEditRoute = Ember.Route.extend

  activate:   -> @controllerFor('lead').set 'isEditing', true
  deactivate: -> @controllerFor('lead').set 'isEditing', false
// app/assets/javascripts/routes/lead_edit.js
App.LeadEditRoute = Ember.Route.extend({

  activate:   function() { this.controllerFor('lead').set('isEditing', true) },
  deactivate: function() { this.controllerFor('lead').set('isEditing', false) }

})

Now we see those route hooks coming in handy! On activate we get the LeadController and set isEditing to true. On deactivate we do the opposite. And boom, we’re done.

We could do one last thing for clarity – add isEditing to the LeadController and default it to false.

# app/assets/javascripts/controllers/lead.js.coffee
App.LeadController = Ember.ObjectController.extend

  isEditing: false

  #etc...
// app/assets/javascripts/controllers/lead.js
App.LeadController = Ember.ObjectController.extend({

  isEditing: false,

  // etc...

})

This way future programmers (or our future selves) will know that we have an isEditing property on this controller and it should be false by default. We don’t have to do this but I think it’s good style.

Now that we can edit everything about leads I’ll show you how to delete them.

Deleting a Lead

This may be the shortest chapter. Deleting in Ember is typically handled by an action.

Add a Delete Link

Open up the lead template and add a delete link immediately above the submit button:

// app/assets/javascripts/templates/lead.js.emblem
p
  a.delete href='#' click="delete" delete
  input type='submit' value='Save Changes' click="saveChanges"
  # etc ...

Create the Controller Action

Template actions are handled in the controller, so open up the LeadController and add a delete action:

# app/assets/javascripts/controllers/lead.js.coffee
actions:

  delete: ->
    @get('model').destroyRecord().then =>
      @transitionToRoute 'leads'
// app/assets/javascripts/controllers/lead.js
actions: {

  delete: function() {
    var self = this;
    this.get('model').destroyRecord().then(function() {
      self.transitionToRoute('leads');
    });
  }

}

We call destroyRecord() on the model, which sends a DELETE request to the server. After we delete the record we need to transition back to the leads route.

And that’s it!

Next I’ll cover adding a new lead.

Creating a Lead

Add the Route

By now you know the drill…

# app/assets/javascripts/router.js.coffee
@resource 'leads', path: '/', ->
  @route 'new'
// app/assets/javascripts/router.js
this.resource('leads', { path: '/' }, function() {
  this.route('new');
});

New lead will be a route and not a resource because it does not need to load an existing model in our system.

Setup the Fields

We’re going to create a fields property that will be a plain javascript object. We’ll use this to hold the attributes for the new lead until we’re ready to actually create it.

We set fields to an empty object in the route so that it’s reset every time we visit the new lead route.

# app/assets/javascripts/routes/leads_new.js.coffee
App.LeadsNewRoute = Ember.Route.extend

  setupController: (controller) ->
    controller.set 'fields', {}
// app/assets/javascripts/routes/leads_new.js
App.LeadsNewRoute = Ember.Route.extend({

  setupController: function(controller) {
    controller.set('fields', {})
  }

});

Create the Template

And here’s our new lead template. Note that it goes in templates/leads/ because it’s a route nested under a resource named leads.

// app/assets/javascripts/templates/leads/new.js.emblem
article#lead
  h1 New Lead

  form
    fieldset
      dl
        dt: label First Name:
        dd: view Ember.TextField value=fields.firstName

      dl
        dt: label Last Name:
        dd: view Ember.TextField value=fields.lastName

      dl
        dt: label Email:
        dd: view Ember.TextField value=fields.email

      dl
        dt: label Phone:
        dd: view Ember.TextField value=fields.phone

    fieldset.actions
      input type='submit' value='Create Lead' click="createLead"

As you can see, I’ve bound all of the inputs to the fields property.

The submit has a click action called createLead, which we’ll deal with now.

Handle the Action

Create a controller to handle the createLead action:

# app/assets/javascripts/controllers/leads_new.js.coffee
App.LeadsNewController = Ember.Controller.extend

  actions:

    createLead: ->
      lead = @store.createRecord 'lead', @get('fields')
      lead.save().then =>
        @transitionToRoute 'lead', lead
// app/assets/javascripts/controllers/leads_new.js
App.LeadsNewController = Ember.Controller.extend({

  actions: {
    createLead: function() {
      var self = this;
      var lead = this.store.createRecord('lead', this.get('fields'));
      lead.save().then(function() {
        self.transitionToRoute('lead', lead);
      });
    }
  }

});

This action first calls createRecord, which we pass the string name of the model and an object with the attributes we want to give the new model. Since we bound fields to all the attributes we can just use it as is. If you logged fields you would see something like { firstName: 'Sam', lastName: 'Smith', email: 'sam@example.com', phone: '123-456-7890' }.

Once the record is created we save it, then transition to the show lead route.

There’s another common pattern to create new records that I didn’t use. I could have created a new record in the route and bound the inputs to the record. The reason I didn’t do this is because I didn’t want the record to show up in our list of leads on the left until the user pressed “Create Lead”. By keeping the attributes in fields and waiting until createLead is called to create a record, the record won’t appear in the list until the user has chosen to create it.

Add a Link

Add a link to our new lead route in the leads template:

// app/assets/javascripts/templates/leads.js.emblem
article#leads
  h1
    | Leads
    link-to 'leads.new' | New Lead

Now refresh and try it. Everything should work, but it’s not perfect. You can create leads where every attribute is null. We should perform a validation here to prevent this.

Perform Validations

Validation libraries for Ember exist and if you want to use one I recommend Dockyard’s Ember Validations library. However, given Ember’s robust object system and the large amount of control it gives you over the client, you don’t necessarily need a library.

I’m going to do these validations by hand. There are any number of different ways to do validations, this is just one.

First let’s define a valid method on the Lead class. Let’s say we just care that a first and last name are present:

# app/assets/javascripts/models/lead.js.coffee
App.Lead.reopenClass

  valid: (fields) ->
    fields.firstName and fields.lastName
// app/assets/javascripts/models/lead.js
App.Lead.reopenClass({

  valid: function(fields) {
    return fields.firstName && fields.lastName
  }

});

We pass this method an object with the attributes we want to assign to a lead, and it tells us if this collection of attributes is valid or not.

Now we need to modify our createLead method in the new lead controller:

# app/assets/javascripts/controllers/leads_new.js.coffee
createLead: ->
  fields = @get('fields')
  if App.Lead.valid(fields)
    lead = @store.createRecord 'lead', fields
    lead.save().then (lead) =>
      @transitionToRoute 'lead', lead
  else
    @set 'showError', true
// app/assets/javascripts/controllers/leads_new.js
createLead: function() {

  var self = this;
  var fields = this.get('fields')

  if (App.Lead.valid(fields)) {
    var lead = this.store.createRecord('lead', fields)
    lead.save().then(function(lead) {
      self.transitionToRoute('lead', lead)
    });
  } else {
    this.set('showError', true)
  }
}

We check to see if these fields are valid. If they are, create the record. If they aren’t, set the showError property to true. Now we can use the showError property to display a message to the user:

// app/assets/javascripts/templates/leads/new.js.emblem
article#lead
  h1 New Lead

  if showError
    .error Leads must have a first and last name.

One last thing: controller instances remain active, so if you created this error, went to another route, then came back, the showError property would still be true. We don’t want that, so we need to default it to false in the route on setupController:

# app/assets/javascripts/routes/leads_new.js.coffee
setupController: (controller) ->
   # etc...
   controller.set 'showError', false
// app/assets/javascripts/routes/leads_new.js
setupController: function(controller) {
   // etc...
   controller.set('showError', false)
}

Now if you create the error, leave, then come back, the form should be fully reset.

That’s it for adding leads. This chapter feels a bit dry, but creating new records is probably something you’ll do a lot so it’s good to know.

The next chapter is more exciting: we’re going to instantly search leads.

Searching Leads

Let’s create a search box that will instantly search leads by name as we type.

The neat thing about this is that it can be accomplished with a surprisingly small amount of code in a very clean manner. That, my friend, is the beauty of Ember.

Add the Search Field

Add a text field view at the top of the list of leads, right under the h1:

// app/assets/javascripts/templates/leads.js.emblem
article#leads
  h1
    | Leads
    link-to 'leads.new' | New Lead
  view Ember.TextField value=search placeholder="search" classNames="search"
  ul
  # etc ...

I’m binding the text field’s value to a property named search.

Change the Loop

Right now we’re doing each lead in controller. We need to manually modify the list of leads, so change it to each lead in leads, and we’ll create a leads property on the controller.

// app/assets/javascripts/templates/leads.js.emblem
ul
  each lead in leads
  # etc ...

Filtering Leads

Open up LeadsController and add the following two properties:

# app/assets/javascripts/controllers/leads.js.coffee
leads: ( ->
  if @get('search') then @get('searchedLeads') else @
).property('search', 'searchedLeads')

searchedLeads: ( ->
  search = @get('search').toLowerCase()
  @filter (lead) => lead.get('fullName').toLowerCase().indexOf(search) != -1
).property('search', '@each.fullName')
// app/assets/javascripts/controllers/leads.js
leads: function() {
  return this.get('search') ? this.get('searchedLeads') : this
}.property('search', 'searchedLeads'),

searchedLeads: function() {
  var search = this.get('search').toLowerCase()
  return this.filter(function(lead) {
    return lead.get('fullName').toLowerCase().indexOf(search) != -1
  })
}.property('search', 'this.@each.fullName')

The leads property looks to see if there is a search string. If there is, it returns searchedLeads. If there isn’t, it returns this. this in an ArrayController references the array of models that it is wrapping.

searchedLeads gets the search string and lower cases it. It then runs filter on this, which is the list of leads, and returns the leads where the full name includes the search string.

searchedLeads needs to depend on `this.@each.fullName‘, which means that the property will be updated whenever the full name of any lead changes.

Try It

It should work right now. There are two cool things to notice here.

First, as you search the list of names they stay sorted by first and last name. That’s sortProperties in action.

Second, try clicking on a lead, then entering a search string that doesn’t match. Then, edit the lead’s name to something that matches and watch it appear in the search list. That’s pretty cool, and it’s a good example of how everything is properly bound together.

Deploying to Heroku

The world should see our masterpiece. If you use Git and Heroku then you can deploy your app very quickly.

First add the rails_12factor gem that Heroku likes you to have. Bundle and commit. Then run these commands:

heroku create <whatever-name-you-want>
git push heroku master
heroku run rake db:migrate db:populate
heroku open

And that’s it!

Conclusion

I hope that this tutorial provided a good introduction to the Ember framework and was maybe even fun along the way. Please don’t hesitate to let me know if anything is unclear or needs further explanation.

Where To Now?

If you want to keep learning I recommend reading through the Ember Guides and the Ember API Docs. They are very detailed and they do an excellent job explaining all the nuances of Ember.

Beyond that, I think building your own applications is the best way to learn.

Extending this Tutorial

I’m considering adding more to the tutorial to cover more Ember topics. If you’d like this then let me know. Also, if there’s anything in particular that you think would be cool to add then please tell me.

This tutorial is on GitHub, so you could even add a chapter yourself if you want to!

Thanks

Thanks for giving my tutorial a read, and best of luck on your Ember journey!