Defining Your Routes Edit Page
When your application starts, the router is responsible for displaying templates, loading data, and otherwise setting up application state. It does so by matching the current URL to the routes that you've defined.
1 2 3 4 |
App.Router.map(function() { this.route("about", { path: "/about" }); this.route("favorites", { path: "/favs" }); }); |
Now, when the user visits /about
, Ember.js will render the about
template. Visiting /favs
will render the favorites
template.
Note that you can leave off the path if it is the same as the route name. In this case, the following is equivalent to the above example:
1 2 3 4 |
App.Router.map(function() { this.route("about"); this.route("favorites", { path: "/favs" }); }); |
Inside your templates, you can use {{link-to}}
to navigate between
routes, using the name that you provided to the route
method (or, in
the case of /
, the name index
).
The {{link-to}}
helper will also add an active
class to the link that
points to the currently active route.
You can customize the behavior of a route by creating an Ember.Route
subclass. For example, to customize what happens when your user visits
/
, create an App.IndexRoute
:
1 2 3 4 5 6 |
App.IndexRoute = Ember.Route.extend({ setupController: function(controller) { // Set the IndexController's `title` controller.set('title', "My App"); } }); |
The IndexController
is the starting context for the index
template.
Now that you've set title
, you can use it in the template:
(If you don't explicitly define an App.IndexController
, Ember.js will
automatically generate one for you.)
Ember.js automatically figures out the names of the routes and controllers based on
the name you pass to this.route
.
URL | Route Name | Controller | Route | Template |
---|---|---|---|---|
/ |
index |
IndexController |
IndexRoute |
index |
/about |
about |
AboutController |
AboutRoute |
about |
/favs |
favorites |
FavoritesController |
FavoritesRoute |
favorites |
Resources
You can define groups of routes that work with a resource:
1 2 3 4 5 |
App.Router.map(function() { this.resource('posts', { path: '/posts' }, function() { this.route('new'); }); }); |
As with this.route
, you can leave off the path if it's the same as the
name of the route, so the following router is equivalent:
1 2 3 4 5 |
App.Router.map(function() { this.resource('posts', function() { this.route('new'); }); }); |
This router creates three routes:
URL | Route Name | Controller | Route | Template |
---|---|---|---|---|
/ |
index |
IndexController |
IndexRoute |
index |
N/A | posts 1 |
PostsController |
PostsRoute |
posts |
/posts |
posts.index |
PostsController ↳ PostsIndexController |
PostsRoute ↳ PostsIndexRoute |
posts ↳ posts/index |
/posts/new |
posts.new |
PostsController ↳ PostsNewController |
PostsRoute ↳ PostsNewRoute |
posts ↳ posts/new |
1 Transitioning to posts
or creating a link to
posts
is equivalent to transitioning to posts.index
or linking to
posts.index
NOTE: If you define a resource using this.resource
and do not supply
a function, then the implicit resource.index
route is not created. In
that case, /resource
will only use the ResourceRoute
, ResourceController
,
and resource
template.
Routes nested under a resource take the name of the resource plus their
name as their route name. If you want to transition to a route (either
via transitionTo
or {{#link-to}}
, make sure to use the full route
name (posts.new
, not new
).
Visiting /
renders the index
template, as you would expect.
Visiting /posts
is slightly different. It will first render the
posts
template. Then, it will render the posts/index
template into the
posts
template's outlet.
Finally, visiting /posts/new
will first render the posts
template,
then render the posts/new
template into its outlet.
NOTE: You should use this.resource
for URLs that represent a noun,
and this.route
for URLs that represent adjectives or verbs
modifying those nouns. For example, in the code sample above, when
specifying URLs for posts (a noun), the route was defined with
this.resource('posts')
. However, when defining the new
action
(a verb), the route was defined with this.route('new')
.
Dynamic Segments
One of the responsibilities of a resource's route handler is to convert a URL into a model.
For example, if we have the resource this.resource('posts');
, our
route handler might look like this:
1 2 3 4 5 |
App.PostsRoute = Ember.Route.extend({ model: function() { return this.store.find('posts'); } }); |
The posts
template will then receive a list of all available posts as
its context.
Because /posts
represents a fixed model, we don't need any
additional information to know what to retrieve. However, if we want a route
to represent a single post, we would not want to have to hardcode every
possible post into the router.
Enter dynamic segments.
A dynamic segment is a portion of a URL that starts with a :
and is
followed by an identifier.
1 2 3 4 5 6 7 8 9 10 |
App.Router.map(function() { this.resource('posts'); this.resource('post', { path: '/post/:post_id' }); }); App.PostRoute = Ember.Route.extend({ model: function(params) { return this.store.find('post', params.post_id); } }); |
Because this pattern is so common, the above model
hook is the
default behavior.
For example, if the dynamic segment is :post_id
, Ember.js is smart
enough to know that it should use the model App.Post
(with the ID
provided in the URL). Specifically, unless you override model
, the route will
return this.store.find('post', params.post_id)
automatically.
Not coincidentally, this is exactly what Ember Data expects. So if you use the Ember router with Ember Data, your dynamic segments will work as expected out of the box.
If your model does not use the id
property in the URL, you should
define a serialize method on your route:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
App.Router.map(function() { this.resource('post', {path: '/posts/:post_slug'}); }); App.PostRoute = Ember.Route.extend({ model: function(params) { // the server returns `{ slug: 'foo-post' }` return jQuery.getJSON("/posts/" + params.post_slug); }, serialize: function(model) { // this will make the URL `/posts/foo-post` return { post_slug: model.get('slug') }; } }); |
The default serialize
method inserts the model's id
into the route's
dynamic segment (in this case, :post_id
).
Nested Resources
You cannot nest routes, but you can nest resources:
1 2 3 4 5 6 7 8 |
App.Router.map(function() { this.resource('post', { path: '/post/:post_id' }, function() { this.route('edit'); this.resource('comments', function() { this.route('new'); }); }); }); |
This router creates five routes:
URL | Route Name | Controller | Route | Template |
---|---|---|---|---|
/ |
index |
App.IndexController |
App.IndexRoute |
index |
N/A | post |
App.PostController |
App.PostRoute |
post |
/post/:post_id2 |
post.index |
App.PostIndexController |
App.PostIndexRoute |
post/index |
/post/:post_id/edit |
post.edit |
App.PostEditController |
App.PostEditRoute |
post/edit |
N/A | comments |
App.CommentsController |
App.CommentsRoute |
comments |
/post/:post_id/comments |
comments.index |
App.CommentsIndexController |
App.CommentsIndexRoute |
comments/index |
/post/:post_id/comments/new |
comments.new |
App.CommentsNewController |
App.CommentsNewRoute |
comments/new |
2 :post_id
is the post's id. For a post with id = 1, the route will be:
/post/1
The comments
template will be rendered in the post
outlet.
All templates under comments
(comments/index
and comments/new
) will be rendered in the comments
outlet.
You are also able to create deeply nested resources in order to preserve the namespace on your routes:
1 2 3 4 5 6 7 |
App.Router.map(function() { this.resource('foo', function() { this.resource('foo.bar', { path: '/bar' }, function() { this.route('baz'); // This will be foo.bar.baz }); }); }); |
This router creates the following routes:
URL | Route Name | Controller | Route | Template |
---|---|---|---|---|
/ |
index |
App.IndexController |
App.IndexRoute |
index |
/foo |
foo.index |
App.FooIndexController |
App.FooIndexRoute |
foo/index |
/foo/bar |
foo.bar.index |
App.FooBarIndexController |
App.FooBarIndexRoute |
foo/bar/index |
/foo/bar/baz |
foo.bar.baz |
App.FooBarBazController |
App.FooBarBazRoute |
foo/bar/baz |
Initial routes
A few routes are immediately available within your application:
App.ApplicationRoute
is entered when your app first boots up. It renders theapplication
template.App.IndexRoute
is the default route, and will render theindex
template when the user visits/
(unless/
has been overridden by your own custom route).
Remember, these routes are part of every application, so you don't need to
specify them in App.Router.map
.