{"action":"created","issue":{"url":"https://api.github.com/repos/gf-rd/blog/issues/28","repository_url":"https://api.github.com/repos/gf-rd/blog","labels_url":"https://api.github.com/repos/gf-rd/blog/issues/28/labels{/name}","comments_url":"https://api.github.com/repos/gf-rd/blog/issues/28/comments","events_url":"https://api.github.com/repos/gf-rd/blog/issues/28/events","html_url":"https://github.com/gf-rd/blog/issues/28","id":125604728,"number":28,"title":"【Angular2开发指南】9、路由 & 导航","user":{"login":"253482219","id":13111484,"avatar_url":"https://avatars.githubusercontent.com/u/13111484?v=3","gravatar_id":"","url":"https://api.github.com/users/253482219","html_url":"https://github.com/253482219","followers_url":"https://api.github.com/users/253482219/followers","following_url":"https://api.github.com/users/253482219/following{/other_user}","gists_url":"https://api.github.com/users/253482219/gists{/gist_id}","starred_url":"https://api.github.com/users/253482219/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/253482219/subscriptions","organizations_url":"https://api.github.com/users/253482219/orgs","repos_url":"https://api.github.com/users/253482219/repos","events_url":"https://api.github.com/users/253482219/events{/privacy}","received_events_url":"https://api.github.com/users/253482219/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/gf-rd/blog/labels/Angular2%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97","name":"Angular2开发指南","color":"eb6420"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2016-01-08T12:42:43Z","updated_at":"2016-02-05T05:18:43Z","closed_at":null,"body":"## 学习带有Angular2的屏幕导航\r\n对于大多数应用程序，当用户执行应用中的任务时，界面会从一个视图跳转到下一个视图。\r\n\r\n浏览器是一个大家熟悉的应用导航模型。当我们在地址栏中输入URL时，浏览器将返回相应的页面。当我们点击网页上的超链接时，浏览器又会显示一个新的页面。当我们点击浏览器的返回和前进按钮时，浏览器会根据我们浏览的历史记录来展示相应的网页。\r\n\r\nAngular中的“组件路由”借鉴了浏览器的路由方式。它可以解析浏览器url作为一条导航指令传递给客户端视图，并且传递可选参数一直到支持该路由指令的视图组件，帮助视图组件呈现具体视图。我们可以把路由和网页的超链接绑定起来，当网页上的超链接被点击时，应用将跳转到对应的视图。我们可以在用户点击按钮，选择一个下拉框或者响应任何来源的消息时使用页面跳转功能。路由日志记录在浏览器的历史记录中，所以后退，前进按钮也是有效的。\r\n\r\n举个例子\r\n\r\n## 基础概念\r\n下面我们开始介绍组件路由器的一些核心概念，然后通过一系列的例子来探讨细节。\r\n\r\n路由选择器（The Router）是一个通过指定URL展示特定组件视图的服务。当改变浏览器的url时，路由选择器会查找RouteDefinition来确定哪个组件会被展示出来。\r\n\r\n一个新建的路由选择器是没有route definition的，使用之前还得经过配置。配置路由器最好是通过关联在主组件上的@RouteConfig装饰器进行。\r\n\r\n在本例中，我们配置了一个有三条路由规则的顶级AppComponent。\r\n\r\napp.component.ts(excerpt)\r\n```javascript\r\n@Component({...}) @RouteCoufig([{path:'/crisis-center',name:'CrisisCenter',component:CrisisListComponent},{path:'/heroes',name:'Heros',component:HeroListComponent},{path:'/hero/:id',name:'HeroDetail',component:HeroDetailComponent}])\r\n \r\nexport class AppComponent{}\r\n```\r\n\r\n上面定义了几个不同的 RouteDefinition。这些带有name属性的RouteDefinition的相同之处在于，都是把一个URL映射到一个组件上。\r\n\r\n此处的name是路由的名字。名字的拼写必须按照帕斯卡命名法（所有单词的首字母大写，然后直接连接起来，单词之间没有连接符： Age，LastName，WinterOfDiscontent）\r\n\r\n第三个路由中的:id是一个作为参数的标示符。例如在  '/hero/42' 这个url中，‘42’是id参数的值。组件HeroDetailComponent将会使用这个值找到一个id号为42的英雄，并将其信息展示出来。后面我们将会在本章中学到关于路由器参数的更多知识。\r\n\r\n现在我们知道如何配置RouteDefinition了。当应用的URL变成 '/heros' 时，路由器会根据这个url到RouteDefinition匹配出相应的组件（在这里就是名字为 Heros 的HeroListComponent），并将组件显示到挂载在主视图HTML下的RouterOutlet中。\r\n\r\n<!--Routed views go here--><router-outlet></router-outlet>\r\n\r\n现在我们有了配置好的路由选择器以及展示组件的地方，但是我们要怎样进行网页跳转呢？在浏览器的地址栏输入URL，可以直接跳转到我们想去的页面，不过大多数时候，跳转网页是作为用户行为的一种响应，例如用户点击了锚标签。\r\n\r\n我们给锚标签增加了一条RouteLink指令，并绑定到一个模板表达（a template expression），此模板表达式返回一个路由链接参数数组（the link parameters array）。路由最终会把这个数组传递给ULR和组件视图，并进行处理。\r\n\r\n我们在AppComponent模板中看是如何绑定的:\r\n\r\ntemplate:`<h1>Component Router</h1><a [routerLink]=\"['CrisisCenter']\">Crisi Center</a><a [routerLink]=\"['Heroes']\">Heroes</a><router-outlet></router-outlet>`\r\n\r\n我们给2个a标签添加了RouterLink指令。再给每个RouterLink绑定上一个字符串类型的数组，数组中存放的是在route definition中定义的名字。‘CrisisCenter’和‘Heroes’都是我们在上文配置过的路由的名字。\r\n\r\n我们会在本章的后面学习更复杂的链接表达式，并解释为什么这些链接都要定义成数组。\r\n\r\n## 小结\r\n@RouterConfig装饰器把AppComponent和一个配有路由规则的路由关联起来。组件拥有一个RouterOutlet，由路由生成的视图即显示在RouterOutlet中。组件还可以为锚标签配置RouterLinks指令，点击后通过事先配置好的路由跳转到相应页面。\r\n\r\n经此配置，AppComponent已经变成了一个路由组件，可以被路由找到。\r\n\r\n下面有一张组件路由的关键字含义对照表：\r\n\r\n路由部分 | 含义\r\n------------ | -------------\r\nRouter | 为当前URL展示应用组件。控制导航从一个组件跳转到下一个组件。\r\n@RouteConfig | 用RouteDefinitions配置路由器，每个RouteDefinition为URL关联一个组件。\r\nRouteDefinition | 定义路由如何根据URL跳转到组件\r\nRoute | 通常在RouteDefinition定义了路径，路由名称和组件类型。\r\nRouteOutlet | 路由应该在<router-outlet>标记处显示视图\r\nRouterLink | RouterLink指令绑定一个可被点击的HTML元素到路由上。\r\nLink Paraeters Array | 这个数组被路由解释成一个路由指令。我们可以绑定一个RouterLink到那个数组或者把数组作为参数传递到Router.navigate方法中。\r\nRouting Component | 一个有一个路由导航的Angular组件。\r\n \r\n我们在下列涉及到路由选择器的章节中学习到更多的细节\r\n* 路由配置\r\n* 推动路由导航的超连接参数数组\r\n* 当用户点击数据绑定的RouterLink时的导航\r\n* 程序控制下的导航\r\n* 如何在路由参数中传递信息\r\n* 在已经存在的路由中，如何创建子路由\r\n* 如何设置一个默认路由\r\n* 在路由生命周期中通过routerCanDeactivate暂停、确认和取消导航\r\n我们已经进入到路由学习的一个里程碑。我们第一个里程碑在如何导航切换2个占位视图。在我们最后一个里程碑，将会有一个拥有很多子路由的多视图化的模块，\r\n\r\n我们认为你已经熟悉了Angular2.0的工具，也了解了在快速开始中提到的基本概念和英雄之旅的教程。\r\n\r\n当我们在一些简单的例子中获得一些进步时，这章就不是教程了。我们讨论代码和设计与路由相关的设计决策和应用设计。\r\n\r\n我们的内容涵盖了一切。在实例中可了解更多内容。\r\n\r\n## 一个简单的应用\r\n在我们脑海里有一个从一个里程碑移动到下一个里程碑的应用。我们的客户端就是英雄雇佣公司。英雄们需要工作，公司找到危机派他们去解决。\r\n\r\n这个应用有2个主要的特征：\r\n* 一个危机中心：我们保存一个危机列表用来给英雄们分配任务。\r\n* 一个英雄区域：我们保存一个英雄列表用来给公司雇佣英雄。\r\n 打开应用。首先显示的是危机中心。我们将返回这里。\r\n点击英雄列表连接，我们将看到英雄列表显示界面。\r\n\r\n![](https://angular.io/resources/images/devguide/router/hero-list.png)\r\n\r\n我们选择一位英雄，应用跳转到英雄名单界面。\r\n\r\n![](https://angular.io/resources/images/devguide/router/hero-detail.png)\r\n\r\n我们也可以点击浏览器自带的后退按钮。这样我们同样也能回退到英雄列表界面。\r\n我们的输入立刻被响应。我们点击‘后退’按钮，应用会跳转到英雄名单界面。Angular组件同web一样更新浏览器历史记录。\r\n现在我们点击危机中心的连接。我们来到危机中心，并且看到正在进行的危机列表。\r\n\r\n![](https://angular.io/resources/images/devguide/router/crisis-center-list.png)\r\n\r\n我们选择一个，应用呢会跳转到一个危机编辑界面。\r\n\r\n![](https://angular.io/resources/images/devguide/router/crisis-center-detail.png)\r\n\r\n这里和英雄细节界面有点区别。英雄细节编辑界面直接保存我们输入的内容。而在危机细节编辑界面中，我们输入的内容是临时的，直到我们点击保存按钮或者取消按键，才决定是否保存或者丢弃我们输入的内容。两个按钮都会跳转到危机中心，，并显示危机列表。\r\n设想一下我们点击了危机中心，输入了内容，但没有点击任何一个按钮。此时我们或许点击浏览器的后退按钮，或许点击英雄列表连接。\r\n无论做那个操作，都会弹出一个对话框。\r\n\r\n![](https://angular.io/resources/images/devguide/router/confirm-dialog.png)\r\n\r\n我们点击‘OK’会丢失我们输入的危机细节，点击‘取消’会接着编辑危机细节。\r\n路由选择器支持routerCanDeactivate生命周期,这给了我们一个清理内存的机会。并在离开当前视图时询问用户。\r\n\r\n![](https://angular.io/resources/images/devguide/router/router-anim.gif)\r\n\r\n这里有一个图显示应用中所有可选择的路由跳转：\r\n\r\n![](https://angular.io/resources/images/devguide/router/complete-nav.png)\r\n\r\n这个应用程序演示了路由选择的器的功能：\r\n* 跳转到一个组件（英雄超链接，跳转到英雄列表）\r\n* 包含了一个路由参数（当跳转到英雄细节界面时传递英雄id）\r\n* 子路由（危机中心有它自己的子路由）\r\n\r\n# 里程碑#1，从路由开始\r\n我们从一个简单的视图应用开始，这个应用是在两个空视图界面之间跳转。\r\n\r\n![](https://angular.io/resources/images/devguide/router/router-1-anim.gif)\r\n\r\n## 下载组件路由选择器库\r\n组件路由选择器并不是Angular2.0的核心。这是它自己的库。路由选择器是一个可选的服务，也许有一天你会更喜欢一个不同的路由选择器。\r\n组件路由库是Angular npm的一部分。通过加载index.html就可以获得该库，在加载完Angular核心脚本之后就能得到了。\r\n```javascript\r\n<script scr=’node_modules/angular2/bundles/router.dev.js’></script>       \r\n```\r\n## 设置<base href>\r\n组件路由通过使用浏览器的 history.pushState功能进行跳转。多亏了pushState,我们才使得应用中的URL路径看起来是我们想象中的那样。例如：localhost:3000/crisis-center.在应用中的URL和服务器端的ULR看起来一样。\r\n现在的HTML5浏览器是一个支持pushState的浏览器，这让许多用户倾向于使用“HTML5”类型的URLs。\r\n我们必须添加一个<base href>元素标签在index.html才能使它运行起来。这个href的值详细描述了基础URL的信息，用于包括css文件，脚本，图片的文档，\r\n添加base元素在head标签之后即可。如果应用的文件夹是该应用的根路径，设置href准确的指向根路径。\r\n<base href=’/’>\r\n组件路由的默认导航是HTML5类型的导航。在浏览器URL类型的下方，我们可以学习下为什么”html5“类型被喜欢，如何适应他的行为，并且如何去切换旧的哈希（#）类型，这些是非常有必要学习的。\r\n\r\n## 实例节点\r\n当我们运行实例的时候有点复杂，因为服务器将应用的基本地址设置成了动态地址。这样我们就替换<base href…>为一个脚本，这个脚本中有一个<base>标签能快速加载。\r\n```javascript\r\n<script>document.write(‘<base href=”’ + document.location +’”/>’);</script>\r\n```\r\n我们可以用这种技巧来搞定实例，不用写过多的代码。\r\n\r\n## 路由服务提供者指南\r\n我们的应用启动于~/app文件夹中的boot.ts文件。这个文件短小，但对于路由意义重大。\r\nboot.ts\r\n从’./app.component’导入{AppComponent}; 从’Angular2/platform/browser'导入{bootstrap}；从”angular2/router“导入{ROUTER_PROVIDERS}；bootstrap(AppComponent,[ROUTER_PROVIDERS]);\r\n我们按照预计的引入了我们根组件和Angular bootstrap方法。\r\n我们也从路由库中导入了 ROUTER_PROVIDERS。路由选择器是一个服务应用于依赖注入器集合。在依赖注入器集合中，我们定义了ROUTER_PROVIDERS数组。\r\n\r\n我们用AppComponent来引导Angular作为我们根组件，并注册服务，像我们经常做的一样。在服务数组中的第二个参数是bootstrap的方法。把路由服务作为root，可以使组件路由在应用的任何地方被使用。\r\n\r\n在依赖注入章节学习关于开发者，provide方法，注入服务等相关知识。\r\n\r\n##  AppComponent 外壳\r\n根AppComponent是一个应用壳。在顶部有个标题，一个导航栏上有2个超链接。一个Router outlet在底部显示视图开、关页面。以下是具体含义：\r\n\r\n![](https://angular.io/resources/images/devguide/router/shell-and-outlet.png)\r\n\r\n对应的组件模板如下：\r\ntemplate: `<h1>Component Router</h1><a [routerLink]=”[‘CrisisCenter’]”>Crisis Center</a><a [routerLink]=”[‘Herose’]”>heroes</a><router-outlet></router-outlet>`,\r\n\r\n## RouterOutlet\r\nRouterOutlet是路由库的一个组件。路由显示页面在绑定<router-outlet>标签内。\r\n一个模板可以写多个匿名的<router-outlet>\r\n\r\n## RouterLink 绑定\r\n在outlet之上，在锚标签之内。我们看到属性绑定到RouterLink指令，看起来是[routerLink]=“[…]”。我们从路由库中导入RouterLink。\r\n模板表达式的右边等于是返回一个连接参数数组。\r\n连接参数数组存放着路由导航的信息：\r\n* 路由的名字，指定了目标组件和URL路径。\r\n* 可选的路由和查询参数可直达路由URL\r\n在本例中的数组每一个都有一个单独的字符串类型的参数，Route的名字，这个也是我们配置在@RouteConfig()中的。我们现在不需要设置路由参数。\r\n在appendix below中学习更多关于连接参数数组的相关知识。\r\n\r\n## @RouteConfig\r\n一个路由选择器存着一个路由定义列表。在新的路由选择器中，这张列表是空的。我们必须亲自配置它。\r\n一个路由选择器也需要一个主组件，一个原始的导航点。\r\n把一个新的路由选择器创建组合起来是很自然的，它的配置和它分配给主组件就一个步骤。@RouteConfig的目的是我们能在这里使用：\r\n```javascript\r\n@Componen({…}) @RouteConfig([{path:’/crisis-center’,name:’CrisisiCenter’,component:CrisisListCompoent},{path:’/heroes’,name:’heroes’,component:HerolistComponent}]) export class ApppComponent{}\r\n```\r\n这个@RouteConfig 工具创建了一个新的路由选择器。我们把这个工具应用在AppComponent上，这里设定为路由的主组件。我们来讨卵在@RouteConfig()是一个Route Definitions数组。\r\n\r\n我们来设定2个定义：\r\n```javascript\r\n{path:’/crisis-center’,name:’CrisisCenter’,component:CrisisListComponent},{path:’/heroes’,name:’Heroes’,component:HeroListComponent}\r\n```\r\n每一个定义转化成一个路由并包含如下信息：\r\n* path-URL路径的段\r\n* name-路由的名字\r\n* component-路由相关的组件。\r\n\r\n路由选择器借鉴了注册表的定义方式：\r\n1．  当浏览器的URL 改变\r\n2．  我们通知路由选择跳转到另一个路由的名字\r\n\r\n翻译成普通话，我们可以称第一个路由：\r\n当浏览器的当前url改变，并且匹配到了/crisis-center段，创建或者检索一个CrisisCenterComponent实例并显示。\r\n当应用需要跳转到一个名字为CrisisCenter的组件时，组成浏览器url的地址有/crisis-center这个段时，更新浏览器的当前地址和历史记录，创建或查找一个CrisisCenterComponent实例，并显示这个组件视图。\r\n##  ‘开始’打包\r\n我们有一个很基础的导航应用，可以通过点击超链接切换两个视图的简单应用。\r\n我们将要学会：\r\n* 下载路由库\r\n* 添加一个导航条用于锚节点和routerLink指令的模板\r\n* 添加一个router-outlet作为视图将要展示的地方\r\n* 用@RouterConfig配置一个路由选择器\r\n* 设置路由组成‘HTML5’浏览器ULR\r\n应用程序的其他部分都是平凡的，很少有兴趣从路由器的角度看。这里有读者倾向于通过这一个里程碑构建示例的详细信息。\r\n\r\n我们应用的开始文件结构如下：\r\n \r\n\r\n这里是应用程序特定的文件\r\n```javascript\r\nimport{Component} from ‘angular2/core’;\r\nimport {RouteConfig, ROUTER_DIRECTIVES} from ‘angular2/router’;\r\nimport {CrisisListComponent} from ‘./crisis-list.component’;\r\nimport {HeroListComponent} from ‘./hero-list.component’;\r\n@Component({\r\n         selector: ‘my-app’,\r\n         template: `\r\n         &lt;h1&gt;Component Router&lt;/h1&gt;\r\n         &lt;a [routerLink]=&quot;[‘CrisisCenter’]&quot;&gt;Crisis Center&lt;/a&gt;\r\n         &lt;a [routerLink]=&quot;[‘Heroes’]&quot;&gt;Heroes&lt;/a&gt;\r\n         &lt;router-outlet&gt;&lt;/router-outlet&gt;\r\n         `,\r\n         directives: [ROUTER_DIRECTIVES]\r\n         })\r\n@RouteConfig([\r\n         {path:’/crisis-center’, name: ‘CrisisCenter’, component: CrisisListComponent},\r\n         {path:’/heroes’, name: ‘Heroes’, component: HeroListComponent}\r\n         ])\r\nexport class AppComponent { }\r\n```\r\n# 里程碑#2：英雄人物\r\n我们已经看到导航如何是用RouteLink指令。\r\n现在我们将学一些新的技巧，例如：\r\n* 如何组织我们的应用到功能区域\r\n* 如何从一个组件跳转到另一个组件\r\n* 如何在路由参数（RouteParams）中传递信息\r\n为了演示，我们创建了英雄人物特征这个例子。\r\n\r\n## 英雄人物＂特征区域＂\r\n一个典型的应用有很多特征区域，每一个功能岛都有自己的工作流．为了一个特定的商业目的。\r\n我们本可以继续在app／　这个目录下添加文件。这是不现实的，最终也是不可被维护的。我们想最好能把各自的特征岛放在各自的文件夹下。\r\n第一步：我们要创建一个独立的 app/heroes/ 文件夹。并且在这里添加Hero Management用来管理特征文件。\r\n我们不会另辟蹊径。我们的例子很多的代码和功能来自“Tutorial: Tour of Heroes”.\r\n这里演示了用户将如何操作这个应用程序。\r\n\r\n![](https://angular.io/resources/images/devguide/router/router-2-anim.gif)\r\n\r\n## 添加英雄人物功能\r\n我们删除在app/文件夹中的占位文件 hero-list.component.ts。\r\n\r\n我们创建了一个新的hero-list.component.ts在app/heroes/中，从教程中的heroes.component.ts内容复制来的。我们同样把hero-detail.component.ts和hero.service.ts文件复制到 heroes/ 这个文件内。\r\n当我们完成组织架构，我们有3个英雄管理文件：\r\n \r\n\r\n我们将在启动时提供HeroService服务，以便于在应用中的任何地方都可以使用（详见：boot.ts）\r\n## 带有参数的路由定义\r\n新的英雄人物特征模块有两个互相作用的组件，列表组件和细节组件。列表视图是自给自足的，我们跳转到视图列表，它给我们一个英雄列表并展示出来。它不需要外界的其它信息。\r\n细节列表则不同。它展示了英雄的特别之处。它不知道那个英雄才是他自己。所以这些信息要从外界得到。\r\n在我们的例子中，当用户从列表中选择了一个英雄的时候，我们就跳转并展示英雄的细节视图。我们将告诉细节视图，去展示在URL中包含英雄ID对应的那个英雄的详细视图。\r\n有了这个计划，我们返回app.component.ts文件，来对路由配置做一些改变。\r\n首先，我们从它们新的本地路径app/heroes/中导入2个组件：\r\n```javascript\r\nimport {HeroListComponent}     from'./heroes/hero-list.component';\r\nimport {HeroDetailComponent}   from'./heroes/hero-detail.component';\r\n```\r\n我们更新@RouteConfig如下：\r\n```javascript\r\n@Component({ ... })\r\n@RouteConfig([\r\n  {path:'/crisis-center', name:'CrisisCenter', component: CrisisListComponent},\r\n  {path:'/heroes',        name:'Heroes',       component: HeroListComponent},\r\n  {path:'/hero/:id',      name:'HeroDetail',   component: HeroDetailComponent}\r\n])\r\nexport class AppComponent { }\r\n```\r\nCrisisCenter和Heroes定义不改变，当我们移动hero-list.component.ts到app/heroes这个新位置时，只会影响import状态，不会影响路由定义。\r\n我们为HeroDetailComponent添加一个新的路由定义，把相关信息记录如下：\r\n```javascript\r\n{path:’/hero/:id’,name:’HeroDetail’,component:HeroDetailComponent}\r\n```\r\n注意 :id 会为路由参数创建一个入口。在这种情况下，我们希望路由选择器把英雄id填充进去。\r\n如果我们告诉路由选择器跳转到细节组件，并且展示“Magenta”英雄id为15，浏览器url如下：localhost:3000/hero/15\r\n\r\n如果用户将这个url输入到浏览器地址栏，路由选择器会识别参数并且跳转到名字为‘Magenta’的详细页面。\r\n\r\n## 跳转到细节界面命令\r\n通过点击超链接我们并不能跳转到细节界面。因为我们并没有在导航栏中添加一个新的锚节点。相反，我们会在用户在列表中选择一个英雄时并要跳转展示这个英雄的细节页面是去检测。\r\n\r\n我们将要调整HeroListComponent组件来实现这个功能，它的构造函数会从依赖注入中获得路由服务和HeroService。\r\n```javascript\r\nconstructor(private_router:Router,private_service:HeroService){}\r\n```\r\n我们在这个模板上做一些小的改变。\r\n```javascript\r\ntemplate: `\r\n  <h2>HEROES</h2>\r\n  <ul>\r\n    <li *ngFor=&quot;#hero of heroes&quot;\r\n      (click)=&quot;onSelect(hero)&quot;>\r\n      <span class=&quot;badge&quot;>{{hero.id}}</span> {{hero.name}}\r\n    </li>\r\n  </ul>\r\n`\r\n```\r\n这个模板定义了一个 *ngFor 做了一个循环。这里有个（click）事件绑定到了组件的选择器onSelect方法中，在onSelect中我们这样编写：\r\n```javascript\r\nonSelect(hero:Hero){ this._router.navigate([‘HeroDetail’,{id:hero.id}]);}\r\n```\r\n它叫做带有超连接参数数组的路由navigate方法。这个数组很像我们之前看到的连接参数数组，在绑定RouterLink指令时使用的。\r\n这次我们在代码中看到了它，而不是HTML中。\r\n\r\n## 设置路由参数\r\n我们在跳转到我们希望看到被选中的英雄的HeroDetailComponent组件时，我们需要2个信息，目的路由和英雄的id。\r\n因此，连接参数数组有2个元素：目的路由的名字和一个指定被选英雄id的路由参数对象。\r\n[‘HeroDetail’,{id:hero.id}]//{id:15}\r\n路由选择器从这个数组中获取数据组合由两部分组成的URL。\r\nlocalhost:3000/hero/15\r\n\r\n## 获得路由选择器的参数\r\nHeroDetailComponent组件如何获得那个id值的呢？当然不用解析URL！那是路由的工作。\r\n路由可以准确的从ULR中获得路由参数（id：15）并且通过RouteParam服务传递给HeroDetailComponent.\r\n通常情况下，我们编写构造函数去告知Angular把服务注射到其它服务，使得组件导入和把他们当做私有变量使用。\r\n```javascript\r\nconstructor(private_router:Router, private_routeParams:RouteParams,private_service:HeroService){}\r\n```\r\n然后，在ngOnInit方法中，我们通过名字去RouteParams服务中获得参数id的值，并告知HeroService返回这个id的英雄信息。\r\n```javascript\r\nngOninit(){ let id = this._routeParams.get(‘id’);this._service.getHero(id).then(hero=>this.hero = hero);}\r\n```\r\nAngualr 在创建一个HeroDetailComponent实例之后就调用ngOnInit方法。\r\n我们把数据存取逻辑放在ngOnInit方法中，比放在构造函数中更具有可测试性。我们在OnInit appendix中会更展开更多有关细节。\r\n\r\n## 跳转回列表组件\r\nHeroDetailComponent有一个返回按钮绑定了它自己的gotoHeroes方法，以至于能够跳回到HeroListComponent组件。\r\n\r\n路由选择器中navigate方法带有一个相同的一个节点连接参数数组，为了[routerLink]指令绑定。它保存了HeroListComponent路由的名字：\r\n\r\ngotoHeroes(){// <a [routerLink]=”[‘Heroes’]”>Heroes</a>this._router.navigate([‘Heroes’]);}\r\n\r\n##把英雄应用包起来\r\n在路由学习中，我们已经到了第二里程碑了\r\n我们已经学会了：\r\n* 如何去组织架构特征区域\r\n* 如何从一个组件跳转到另一个组件\r\n* 在路由参数中传递信息\r\n在经过以上改变后，我们的目录结构改变如下：\r\n\r\n## 英雄app代码\r\n这里是有关该版本的示例应用程序的相关文件。\r\n```javascript\r\nimport {Component}   from 'angular2/core';\r\nimport {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';\r\nimport {CrisisListComponent}   from './crisis-list.component';\r\nimport {HeroListComponent}     from './heroes/hero-list.component';\r\nimport {HeroDetailComponent}   from './heroes/hero-detail.component';\r\n@Component({\r\n  selector: 'my-app',\r\n  template: `\r\n    <h1>Component Router</h1>\r\n    <a [routerLink]=&quot;['CrisisCenter']&quot;>Crisis Center</a>\r\n    <a [routerLink]=&quot;['Heroes']&quot;>Heroes</a>\r\n    <router-outlet></router-outlet>\r\n  `,\r\n  directives: [ROUTER_DIRECTIVES]\r\n})\r\n\r\n@RouteConfig([\r\n  {path:'/crisis-center', name: 'CrisisCenter', component: CrisisListComponent},\r\n  {path:'/heroes',        name: 'Heroes',       component: HeroListComponent},\r\n  {path:'/hero/:id',      name: 'HeroDetail',   component: HeroDetailComponent}\r\n])\r\n\r\nexport class AppComponent { }\r\n```\r\n \r\n# 里程碑#3：危机中心\r\n此时危机中心是一个假视图。是时候把它变成真正的视图了。\r\n新的危机中心开始于对英雄特征模块的拷贝。我们创建了一个新的app/crisis-center目录，把英雄文件中的每一个‘hero’单词替换成‘crisis’.\r\n一个crisis有一个id和名字，就像一个Hero一样。新的CrisisListComponent显示危机列表。当用户选择一个危机，应用就跳转到CrisisDetailComponent组件来显示并编辑危机名称。\r\nVoila(俄语，语气词)，即时特征模块。\r\n除非我们能学点东西，否则这项练习是没有意义的。我们又有了些新想法：\r\n*  应用应该默认导航到危机中心\r\n* 用户可以取消不想要的编辑\r\n* 当还在编辑状态的时候，路由应该阻止离开细节的编辑界面。\r\n也有一些挥之不去的问题在英雄中，我们可以在危机中心解决。\r\n目前我们在应用的最顶级中给每个视图都注册了一个路由。如果我们展开有100条记录的危机中心，我们将会在APPComponent配置中进行100次改变。如果我们重命名了一个危机中心组件或者改变了一个路由的定义，我们也将改变AppComponent。\r\n如果我们跟着英雄模块的一样，我们将在boot.ts中添加CrisisService给providers。现在我们可以在任何一个地方使用HeroService和CrisisService两个服务，即使它需要在各自的特征模块内。这样很糟糕。\r\n当模块改变，如危机中心改变它不应该影响AppComponent或者boot.ts。我们需要对它单独关注下。\r\n我们要修正所有的这些问题，并且给危机中心添加一个新的路由特征。\r\n最显著的修正就是子路由组件和它子路由的配置。\r\n我们将把Heroes留在这种不完美的状态，与我们希望的更好危机中心做一个明显的对比。\r\n\r\n## 一个独立的危机中心模块\r\n危机中心是两个应用工作流之一。用户在他们选择危机或者英雄界面显示时进行跳转。\r\nCrisisCenter 和 Heroes 都是根组件AppComponent的子组件。\r\n不幸的是，它们和它们相关的文件混在了一起，并且都与根组件AppComponent在相同的目录下。我们最好能把他们分开，放在各自的区域，以至于他们能更独立的操作。或许我们可以重用一个组件或者把其中一个重用在不同的应用程序，或许我们可以在用户选择进入它的工作流程时动态加载其中的一个。\r\n有些人可能会称它为yagni甚至认为是这样的。我们对危机中心和英雄混合在一起这点很担心。这有代码规范。（超连接）\r\n\r\n隔离功能区模块从彼此开起来很好。\r\n\r\n![]{https://angular.io/resources/images/devguide/router/component-tree.png}\r\n\r\n* 每一个特征区域都有自己的模块文件夹\r\n* 每一个区域都有自己的根组件\r\n* 每一个根组件都有自己的router-outlet和子路由选择器\r\n* 区域路由很少交叉\r\n\r\n我们将使得危机中心模块独立出来，并用英雄模块作为比较，效率、结果和缺点。然后我们决定选择哪个方法。\r\n\r\n## 子路由组件\r\n我们创建了一个新的 app/crisis-center文件夹并添加crisis-center-component.ts文件，文件内容如下：\r\n```javascript\r\n@Component({\r\n  template:  `\r\n    <h2>CRISIS CENTER</h2>\r\n    <router-outlet></router-outlet>\r\n  `,\r\n  directives: [RouterOutlet],\r\n  providers:  [CrisisService]\r\n})\r\n\r\n@RouteConfig([\r\n  {path:'/',         name: 'CrisisCenter', component: CrisisListComponent, useAsDefault: true},\r\n  {path:'/:id',      name: 'CrisisDetail', component: CrisisDetailComponent},\r\n  {path:'/list/:id', name: 'CrisisList',   component: CrisisListComponent}\r\n])\r\nexport class CrisisCenterComponent { }\r\n``` \r\nCrisisCenterComponent组件与AppComponnet组件是平行关系。\r\nCrisisCenterComponent 是危机中心的根组件，就像AppComponnet是整个app的根组件一样。\r\nCrisisCenterComponent组件是一个危机管理器的壳和AppComponnet管理高级工作流一样。\r\nAppComponnet有@RouteConfig来定义顶层的路由。CrisisCenterComponent也有一个@RouteConfig来定义危机中心子路由。\r\n\r\nCrisisCenterComponent组件模板很简单，比AppComponnet还要简单。它没有内容，没有超链接，只有一个为危机中心子视图显示的<router-outlet>。\r\n也没有选择器，它也不需要。我们不会嵌入这个组件到它的父模板中。我们是通过父路由，从外面跳转进来。\r\n\r\n## 服务隔离\r\n我们把CrisisSevice添加到组件提供数组，而不是注册到boot.ts中的启动方法里。\r\nprovider:[CrisisService]\r\n这一步限制了为危机中提供服务的scope和它的子组件树。在危机中心之外没有组件需要进入CrisisSevice服务中。通过对scope的限制，我们有信心能够使它独立出来，也不担心它会破坏应用程序中其它不相关的模块。因为其它模块没有访问它的权限。\r\n\r\n## 子路由配置\r\nCrisisCenterComponent路由组件应该像AppComponent路由组件一样。\r\n用@RouteConfig 配置了CrisisCenterComponent的方法和我们之前使用@RouteConfig的方法一致。\r\napp/crisis-center/crisis-center.component.ts(routes only)\r\n```javascript\r\n@RouteConfig([\r\n  {path:‘/‘,         name: ‘CrisisCenter‘, component: CrisisListComponent, useAsDefault: true},\r\n  {path:‘/:id‘,      name: ‘CrisisDetail‘, component: CrisisDetailComponent},\r\n  {path:‘/list/:id‘, name: ‘CrisisList‘,   component: CrisisListComponent}]}\r\n```\r\n这有3个CrisisCenter的路由，其中2个都带有id参数。它们指向的组件我们不用说了，它们将要跳转的组件我们可以通过名字看出来。\r\n\r\n我们不能就只看CrisisCenterComponent就分辨它是应程序的一个子组件。我们也不能分辨它的路由选择器都是子路由。\r\n这是特意设计的。CrisisCenter模块不应知道它是子模块。它或许在它在的应用程序中是顶级组件。它或许被重用在不用的应用程序中。CrisisCenter在它所有可能被用到的地方保持自己的独立性。\r\n我们通过给顶级AppComponent组件的路由配置，给应用程序设置一个子组件。\r\n\r\n## 父组件配置\r\n下面修正了父组件AppComponent路由配置:\r\napp/app.component.ts (routes only)\r\n```javascript\r\n@RouteConfig([\r\n  { // Crisis Center child route\r\n    path: ‘/crisis-center/...‘,\r\n    name: ‘CrisisCenter‘,\r\n    component: CrisisCenterComponent,\r\n    useAsDefault: true\r\n  },\r\n  {path: ‘/heroes‘,   name: ‘Heroes‘,     component: HeroListComponent},\r\n  {path: ‘/hero/:id‘, name: ‘HeroDetail‘, component: HeroDetailComponent},\r\n])\r\n```\r\n第二个和第三个英雄模块的路由保持原样。第一个CrisisCenter的路由已经明显被修改，我们已经重新编写了它，我们来关注下不同。\r\n```javascript\r\n{ // Crisis Center child route\r\n  path: ‘/crisis-center/...‘,\r\n  name: ‘CrisisCenter‘,\r\n  component: CrisisCenterComponent,\r\n  useAsDefault: true\r\n},\r\n```\r\n留意一下path结尾的斜杠和…\r\n这个表示它是一个不完全的路由（AKA a non-terminal route）。这需要与父类路由的/crisis-center路由组合起来变的完整。一个来自子路由的路径，属于指定的组件。\r\n一切都很好。父路由选择器被指定组件是CrisisCenterComponent，这个是一个带有自己的路由选择器和路由的Routing Component。\r\n\r\n## 默认路由\r\n另一个重要的改变是加入了useAsDefault属性。它的值是true时，会让这个路由变成默认路由。当应用程序运行时，从浏览器的URL中获取不到任何信息时，路由会默认跳转到危机中心中。这就是我们计划要做的。\r\n\r\n## 跳转到子路由\r\n我们已经在顶级设置了默认路由来跳转到CrisisCenterComponent。最后拼接成的路由应该是 /crisis-center/ 和 CrisisCenterComponent中3个路由组合而成的。到底是三个中的哪一个呢？\r\n它可能是三个中任一个。由于缺少添加的信息，路由选择器不能决定是哪个从而抛出一个异常。\r\n我们试着试程序又简单又不会挂掉。我们必须做些什么。\r\n回滚到子组件CrisisCenterComponent后尾的第一个路由。\r\napp/crisis-center/crisis-center.component.ts (default route)\r\n```javascript\r\n{path:‘/‘, name: ‘CrisisCenter‘, component: CrisisListComponent, useAsDefault: true},\r\n```\r\n这儿又有一个 useAsDefault:true。这告诉了路由选择器使用来自这个默认子路由中的路径来生成最终的URL。\r\n链接生成基础的URL用 /crisis-center/ 和 / ，删除掉后面的…，然后我们得到：\r\nlocalhost:3000//crisis-center/\r\n\r\n## 处理未保存的更改\r\n返回到“Heroes”工作流。app应用不假思索和验证的接受所有对英雄任务的改变。事实上，用户的更改越来越多。也许我们不得不去跨域验证。也许我们会在服务器上验证。也许我们不得不在编辑时保存所有的修改，直到用户确认修改或者取消修改或者取消后又恢复修改。\r\n当用户跳转走时，我们应该怎么处理这些没有通过验证，又没有被保存的编辑呢？我们最好暂停一下，让用户来决定。或许他们取消离开页面，并继续做更多的修改。\r\n我们需要路由选择器配合一下，来完成这些操作。我们需要路由选择器的生命周期。\r\n\r\n## 路由器生命周期（钩子）\r\nAngular组件都有生命周期钩子。例如，Angular在它创建和销毁一个组件是，调用钩子方法Onlnit 和 OnDestroy接口。\r\n路由调用类似的接口方法，routerCanActive 和 routerCanDeactivate在它要跳转到或者离开一个组件之前调用。\r\n如果 can…函数返回true，跳转继续。如果它返回false,路由取消跳转，并留在当前视图。\r\n路由的生命周期补充了组件的生命周期。虽然路由的钩子方法比组件的钩子有更多方法，但是我们仍需要组件生命周期钩子方法。\r\n例如：组件的生命周期钩子方法不能在组件创建和销毁时暂停。因为它们是同步的，它们不能停止视图的跳转来等待异步程序的结束。\r\n设想一下我们有未保存的修改。用户开始准备离开该视图。我们不能丢弃用户的修改，如果丢弃这将是糟糕的体验。所以我们尝试着把这些修改保存到服务器上。\r\n如果保存失败（或许数据是无效的），我们改怎么办呢？\r\n如果我们让用户移动到下个视图，我们会有一个丢失文字的错误。我们不能为了等待服务器而阻拦程序往下运行，这点是在浏览器中行不通的。\r\n我们异步进行等待服务器应答期间，我们要去暂停跳转。\r\n\r\n## 取消和保存\r\n我们这个应用模型不和服务器通信。幸运的是我们用另一个方法来展示一个异步的路由选择器的钩子方法。\r\n用户在CrisisDetailComponent中改变危机的信息。不像HeroDetailComponent，用户的改变不会立刻危机组件的实体。我们在用户按下保存按钮后，更新危机的实体。当用户点击取消按钮时，我们丢弃所有的修改。\r\n在执行完保存或者取消后，都会跳转到危机列表。\r\ncrisis-detail.component.ts\r\n```javascript\r\nexport class CrisisDetailComponent implements OnInit, CanDeactivate {\r\n         public crisis: Crisis;\r\n         public editName: string;\r\n         cancel() {\r\n         this.editName = this.crisis.name;\r\n         this.gotoCrises();\r\n         }\r\n         save() {\r\n         this.crisis.name = this.editName;\r\n         this.gotoCrises();\r\n         }\r\n}\r\n```\r\n那如果用户试着离开当前页，又不去保存或者取消呢？用户可以点击浏览器的后退按钮或者点击英雄超链接。这两个行为都会触发跳转，我们app应该自动保存或者取消修改吗？\r\n我们两者都不做。相反我们将会讯问用户来做一个明确的选择在确认对话框中，来异步的等待用户的回答。\r\n我们也能停止代码继续运行来同步的等待用户的决定。我们的程序要有更多的响应并做其它工作在异步等待用户的决定。异步等待用户决定和异步等待服务器应答原理一致。\r\n与服务器的对话返回一个promise对象。 当用户决定保存或者放弃保存，promise能相应的解决丢弃并离开本视图或者留在危机编辑视图继续修改。\r\n我们在路由选择器中的routerCanDeactivate生命周期函数中来解决这个问题。\r\ncrisis-detail.component.ts (excerpt)\r\n```javascript\r\nimport {CanDeactivate, ComponentInstruction} from ‘angular2/router‘;\r\nimport {DialogService} from ‘../dialog.service‘;\r\nexport class CrisisDetailComponent implements OnInit, CanDeactivate {\r\n  public crisis: Crisis;\r\n  public editName: string;\r\n}\r\n```\r\n注意routerCanDeactivate方法返回一个同步结果。它立刻返回true如果这里没有在编辑的内容。但是它也能返回一个promise对象，并且路由选择器将会在离开当前视图或者继续编辑前等待这个promise执行的结果。\r\n\r\n## 两个临界点\r\n路由的钩子方法是可选的。我们并不从基础类继承。我们简单的去实现它的方法就好了。\r\n我们信赖路由选择器的钩子函数。我们不担心用户执行任何一个离开当前页的命令。这就是路由选择器的工作。我们简单地的写这个方法，并且让路由选择器去处理。\r\n\r\n# 里程碑#4\r\n\r\n我们使用路由参数来指定路由URL中的参数，作为我们跳转到HeroDetailComponent的参数，为了在HeroDetailComponnet中可视化编辑**id**号为15的英雄。\r\n**localhost:3000/hero/15**\r\n\r\n有时候我们希望在路由请求中添加一个可选择的信息。例如：HerolistComponent不需要显示英雄列表。 如果以前观看的英雄是预先选定的，当从HeroDetailComponent返回时，可能会很好用。\r\n![](https://angular.io/resources/images/devguide/router/selected-hero.png)\r\n\r\n当我们从HeroDetailComponent返回时，如果能在URL中包含英雄Magneta's的 **id**，我们就可以把这个我们梦寐以求的场景变为可能。\r\n\r\n可选的信息采用其它的格式。搜索标准通常是松散的，例如： \r\n```javascript\r\n name='wind*'。 \r\n```\r\n多数的数值格式都类似 \r\n```javascript\r\nafter='12/31/2015' & before='1/1/2017' \r\n```\r\n没有特别的顺序 \r\n```javascript\r\nbefore='1/1/2017' & after='12/31/2015' \r\n```\r\n在各种格式中 \r\n```javascript\r\nduring='currentYear'。\r\n```\r\n\r\n当用户点击后退按钮，HeroDetailComponent构建另一个链接参数数组，这个数组曾经用来跳转回HeroListComponent组件。\r\n```javascript\r\ngotoHeroes() {\r\n\r\n  // Like <a [routerLink]=\"['Heroes']\">Heroes</a>\r\n\r\n  this._router.navigate(['Heroes']);\r\n\r\n}\r\n```\r\n现在我们有了一个理由。我们把当前英雄的id和导航请求一起发送，用来在HeroListComponent的英雄列表中相应的高亮显示这个id的英雄名字。\r\n\r\n我们处理一个路由参数对象就和我们之前的方法一样。我们也定义一个会被HeroListComponent忽略的垃圾参数(foo)。下面是修正后的当行状态：\r\n```javascript\r\nthis._router.navigate(['Heroes',  {id: heroId, foo: 'foo'} ]);\r\n```\r\n程序依然在运行。点击下‘后退’返回英雄列表视图。\r\n\r\n看一下浏览器地址栏。它是这么显示的：\r\n\r\nlocalhost:3000/heroes?id=15&foo=foo\r\n\r\n>  不幸的是，浏览器的地址栏没有改变当程序运行在plunker中。\r\n>  你可以把我们的话给它或者把这个例子从plunker中下载下来，解压它，用浏览器打开index.html。你>  也可以把它作为一个本地的服务器像http-server 或者 lite-server。\r\n\r\nid的值出现在查询字符串中（?id=15&foo=foo），不在URL路径中。“Heroes”的路由路径并没有:id这个标识。\r\n        >  路由在路由参数对象中，把路由路径标识替换为相应的值。不消耗在查询字符串中的路由路径中的每个参数。\r\n\r\n### 在路由参数服务中的查询参数\r\n英雄列表没有变化。没有英雄的名字是高亮显示的。\r\n\r\n这个例子中被选中的英雄是高亮的，以为它演示了程序最终的状态，包括以上我们的所有步骤。这个时候我们描述这些步骤之前的步骤。\r\n\r\nHeroListComponent期待任何参数，但不知道该怎么做。让我们改变一下。\r\n\r\n当页面从HeroListComponent跳转到HeroDetailComponent时，路由挑选了路由参数数组，并且在RouteParams服务的HeroDetailComponent中可用。我们把这个服务注射到HeroDetailComponent的构造函数中。\r\n\r\n这次我们将要跳转到不同的方向，从HeroDetailComponent跳到HeroListComponent。此时我们将把RouteParams服务注射HeroListComponent的构造函数中。\r\n\r\n首先我们扩展下路由，导入包含RouteParams服务标志的声明。\r\n**hero-list.component.ts(import)**\r\n```javascript\r\nimport {Router, RouteParams} from’angular2/router’;\r\n```\r\n然后我们扩展构造函数来注射RouteParams服务，提取参数id作为_selectedId:\r\n\r\n**hero-list.component.ts(constructor)**\r\n```javascript\r\nconstructor(\r\n\r\n  private _service: HeroService,\r\n\r\n  private _router: Router,\r\n\r\n  routeParams: RouteParams) {\r\n\r\n    this._selectedId = +routeParams.get(‘id’);\r\n\r\n}\r\n```\r\n所有的路由参数都是字符串。加号（+）在routeParameter.get前表示这是一个JavaScript中将字符串型转换为整形的小技巧。\r\n\r\n我们添加一个isSelected方法返回true，当一个英雄id匹配选择的id时。\r\n\r\n**hero-list.component.ts(constructor)**\r\n```javascript\r\nisSelected(hero: Hero) { return hero.id === this._selectedId; }\r\n```\r\n呈现被选中状态:\r\n\r\n我们用带有Class Binding的isSelected方法更新我们的模板。当isSelected方法返回true，绑定添加CSS 类selected，当返回false就解绑这个类。查看一下它在循环的<li>标签中的展示：\r\n\r\n**hero-list.component.ts(template)**\r\n```javascript\r\ntemplate: `\r\n\r\n  <h2>HEROES</h2>\r\n\r\n  <ul>\r\n\r\n    <li *ngFor='#hero of heroes'\r\n\r\n      [class.selected]='isSelected(hero)'\r\n\r\n      (click)='onSelect(hero)'>\r\n\r\n      <span class='badge'>{{hero.id}}</span> {{hero.name}}\r\n\r\n    </li>\r\n\r\n  </ul>\r\n\r\n`\r\n```\r\n\r\n最终，我们添加selected类到我们的CSS样式上。\r\n\r\n**styles.css(selected class)**\r\n```javascript\r\n.selected { background-color: #EEE; color: #369; }\r\n```\r\n当用户从英雄列表到“Magneta”编辑页面，并且返回时，“Magneta”呈现被选中状态：\r\n\r\n![](https://angular.io/resources/images/devguide/router/selected-hero.png)\r\n\r\nfoo查询字段是无害的并继续被忽略。\r\n\r\n### 子路由和查询参数\r\n我们也可以定义查询参数给子路由。\r\n\r\n技术实现都是一样的。事实上，我们在Crisis Center中做了同样的改变。确认Hero组件和CrisisCenter组件类似，分列排布方便比较：\r\n\r\n**hero-list.component.ts**\r\n```javascript\r\nimport {Component, OnInit}   from 'angular2/core';\r\n\r\nimport {Hero, HeroService}   from './hero.service';\r\n\r\nimport {Router, RouteParams} from 'angular2/router';\r\n\r\n@Component({\r\n\r\n  template: `\r\n\r\n   <h2>HEROES&lt;/h2>\r\n\r\n   <ul>\r\n\r\n     <li *ngFor='#hero of heroes'\r\n\r\n        [class.selected]='isSelected(hero)'\r\n\r\n        (click)='onSelect(hero)'>\r\n\r\n       <span class='badge'>{{hero.id}}&lt;/span> {{hero.name}}\r\n\r\n     </li>\r\n\r\n   </ul>\r\n\r\n  `\r\n\r\n})\r\n\r\nexport class HeroListComponent implements OnInit {\r\n\r\n  heroes: Hero[];\r\n\r\n  private _selectedId: number;\r\n\r\n  constructor(\r\n\r\n    private _service: HeroService,\r\n\r\n    private _router: Router,\r\n\r\n    routeParams: RouteParams) {\r\n\r\n      this._selectedId = +routeParams.get('id');\r\n\r\n  }\r\n\r\n  isSelected(hero: Hero) { return hero.id === this._selectedId; }\r\n\r\n  onSelect(hero: Hero) {\r\n\r\n    this._router.navigate( ['HeroDetail', { id: hero.id }] );\r\n\r\n  }\r\n\r\n  ngOnInit() {\r\n\r\n    this._service.getHeroes().then(heroes => this.heroes = heroes)\r\n\r\n  }\r\n\r\n}\r\n```\r\n \r\n\r\n**crisis-list.component.ts**\r\n```javascript\r\nimport {Crisis, CrisisService} from './crisis.service';\r\n\r\nimport {Router, RouteParams} from 'angular2/router';\r\n\r\n @Component({\r\n\r\n  template: `\r\n\r\n   <ul>\r\n\r\n     <li *ngFor=\"#crisis of crises\"\r\n\r\n        [class.selected]=\"isSelected(crisis)\"\r\n\r\n        (click)=\"onSelect(crisis)\">\r\n\r\n       <span class=\"badge\">{{crisis.id}}&lt;/span> {{crisis.name}}\r\n\r\n     </li>\r\n\r\n   </ul>\r\n\r\n  `,\r\n\r\n})\r\n\r\nexport class CrisisListComponent implements OnInit {\r\n\r\n  crises: Crisis[];\r\n\r\n  private _selectedId: number;\r\n\r\n  constructor(\r\n\r\n    private _service: CrisisService,\r\n\r\n    private _router: Router,\r\n\r\n    routeParams: RouteParams) {\r\n\r\n      this._selectedId = +routeParams.get('id');\r\n\r\n  }\r\n\r\n  isSelected(crisis: Crisis) { return crisis.id === this._selectedId; }\r\n\r\n  ngOnInit() {\r\n\r\n    this._service.getCrises().then(crises => this.crises = crises);\r\n\r\n  }\r\n\r\n  onSelect(crisis: Crisis) {\r\n\r\n    this._router.navigate( ['CrisisDetail', { id: crisis.id }]  );\r\n\r\n  }\r\n\r\n}\r\n```\r\n**hero-detail.component.ts**\r\n```javascript\r\nimport {Component,  OnInit}  from 'angular2/core';\r\n\r\nimport {Hero, HeroService}   from './hero.service';\r\n\r\nimport {RouteParams, Router} from 'angular2/router';\r\n\r\n@Component({\r\n\r\n  template: `\r\n\r\n <h2>HEROES</h2>\r\n\r\n <div *ngIf=\"hero\">\r\n\r\n   <h3>\"{{hero.name}}\"</h3>\r\n\r\n   <div>\r\n\r\n     <label>Id:</label>{{hero.id}}</div>\r\n\r\n   <div>\r\n\r\n     <label>Name:</label>\r\n\r\n     <input [(ngModel)]=\"hero.name\" placeholder=\"name\"/>\r\n\r\n   </div>\r\n\r\n   <button (click)=\"gotoHeroes()\">Back</button>\r\n\r\n </div>\r\n\r\n  `,\r\n\r\n})\r\n\r\nexport class HeroDetailComponent implements OnInit  {\r\n\r\n  hero: Hero;\r\n\r\n  constructor(\r\n\r\n    private _router:Router,\r\n\r\n    private _routeParams:RouteParams,\r\n\r\n    private _service:HeroService){}\r\n\r\n  ngOnInit() {\r\n\r\n    let id = this._routeParams.get('id');\r\n\r\n    this._service.getHero(id).then(hero => this.hero = hero);\r\n\r\n  }\r\n\r\n  gotoHeroes() {\r\n\r\n    let heroId = this.hero ? this.hero.id : null;\r\n\r\n    // Pass along the hero id if available\r\n\r\n    // so that the HeroList component can select that hero.\r\n\r\n    // Add a totally useless `foo` parameter for kicks.\r\n\r\n    this._router.navigate(['Heroes',  {id: heroId, foo: 'foo'} ]);\r\n\r\n  }\r\n\r\n}\r\n```\r\n**crisis-detail.component.ts**\r\n```javascript\r\nimport {Component, OnInit} from 'angular2/core';\r\n\r\nimport {Crisis, CrisisService} from './crisis.service';\r\n\r\nimport {RouteParams, Router} from 'angular2/router';\r\n\r\nimport {CanDeactivate, ComponentInstruction} from 'angular2/router';\r\n\r\nimport {DialogService} from '../dialog.service';\r\n \r\n\r\n@Component({\r\n\r\n  template: `\r\n\r\n  <div *ngIf=\"crisis\">\r\n\r\n    <h3>\"{{editName}}\"</h3>\r\n\r\n    <div>\r\n\r\n      <label>Id: </label>{{crisis.id}}</div>\r\n\r\n    <div>\r\n\r\n      <label>Name: </label>\r\n\r\n      <input [(ngModel)]=\"editName\" placeholder=\"name\"/>\r\n\r\n    </div>\r\n\r\n    <button (click)=\"save()\">Save</button>\r\n\r\n    <button (click)=\"cancel()\">Cancel</button>\r\n\r\n  </div>\r\n\r\n  `,\r\n\r\n  styles: ['input {width: 20em}']\r\n\r\n})\r\n\r\n\r\nexport class CrisisDetailComponent implements OnInit, CanDeactivate {\r\n\r\n  crisis: Crisis;\r\n\r\n  editName: string;\r\n\r\n  constructor(\r\n\r\n    private _service: CrisisService,\r\n\r\n    private _router: Router,\r\n\r\n    private _routeParams: RouteParams,\r\n\r\n    private _dialog: DialogService\r\n\r\n    ) { }\r\n\r\n  ngOnInit() {\r\n\r\n    let id = +this._routeParams.get('id');\r\n\r\n    this._service.getCrisis(id).then(crisis => {\r\n\r\n      if (crisis) {\r\n\r\n        this.editName = crisis.name;\r\n\r\n        this.crisis = crisis;\r\n\r\n      } else { // id not found\r\n\r\n        this.gotoCrises();\r\n\r\n      }\r\n\r\n    });\r\n\r\n  }\r\n\r\n routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) : any {\r\n\r\n    // Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged.\r\n\r\n    if (!this.crisis || this.crisis.name === this.editName) {\r\n\r\n      return true;\r\n\r\n    }\r\n\r\n    // Otherwise ask the user with the dialog service and return its\r\n\r\n    // promise which resolves to true or false when the user decides\r\n\r\n    return this._dialog.confirm('Discard changes?');\r\n\r\n  }\r\n\r\n  cancel() {\r\n\r\n    this.editName = this.crisis.name;\r\n\r\n    this.gotoCrises();\r\n\r\n  }\r\n  save() {\r\n\r\n    this.crisis.name = this.editName;\r\n\r\n    this.gotoCrises();\r\n\r\n  }\r\n  gotoCrises() {\r\n\r\n    let crisisId = this.crisis ? this.crisis.id : null;\r\n\r\n    // Pass along the hero id if available\r\n\r\n    // so that the CrisisListComponent can select that hero.\r\n\r\n    // Add a totally useless `foo` parameter for kicks.\r\n\r\n    this._router.navigate(['CrisisCenter',  {id: crisisId, foo: 'foo'} ]);\r\n\r\n  }\r\n\r\n}\r\n``` \r\n当我们从一个CrisisDetailComponent返回，并展示 Asteroid危机任务。我们在列表中看到被选中的危机的样式：\r\n![](https://angular.io/resources/images/devguide/router/selected-crisis.png)\r\n 再看下浏览器中的地址栏。它变了，它看起来是：\r\nlocalhost:3000/crisis-center/;id=3;foo=foo\r\n查询字符串并不仅被分隔符**’?’**，**‘&’**分开。还有分号(**;**)可以作为分隔符。矩阵网址符号，这个是我们以前没有看到过的一些东西。\r\n\r\n>  矩阵网址符号这个想法浮现于1996建议，来自web创始人 Tim Berners-Lee。虽然矩阵符号虽然矩阵\r\n>  表示从未到HTML标准，它是合法的，它成为流行的浏览器之间的路由系统作为一种方式来隔离参数\r\n>  属于父母和孩子的路线。角分量路由器就是这样一个系统。\r\n\r\n对我们来说，语法可能看起来有些奇怪，但用户不太可能注意或注意只要URL可以直接粘贴到浏览器地址栏正如这一个可以。\r\n\r\n\r\n## 总结\r\n在我们本章的结尾，我们看一下完整的应用。\r\n我们可以测试这个实例并且在这里下载源代码。\r\n我们最终的项目的目录结构如下：\r\n\r\n \r\n\r\n## 顶级项目文件如下:\r\napp.component.ts\r\n```javascript\r\nimport {Component} from ‘angular2/core‘;\r\nimport {RouteConfig, ROUTER_DIRECTIVES} from ‘angular2/router‘;\r\nimport {CrisisCenterComponent} from ‘./crisis-center/crisis-center.component‘;\r\nimport {HeroListComponent}     from ‘./heroes/hero-list.component‘;\r\nimport {HeroDetailComponent}   from ‘./heroes/hero-detail.component‘;\r\n@Component({\r\n  selector: ‘my-app‘,\r\n  template: `\r\n    &lt;h1 class=&quot;title&quot;&gt;Component Router&lt;/h1&gt;\r\n    &lt;a [routerLink]=&quot;[‘CrisisCenter‘]&quot;&gt;Crisis Center&lt;/a&gt;\r\n    &lt;a [routerLink]=&quot;[‘Heroes‘]&quot;&gt;Heroes&lt;/a&gt;\r\n    &lt;router-outlet&gt;&lt;/router-outlet&gt;\r\n  `,\r\n  directives: [ROUTER_DIRECTIVES]\r\n})\r\n@RouteConfig([\r\n  { // Crisis Center child route\r\n    path: ‘/crisis-center/...‘,\r\n    name: ‘CrisisCenter‘,\r\n    component: CrisisCenterComponent,\r\n    useAsDefault: true\r\n  },\r\n  {path: ‘/heroes‘,   name: ‘Heroes‘,     component: HeroListComponent},\r\n  {path: ‘/hero/:id‘, name: ‘HeroDetail‘, component: HeroDetailComponent},\r\n  {path: ‘/disaster‘, name: ‘Asteroid‘, redirectTo: [‘CrisisCenter‘, ‘CrisisDetail‘, {id:3}]}\r\n])\r\nexport class AppComponent { }\r\n\r\nimport {bootstrap}        from ‘angular2/platform/browser‘;\r\nimport {ROUTER_PROVIDERS} from ‘angular2/router‘;\r\nimport {AppComponent}     from ‘./app.component‘;\r\nimport {DialogService}    from ‘./dialog.service‘;\r\nimport {HeroService}      from  ‘./heroes/hero.service‘;\r\n```\r\nboot.ts\r\n```javascript\r\nbootstrap(AppComponent, [\r\n  ROUTER_PROVIDERS,\r\n  DialogService,\r\n  HeroService\r\n]);\r\n```\r\ndialog.service.ts\r\n```javascript\r\nimport {Injectable} from ‘angular2/core‘;\r\n/**\r\n * Async modal dialog service\r\n * DialogService makes this app easier to test by faking this service.\r\n * TODO: better modal implemenation that doesn‘t use window.confirm\r\n */\r\n@Injectable()\r\nexport class DialogService {\r\n  /**\r\n   * Ask user to confirm an action. `message` explains the action and choices.\r\n   * Returns promise resolving to `true`=confirm or `false`=cancel\r\n   */\r\n  confirm(message?:string) {\r\n    return new Promise&lt;boolean&gt;((resolve, reject) =&gt;\r\n      resolve(window.confirm(message || ‘Is it OK?‘)));\r\n  };\r\n}\r\n```\r\nindex.html\r\n```javascript\r\n<html>\r\n  <head>\r\n    <base href=&quot;/&quot;>\r\n    <title>Router Sample</title>\r\n    <link rel=&quot;stylesheet&quot; href=&quot;styles.css&quot;>\r\n    <script src=&quot;node_modules/angular2/bundles/angular2-polyfills.js&quot;></script>\r\n    <script src=&quot;node_modules/systemjs/dist/system.src.js&quot;></script>\r\n    <script src=&quot;node_modules/rxjs/bundles/Rx.js&quot;></script>\r\n    <script src=&quot;node_modules/angular2/bundles/angular2.dev.js&quot;></script>\r\n    <script src=&quot;node_modules/angular2/bundles/router.dev.js&quot;></script>\r\n    <script>\r\n      System.config({\r\n        packages: {       \r\n          app: {\r\n            format: ‘register‘,\r\n            defaultExtension: ‘js‘\r\n          }\r\n        }\r\n      });\r\n      System.import(‘app/boot‘)\r\n            .then(null, console.error.bind(console));\r\n    </script>\r\n  </head>\r\n  <body>\r\n    <my-app>loading...</my-app>\r\n  </body>\r\n</html>\r\n```\r\nstyles.css\r\n```javascript\r\nh1 {color: #369; font-family: Arial, Helvetica, sans-serif; font-size: 250%;}\r\nh2 { color: #369; font-family: Arial, Helvetica, sans-serif;  }\r\nh3 { color: #444; font-weight: lighter; }\r\nbody { margin: 2em; }\r\nbody, input[text], button { color: #888; font-family: Cambria, Georgia; }\r\nbutton {padding: 0.2em; font-size: 14px}\r\nul {list-style-type: none; margin-left: 1em; padding: 0; width: 20em;}\r\nli { cursor: pointer; position: relative; left: 0; transition: all 0.2s ease; }\r\nli:hover {color: #369; background-color: #EEE; left: .2em;}\r\n/* route-link anchor tags */\r\na {padding: 5px; text-decoration: none; font-family: Arial, Helvetica, sans-serif;\r\n}\r\na:visited, a:link {color: #444;}\r\na:hover {color: white; background-color: #1171a3; }\r\na.router-link-active {color: white; background-color: #52b9e9; }\r\n.selected { background-color: #EEE; color: #369; }\r\n.badge {\r\n  font-size: small;\r\n  color: white;\r\n  padding: 0.1em 0.7em;\r\n  background-color: #369;\r\n  line-height: 1em;\r\n  position: relative;\r\n  left: -1px;\r\n  top: -1px;\r\n}\r\n```\r\n危机中心\r\n危机中心模块包含crisis-center文件如下：\r\ncrisis.service.ts\r\n```javascript\r\nimport {Component}     from 'angular2/core';\r\nimport {RouteConfig, RouterOutlet} from 'angular2/router';\r\nimport {CrisisListComponent}   from './crisis-list.component';\r\nimport {CrisisDetailComponent} from './crisis-detail.component';\r\nimport {CrisisService}         from './crisis.service';\r\n@Component({\r\n  template:  `\r\n    <h2>CRISIS CENTER</h2>\r\n    <router-outlet></router-outlet>\r\n  `,\r\n  directives: [RouterOutlet],\r\n  providers:  [CrisisService]\r\n})\r\n@RouteConfig([\r\n  {path:'/',         name: 'CrisisCenter', component: CrisisListComponent, useAsDefault: true},\r\n  {path:'/:id',      name: 'CrisisDetail', component: CrisisDetailComponent},\r\n  {path:'/list/:id', name: 'CrisisList',   component: CrisisListComponent}\r\n])\r\nexport class CrisisCenterComponent { }\r\n```\r\ncrisis-list.component.ts\r\n```javascript\r\nimport {Component, OnInit} from 'angular2/core';\r\nimport {Crisis, CrisisService} from './crisis.service';\r\nimport {Router, RouteParams} from 'angular2/router';\r\n@Component({\r\n  template: `\r\n    <ul>\r\n      <li *ngFor=\"#crisis of crises\"\r\n        [class.selected]=\"isSelected(crisis)\"\r\n        (click)=\"onSelect(crisis)\">\r\n        <span class=\"badge\">{{crisis.id}}</span> {{crisis.name}}\r\n      </li>\r\n    </ul>\r\n  `,\r\n})\r\nexport class CrisisListComponent implements OnInit {\r\n  public crises: Crisis[];\r\n  private _selectedId: number;\r\n  constructor(\r\n    private _service: CrisisService,\r\n    private _router: Router,\r\n    routeParams: RouteParams) {\r\n      this._selectedId = +routeParams.get('id');\r\n  }\r\n  ngOnInit() {\r\n    this._service.getCrises().then(crises => this.crises = crises);\r\n  }\r\n  isSelected(crisis: Crisis) { return crisis.id === this._selectedId; }\r\n  onSelect(crisis: Crisis) {\r\n    this._router.navigate( ['CrisisDetail', { id: crisis.id }]  );\r\n  }\r\n}\r\n```\r\ncrisis-detail.component.ts\r\n```javascript\r\nimport {Component, OnInit} from 'angular2/core';\r\nimport {Crisis, CrisisService} from './crisis.service';\r\nimport {RouteParams, Router} from 'angular2/router';\r\nimport {CanDeactivate, ComponentInstruction} from 'angular2/router';\r\nimport {DialogService} from '../dialog.service';\r\n@Component({\r\n  template: `\r\n  <div *ngIf=\"crisis\">\r\n    <h3>\"{{editName}}\"</h3>\r\n    <div>\r\n      <label>Id: </label>{{crisis.id}}</div>\r\n    <div>\r\n      <label>Name: </label>\r\n      <input [(ngModel)]=\"editName\" placeholder=\"name\"/>\r\n    </div>\r\n    <button (click)=\"save()\">Save</button>\r\n    <button (click)=\"cancel()\">Cancel</button>\r\n  </div>\r\n  `,\r\n  styles: ['input {width: 20em}']\r\n})\r\nexport class CrisisDetailComponent implements OnInit, CanDeactivate {\r\n  public crisis: Crisis;\r\n  public editName: string;\r\n  constructor(\r\n    private _service: CrisisService,\r\n    private _router: Router,\r\n    private _routeParams: RouteParams,\r\n    private _dialog: DialogService\r\n    ) { }\r\n  ngOnInit() {\r\n    let id = +this._routeParams.get('id');\r\n    this._service.getCrisis(id).then(crisis => {\r\n      if (crisis) {\r\n        this.editName = crisis.name;\r\n        this.crisis = crisis;\r\n      } else { // id not found\r\n        this.gotoCrises();\r\n      }\r\n    });\r\n  }\r\n  routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {\r\n    // Allow navigation (`true`) if no crisis or the crisis is unchanged.\r\n    // Otherwise ask the user with the dialog service and return its\r\n    // promise which resolves true-or-false when the user decides\r\n    return !this.crisis ||\r\n           this.crisis.name === this.editName ||\r\n           this._dialog.confirm('Discard changes?');\r\n  }\r\n  cancel() {\r\n    this.editName = this.crisis.name;\r\n    this.gotoCrises();\r\n  }\r\n   save() {\r\n    this.crisis.name = this.editName;\r\n    this.gotoCrises();\r\n  }\r\n  gotoCrises() {\r\n    let route =\r\n      ['CrisisList',  {id: this.crisis ? this.crisis.id : null} ]\r\n    this._router.navigate(route);\r\n  }\r\n}\r\n```\r\ncrisis.service.ts\r\n```javascript\r\nimport {Injectable} from 'angular2/core';\r\nexport class Crisis {\r\n  constructor(public id: number, public name: string) { }\r\n}\r\n@Injectable()\r\nexport class CrisisService {\r\n  getCrises() { return crisesPromise; }\r\n  getCrisis(id: number | string) {\r\n    return crisesPromise\r\n      .then(crises => crises.filter(c => c.id === +id)[0]);\r\n  }\r\n}\r\nvar crises = [\r\n  new Crisis(1, 'Princess Held Captive'),\r\n  new Crisis(2, 'Dragon Burning Cities'),\r\n  new Crisis(3, 'Giant Asteroid Heading For Earth'),\r\n  new Crisis(4, 'Release Deadline Looms')\r\n];\r\nvar crisesPromise = Promise.resolve(crises);\r\n```\r\n英雄模块\r\n英雄模块目录结构如下:\r\nhero-list.component.ts\r\n```javascript\r\n// TODO SOMEDAY: Feature Componetized like HeroCenter\r\nimport {Component, OnInit}   from 'angular2/core';\r\nimport {Hero, HeroService}   from './hero.service';\r\nimport {Router}              from 'angular2/router';\r\n@Component({\r\n  template: `\r\n    <h2>HEROES</h2>\r\n    <ul>\r\n      <li *ngFor=\"#hero of heroes\"\r\n        (click)=\"onSelect(hero)\">\r\n        <span class=\"badge\">{{hero.id}}</span> {{hero.name}}\r\n      </li>\r\n    </ul>\r\n  `\r\n})\r\nexport class HeroListComponent implements OnInit {\r\n  public heroes: Hero[];\r\n  public selectedHero: Hero;\r\n  constructor(\r\n    private _router: Router,\r\n    private _service: HeroService) { }\r\n  ngOnInit() {\r\n    this._service.getHeroes().then(heroes => this.heroes = heroes)\r\n  }\r\n  onSelect(hero: Hero) {\r\n    this._router.navigate( ['HeroDetail', { id: hero.id }] );\r\n  }\r\n}\r\n```\r\nhero-detail.component.ts\r\n```javascript\r\nimport {Component,  OnInit}  from 'angular2/core';\r\nimport {Hero, HeroService}   from './hero.service';\r\nimport {RouteParams, Router} from 'angular2/router';\r\n@Component({\r\n  template: `\r\n  <h2>HEROES</h2>\r\n  <div *ngIf=\"hero\">\r\n    <h3>\"{{hero.name}}\"</h3>\r\n    <div>\r\n      <label>Id: </label>{{hero.id}}</div>\r\n    <div>\r\n      <label>Name: </label>\r\n      <input [(ngModel)]=\"hero.name\" placeholder=\"name\"/>\r\n    </div>\r\n    <button (click)=\"gotoHeroes()\">Back</button>\r\n  </div>\r\n  `,\r\n})\r\nexport class HeroDetailComponent implements OnInit  {\r\n  public hero: Hero;\r\n  constructor(\r\n    private _router:Router,\r\n    private _routeParams:RouteParams,\r\n    private _service:HeroService){}\r\n  ngOnInit() {\r\n    let id = this._routeParams.get('id');\r\n    this._service.getHero(id).then(hero => this.hero = hero);\r\n  }\r\n\r\n  gotoHeroes() {\r\n    // <a [routerLink]=\"['Heroes']\">Heroes</a>\r\n    this._router.navigate(['Heroes']);\r\n  }\r\n}\r\n```\r\nhero.service.ts\r\n```javascript\r\nimport {Injectable} from 'angular2/core';\r\nexport class Hero {\r\n  constructor(public id: number, public name: string) { }\r\n}\r\n@Injectable()\r\nexport class HeroService {\r\n  getHeroes() { return heroesPromise; }\r\n  getHero(id: number | string) {\r\n    return heroesPromise\r\n      .then(heroes => heroes.filter(h => h.id === +id)[0]);\r\n  }\r\n}\r\nvar HEROES = [\r\n    new Hero(11, 'Mr. Nice'),\r\n    new Hero(12, 'Narco'),\r\n    new Hero(13, 'Bombasto'),\r\n    new Hero(14, 'Celeritas'),\r\n    new Hero(15, 'Magneta'),\r\n    new Hero(16, 'RubberMan')\r\n];\r\nvar heroesPromise = Promise.resolve(HEROES);\r\n```\r\n \r\n\r\n## 附录\r\n本章的平衡是一个集附录，详细阐述了我们上面提到了一些要点。\r\n附录材料不是必不可少的。请保持好奇心继续阅读下去。\r\n## 链接参数数组\r\n我们好多次提到链接参数数组。我们也多次使用过它。\r\n我们在RouterLink指令上如下方式绑定数组:\r\n```javascript\r\n<a [routerLink]=”[‘Heroes’]”>Heroes</a>\r\n```\r\n我们已经写过有2个元素的数组，当如下指定一个路由参数时：\r\n```javascript\r\nthis._router.navigate([‘HeroDetail’,{id:hero.id}]);\r\n```\r\n以上的2个例子能包括我们的需求，在一个含有一级路由的app中。我们加入子路由选择器时，例如危机中心，我们可以创建一个新的链接数组。\r\n我们指定一个默认的子路由给危机中心，使RouterLink妥妥的。\r\n```javascript\r\n<a [routerLink]=”[‘CrisisCenter’]”>Crisis Center</a>\r\n```\r\n如果我们不指定一个默认的路由，我们这个有一个元素的数组将会失败，因为我们没告诉路由选择器要使用哪个子路由。\r\n//The link now fails with a “non-terminal link” error\r\n```javascript\r\n<a [routerLink]=”[CrisisCenter‘’]”>Crisis Center</a>\r\n```\r\n我们不需要像下面一样写一个锚节点：\r\n```javascript\r\n<a [routerLink]=”[‘CrisisCenter’,’CrisisCenter’]”>Crisis Center</a>\r\n```\r\n嗯? Crisis Center, Crisis Center这看起来像个路由危机！\r\n不过它确实有意义。我们解读一下：\r\n* 数组中第一个元素定义了父路由\r\n* 这没有给路由参数，所以我们这里不处理它\r\n* 这里没有给默认子路由，所以我们需要去挑选一个子路由。\r\n* 我们决定跳转到CrisisListComponent这个组件，这个组件名字恰好也叫‘CrisisCenter’\r\n* 所以我们在数组中添加’ CrisisCenter’作为第二个元素\r\n* Voila！！！！[‘CrisisCenter’,’CrisisCenter’]\r\n\r\n让我们再进一步。这时我们要建立一个链接参数数组用来从应用的根组件跳转到‘公主危机’组件。\r\n* 参数的一个元素定义为父路由（’CrisisCenter’）\r\n* 这没有给路由参数，所以我们这里不处理它\r\n* 第二个元素定义子路由关于特别的危机（‘CrisisDetail’）的细节\r\n* 子路由细节需要一个ID参数\r\n* 我们添加”Princess Crisis”id，作为数组的第三个参数（{id:1}）\r\n看起来如下：\r\n```javascript\r\n<a [routerLink]=”[‘CrisisCenter’,’CrisisDetail’,{id:1}]”>Princess Crisis</a>\r\n```\r\n我们可以用危机中心的路由重新定义AppComponent模板：\r\n```javascript\r\ntemplate: `\r\n  <h1 class=\"title\">Component Router</h1>\r\n  <a [routerLink]=\"['CrisisCenter', 'CrisisCenter']\">Crisis Center</a>\r\n  <a [routerLink]=\"['CrisisCenter', 'CrisisDetail', {id:1}]\">Princess Crisis</a>\r\n  <a [routerLink]=\"['CrisisCenter', 'CrisisDetail', {id:2}]\">Dragon Crisis</a>\r\n  <router-outlet></router-outlet>\r\n` \r\n```\r\n\r\n## 链接参数数组重定向\r\n那如果我们不用RouterLink指令来构造锚节点呢？那如果我们想添加了一个灾难路由在顶级路由配置中呢？\r\n我们居然可以这么做！！！\r\n我们按照流程拼装一个有3个参数的链接参数数组。这次我们把id设置给’Asteroid Crisis‘（{id:3}）。\r\n我们不能定义一个普通的路由，因为那需要设置一个目标组件。我们也不为组件定义一个路由。我们定义一个跳转到路由的路由。一个跳转到路由的路由就叫做重定向。这个重定向路由，我们会加入到配置中。\r\n```javascript\r\n{path:’/disaster’,name:’Asteroid’,redirectTo:[‘CrisisCenter’,’CrisisDetail’,{id:3}]}\r\n```\r\n我们希望构想能清晰些。我们写一个app，带有一，二和更多层级的路由。链路参数数组提供了灵活性，以表示任何路由的深度和任何合法路径名称（可选）路径参数对象。\r\n\r\n## 附加：为什么使用 ngOnInit method\r\n我们在组件的很多类中使用了ngOnInit方法。像是在HeroDetailComponent中使用那样。我们用ngOnInit逻辑代替构造函数。我们不需要理由，这个理由是可测试的。\r\n一个有主要副作用的构造函数很难测试，因为它很快就开始做事在我们穿件一个测试实例\r\n。在这种情况下，它可能会向远程服务器提出请求，他不应该做测试。在测试环境中，它甚至是不可能被测试的。\r\n比较好的方式是限制构造函数的功能。大多数情况下，它会把参数藏在本地并执行简单的实例配置。\r\n现在我们想拿到这个类的一个实例，在它一创建用来从HeroService获取英雄数据。我们怎么能保证不用构造函数也能做到这一点。\r\nAngular会检测当一个组件有某一个像生命周期的方法ngOnInit或ngOnDestroy方法并在适当的时候调用他们\r\nAngular将会调用ngOnInit函数当我们跳转到HeroDetailComponent。我们从RouterParams获得id并且请求服务器获得相对于id的英雄信息。\r\n如果我们愿意也可以调用ngOnInit方法在我们的测试中，在我们控制了注射器中的HeroService服务，就可以笑笑它了。\r\n\r\n## 附录：浏览器URL样式\r\n       当路由跳转到一个新的视图组件，它就会用视图的URL更新浏览器当前和历史信息。 这是一个严格的本地URL。浏览器不应该发送给服务器需求也不应该重新加载页面。\r\n       现在我们谈论关于浏览器url并不是我们在RouteDefinition中记录的路由url。浏览器URL是我们粘贴在浏览器地址栏和发送email时用的，因此他们能建立应用的深层链接。\r\n         HTML5模型浏览器支持history.pushState，一种不需要触发向服务器请求就能改变浏览器本地和历史url记录。路由选择器可以组合一个’自然的‘URL，和其它需求网页所用的URL没什么不同。\r\n         以下是 危机中心 URL在’HTML5 pushState‘的样式：\r\n         localhost:3002/crisis-center/\r\n        当本地的URL改变时，较老的浏览器向服务器发送网页请求，除非改变后的url后面有一个’#‘（称作“哈希”）。路由选择器更加先进的用哈希来组成应用中所需要的URL。这里有一个‘hash URL‘用来跳转到 危机中心。\r\nlocalhost:3002/src/#/crisis-center/\r\nAngular组件路由支持之上2中类型。我们可以在引导程序中设定我们喜欢的的样式提供给LocationStrategy。\r\n在Dependency Injection chapter中学习’providers‘和引导程序相关知识。\r\n\r\n## 哪种策略最好？\r\n我们必须选择一个策略，我们需要再我们程序中调用正确的方法。一旦应用程序上线，这个策略不会被轻易修改，外界将会有非常多的URL指定到该应用。\r\n大多数的Angular2程序应该使用默认的HTML5类型。它生成的URL用户更容易理解。并且它会对服务器端的绘制有保护选择功能。\r\n\r\n在服务器生成一个临界页面，这个技术大大提高app被首次加载的速度。否则一个app要用10秒或者更长的时间来启动，使用服务器端生成并发送到用户设备耗时不超过1秒。\r\n这种优化选择只能被用在应用程序URL看起来像普通的webURL并没有哈希（#）符号时使用。\r\n坚持只用默认路由，除非你有一个令人信服的去使用哈希路由。\r\n\r\n## HTML5 URLS和<base href>\r\n路由默认使用’HTML5 pushState‘。我们不需要提供路由的PathLocationStrategy因为它会自动加载。\r\n我们必须在index.html中的<head>标签中添加<base href>元素标签。\r\n<base href=”/”>\r\n没有那个标签，浏览器可能不会加载资源文件(图片，css，脚本)当’深链接‘接入app时。糟糕的事情将会在用户把应用url粘贴在浏览器地址栏中或者在email中点击这样的链接是发生。\r\n有些开发者也许不会添加<base>元素，或许因为他们没有访问<head>或者index.html的权限。\r\n那些开发者仍然使用HTML5 URLs，通过2种补救的步骤：\r\n1.提供一个带有适当值的APP_BASE_HREF的路由选择器\r\n2.所有的web资源都使用绝对地址，比如：css，图片，脚本和html模板文件。\r\n在API指南中学习APP_BASE_HREF\r\n## 本地哈希地址\r\n在应用引导期间，把HashLocationStrategy作为路由选择器的LocationStrategy，我们能重返老学校。\r\n这个意思是从路由选择器中导入provide，为依赖注入、Location和hashLoacationStrategy，然后在调用中提供给bootstrap这个策略。\r\n```javascript\r\nimport {bootstrap} from 'angular2/platform/browser';\r\nimport {ROUTER_PROVIDERS}  from 'angular2/router';\r\nimport {AppComponent} from './app.component';\r\n\r\n// Add these symbols to register a `LocationStrategy`\r\nimport {provide}  from  'angular2/core';\r\nimport {LocationStrategy,HashLocationStrategy} from 'angular2/router';\r\nbootstrap(AppComponent, [\r\n  provide(LocationStrategy,\r\n         {useClass: HashLocationStrategy}), // ~/src/#/crisis-center/\r\n  ROUTER_PROVIDERS\r\n]);\r\n```\r\n\r\n>### 下一章\r\n> [10.生命周期钩子](https://github.com/gf-rd/blog/issues/13)"},"comment":{"url":"https://api.github.com/repos/gf-rd/blog/issues/comments/180202705","html_url":"https://github.com/gf-rd/blog/issues/28#issuecomment-180202705","issue_url":"https://api.github.com/repos/gf-rd/blog/issues/28","id":180202705,"user":{"login":"helloyxw","id":5844826,"avatar_url":"https://avatars.githubusercontent.com/u/5844826?v=3","gravatar_id":"","url":"https://api.github.com/users/helloyxw","html_url":"https://github.com/helloyxw","followers_url":"https://api.github.com/users/helloyxw/followers","following_url":"https://api.github.com/users/helloyxw/following{/other_user}","gists_url":"https://api.github.com/users/helloyxw/gists{/gist_id}","starred_url":"https://api.github.com/users/helloyxw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/helloyxw/subscriptions","organizations_url":"https://api.github.com/users/helloyxw/orgs","repos_url":"https://api.github.com/users/helloyxw/repos","events_url":"https://api.github.com/users/helloyxw/events{/privacy}","received_events_url":"https://api.github.com/users/helloyxw/received_events","type":"User","site_admin":false},"created_at":"2016-02-05T05:18:42Z","updated_at":"2016-02-05T05:18:42Z","body":"RouterOutlet部分翻译好像有点问题。原文：A template may hold exactly one unnamed <router-outlet>.\r\n上文翻译为：一个模板可以写多个匿名的。 应该翻译为： 一个模板可以持有刚好一个未命名的<router-outlet>"}}