英文原文: http://emberjs.com/guides/routing/defining-your-routes/

定义路由(Defining Your Route)

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.

当启动你的应用时,路由器会负责展示模板,载入数据,以及设置应用状态等任务。 这些都是通过将当前的URL与你定义的路由进行匹配来实现的。

  1. App.Router.map(function() {
  2. this.route("about", { path: "/about" });
  3. this.route("favorites", { path: "/favs" });
  4. });

Now, when the user visits /about, Ember.js will render the about template. Visiting /favs will render the favorites template.

现在当用户访问/about时,Ember.js就会渲染about的模板。访问/favs将渲染favorites的模板。

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:

提示:如果路径(path)的名字跟路由(route)的名字是一样的话,你可以不用写上路径。 所以下面的示例跟上面的是相同的。

  1. App.Router.map(function() {
  2. this.route("about");
  3. this.route("favorites", { path: "/favs" });
  4. });

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

在模板里面,你可以用{{link-to}}来导向路由,这需要用到你在route方法中定义的名字 (对于/来说,名字就是index)。

  1. {{#link-to 'index'}}<img class="logo">{{/link-to}}
  2. <nav>
  3. {{#link-to 'about'}}About{{/link-to}}
  4. {{#link-to 'favorites'}}Favorites{{/link-to}}
  5. </nav>

The {{link-to}} helper will also add an active class to the link that points to the currently active route.

{{link-to}}助手会在链接上面加上active的类名(class)来指出当前活跃的路由。

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:

你也可以通过创建一个Ember.Route的子类来对路由的行为进行自定义。例如,创建 App.IndexRoute类来定义当用户访问/时会发生什么。

  1. App.IndexRoute = Ember.Route.extend({
  2. setupController: function(controller) {
  3. // Set the IndexController's `title`
  4. controller.set('title', "My App");
  5. }
  6. });

The IndexController is the starting context for the index template. Now that you’ve set title, you can use it in the template:

IndexControllerindex模板初始的上下文环境。如果你已经设置了title, 那么你可以在模板里面使用它。

  1. <!-- get the title from the IndexController -->
  2. <h1>{{title}}</h1>

(If you don’t explicitly define an App.IndexController, Ember.js will automatically generate one for you.)

(如果你没有显式的声明IndexControllerEmber.js会自动生成一个。)

Ember.js automatically figures out the names of the routes and controllers based on the name you pass to this.route.

Ember.js会自动地根据你在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. App.Router.map(function() {
  2. this.resource('posts', { path: '/posts' }, function() {
  3. this.route('new');
  4. });
  5. });

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:

this.route一样,如果路径名称跟路由名称相同,你可以忽略路径,所以下面的路由器跟上面是等效的:

  1. App.Router.map(function() {
  2. this.resource('posts', function() {
  3. this.route('new');
  4. });
  5. });

This router creates three routes: 这个路由器创建了三个路由:

URL Route Name Controller Route Template
/ index IndexController IndexRoute index
N/A posts1 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

1 跳转到posts或者链接到posts,等效于跳转到posts.index或链接到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.

注意:如果你通过this.resource定义了一个资源,但是没有提供一个函数作为参数, 那么隐式的resource.index不会被创建的。在这种情况下,/resource只会用到 ResourceRouteRescourceControllerresource模板。

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

一个资源下的嵌套路由的名字会是资源名加上路由名。如果你想跳转到一个路由(用transitionTo{{#link-to}}),请确保使用了完整的路由名(如:post.new,而不是new)。

Visiting / renders the index template, as you would expect.

正如你期望的一样,访问/会渲染index模板。

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.

访问/posts会有点不同。它会先渲染posts模板,然后再渲染posts/index模板到 post模板的出口(outlet)上。

Finally, visiting /posts/new will first render the posts template, then render the posts/new template into its outlet.

最后,访问/posts/new会先渲染posts模板,然后渲染posts/new模板到它的出口上。

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

注意:你应该使用this.resource来定义一个URL中的名词字段,而对于用来改变名词字段的形容词动词字段 ,使用this.route来定义。例如,在上例中的代码,当指定posts(名词)的URL时,路由被定义为this.resource('posts')。然而,当定义new操作(动词)时,那么路由被定义为this.route('new')

动态段(Dynamic Segments)

One of the responsibilities of a resource’s route handler is to convert a URL into a model.

在路由处理器的众多职责里,其中有一个就是转换URL并将其传入模型(model)中。

For example, if we have the resource this.resource('posts'), our route handler might look like this:

例如,如果我们有一个资源this.resource('posts'),那么我们的路由处理器看起来可能像这样:

  1. App.PostsRoute = Ember.Route.extend({
  2. model: function() {
  3. return this.store.find('post');
  4. }
  5. });

The posts template will then receive a list of all available posts as its context.

posts模板将会接收到一张所有可用的posts清单并将它们当做是上下文环境。

Because /posts represents a fixed model, we don’t need any additional information to know what to use. 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.

由于/posts映射到一个特定的模型上,所以我们不需要其他额外的信息就可以运行。然而,如果我们想要路由映射到某个post上,我们可不想通过在路由器中写死每一个可能的post来实现。

Enter dynamic segments.

探究动态段

A dynamic segment is a portion of a URL that starts with a : and is followed by an identifier.

一个动态段是URL的一部分,由一个:起始,后面加上一个标示符组成。

  1. App.Router.map(function() {
  2. this.resource('posts');
  3. this.resource('post', { path: '/post/:post_id' });
  4. });
  5. App.PostRoute = Ember.Route.extend({
  6. model: function(params) {
  7. return this.store.find('post', params.post_id);
  8. }
  9. });

Because this pattern is so common, the above model hook is the default behavior.

由于这种模式很常用,所以上面的模型(model)钩子函数就是默认的行为。

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.

例如,如果动态段是:post_idember.js会智能地使用App.post(加上URL提供的ID)。 特别地,如果你没有重写了模型(model),路由将会自动地返回this.store.find('post', params.post_id)

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.

这不是巧合,而是Ember Data所想要的。所以如果你使用Ember路由和Ember Data, 你的动态段将会以默认的方式工作。

If your model does not use the id property in the URL, you should define a serialize method on your route:

如果模型没有在URL中使用id属性,那么应该在路由中定义一个序列化方法:

  1. App.Router.map(function() {
  2. this.resource('post', {path: '/posts/:post_slug'});
  3. });
  4. App.PostRoute = Ember.Route.extend({
  5. model: function(params) {
  6. // the server returns `{ slug: 'foo-post' }`
  7. return jQuery.getJSON("/posts/" + params.post_slug);
  8. },
  9. serialize: function(model) {
  10. // this will make the URL `/posts/foo-post`
  11. return { post_slug: model.get('slug') };
  12. }
  13. });

The default serialize method inserts the model’s id into the route’s dynamic segment (in this case, :post_id).

缺省的serialize方法将模型的id插入到路由的动态段中(上述的:post_id)。

(嵌套资源)Nested Resources

You cannot nest routes, but you can nest resources:

你不能嵌套路由,但是你可以嵌套资源:

  1. App.Router.map(function() {
  2. this.resource('post', { path: '/post/:post_id' }, function() {
  3. this.route('edit');
  4. this.resource('comments', function() {
  5. this.route('new');
  6. });
  7. });
  8. });

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

2 :post_id就是post的id。例如一个post的id是1,那么路由就是/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.

comments模板会被渲染进post的出口。 所有在comments下的模板(comments/indexcomments/new)都会被渲染进comments出口。

You are also able to create deeply nested resources in order to preserve the namespace on your routes:

为了保护路由的命名空间,可以添加深层嵌套的资源:

  1. App.Router.map(function() {
  2. this.resource('foo', function() {
  3. this.resource('foo.bar', { path: '/bar' }, function() {
  4. this.route('baz'); // This will be foo.bar.baz
  5. });
  6. });
  7. });

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 the application template.

  • 应用启动时,首先进入App.ApplicationRoute,它将渲染application模板。

  • App.IndexRoute is the default route, and will render the index template when the user visits / (unless / has been overridden by your own custom route).

  • App.IndexRoute是默认路由,当用户访问/时,将渲染index模板(除非/被自定义的路由覆盖)。

Remember, these routes are part of every application, so you don’t need to specify them in App.Router.map.

请记住,这些路由是每个应用的一部分,因此不需要在App.Router.map中指定。

Wildcard / globbing routes

通配符路由

You can define wildcard routes that will match mutliple routes. This could be used, for example, if you’d like a catchall route which is useful when the user enters an incorrect URL not managed by your app.

可以定义通配符路由来匹配多个路由。这种方法很有用,比如如果想获取用户进入应用的错误路由的时候。

  1. App.Router.map(function() {
  2. this.route('catchall', {path: '/*wildcard'});
  3. });

Like all routes with a dynamic segment, you must provide a context when using a {{link-to}} or transitionTo to programatically enter this route.

通配符路由与动态路由一样,在使用{{link-to}}或者transitionTo`来进入这个路由的时候,需要提供一个上下文。

  1. App.ApplicationRoute = Ember.Route.extend({
  2. actions: {
  3. error: function () {
  4. this.transitionTo('catchall', "application-error");
  5. }
  6. }
  7. });

With this code, if an error bubbles up to the Application route, your application will enter the catchall route and display /application-error in the URL.

在上述代码中,如果一个错误冒泡到应用路由,那么应用将进入catchall路由,并在URL中显示/application-error