定义路由 编辑页面


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

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

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

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

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

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

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

1
2
3
4
5
6
{{#link-to 'index'}}<img class="logo">{{/link-to}}

<nav>
  {{#link-to 'about'}}About{{/link-to}}
  {{#link-to 'favorites'}}Favorites{{/link-to}}
</nav>

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

你也可以通过创建一个Ember.Route的子类来对路由的行为进行自定义。例如,创建 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");
  }
});

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

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

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

Ember.js会自动地根据你在this.route设置的名字来找出对应的路由与控制器。

URL Route Name Controller Route Template
/ index IndexController IndexRoute index
/about about AboutController AboutRoute about
/favs favorites FavoritesController FavoritesRoute favorites

资源

你可以为一个资源定义一系列的路由:

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

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

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

这个路由器创建了三个路由:

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

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

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

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

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

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

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

动态段

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

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

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

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

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

探究动态段

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

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);
  }
});

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

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

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

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

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') };
  }
});

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

嵌套资源

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

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');
    });
  });
});

这个路由器创建了五个路由:

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

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

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

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
    });
  });
});

上面定义的路由器会创建如下路由:

URL 路由名称 控制器 路由 模板
/ 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

初始路由

一些路由在应用创建后便存在:

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

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

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

通配符路由

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

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

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

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

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