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.
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.
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.
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.
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.
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.