翻译了一部分路由

This commit is contained in:
Zhicheng Wang 2017-05-06 21:46:57 +08:00
parent 0876fb3e90
commit 8baa278399
1 changed files with 298 additions and 124 deletions

View File

@ -599,7 +599,9 @@ a#basics-summary
下面是一些*路由器*中的关键词汇及其含义: 下面是一些*路由器*中的关键词汇及其含义:
style td, th {vertical-align: top} style
td, th { vertical-align: top }
table table
tr tr
th th
@ -1422,21 +1424,21 @@ a#redirect
下面是当前里程碑中讨论过的文件列表: 下面是当前里程碑中讨论过的文件列表:
+makeTabs( +makeTabs(
`router/ts/src/app/app.component.1.ts, `router/ts/src/app/app.component.1.ts,
router/ts/src/app/app.module.1.ts, router / ts / src / app / app.module.1.ts,
router/ts/src/main.ts, router / ts / src / main.ts,
router/ts/src/app/hero-list.component.ts, router / ts / src / app / hero - list.component.ts,
router/ts/src/app/crisis-list.component.ts, router / ts / src / app / crisis - list.component.ts,
router/ts/src/app/not-found.component.ts, router / ts / src / app / not - found.component.ts,
router/ts/src/index.html`, router / ts / src / index.html`,
',,,,', ',,,,',
`app.component.ts, `app.component.ts,
app.module.ts, app.module.ts,
main.ts, main.ts,
hero-list.component.ts, hero - list.component.ts,
crisis-list.component.ts, crisis - list.component.ts,
not-found.component.ts, not - found.component.ts,
index.html`) index.html`)
.l-main-section#routing-module .l-main-section#routing-module
:marked :marked
@ -1757,7 +1759,7 @@ a#hero-routing-module
Import the hero components from their new locations in the `src/app/heroes/` folder, define the two hero routes, Import the hero components from their new locations in the `src/app/heroes/` folder, define the two hero routes,
and export the `HeroRoutingModule` class. and export the `HeroRoutingModule` class.
从新位置`src/app/heroes/`目录中导入英雄相关的组件,定义两个英雄路由,并导出`HeroRoutingModule`类。 从新位置`src/app/heroes/`目录中导入英雄相关的组件,定义两个"英雄管理"路由,并导出`HeroRoutingModule`类。
Now that you have routes for the `Heroes` module, register them with the `Router` via the Now that you have routes for the `Heroes` module, register them with the `Router` via the
`RouterModule` _almost_ as you did in the `AppRoutingModule`. `RouterModule` _almost_ as you did in the `AppRoutingModule`.
@ -1806,7 +1808,7 @@ a#remove-duplicate-hero-routes
:marked :marked
### Remove duplicate hero routes ### Remove duplicate hero routes
### 移除重复的英雄路由 ### 移除重复的"英雄管理"路由
The hero routes are currently defined in _two_ places: in the `HeroesRoutingModule`, The hero routes are currently defined in _two_ places: in the `HeroesRoutingModule`,
by way of the `HeroesModule`, and in the `AppRoutingModule`. by way of the `HeroesModule`, and in the `AppRoutingModule`.
@ -1836,54 +1838,84 @@ a#merge-hero-routes
:marked :marked
### Import hero module into AppModule ### Import hero module into AppModule
### 把英雄模块导入到AppModule ### 把"英雄管理"模块导入到AppModule
The heroes feature module is ready, but the application doesn't know about the `HeroesModule` yet. The heroes feature module is ready, but the application doesn't know about the `HeroesModule` yet.
Open `app.module.ts` and revise it as follows. Open `app.module.ts` and revise it as follows.
英雄这个特性模块已经就绪,但应用仍然不知道`HeroesModule`的存在。
打开`app.module.ts`,并按照下述步骤修改它。
Import the `HeroesModule` and add it to the `imports` array in the `@NgModule` metadata of the `AppModule`. Import the `HeroesModule` and add it to the `imports` array in the `@NgModule` metadata of the `AppModule`.
导入`HeroesModule`并且把它加到根模块`APPModule`的`@NgModule`元数据中的`imports`数组中。
Remove the `HeroListComponent` from the `AppModule`'s `declarations` because it's now provided by the `HeroesModule`. Remove the `HeroListComponent` from the `AppModule`'s `declarations` because it's now provided by the `HeroesModule`.
This is important. There can be only _one_ owner for a declared component. This is important. There can be only _one_ owner for a declared component.
In this case, the `Heroes` module is the owner of the `Heroes` components and is making them available to In this case, the `Heroes` module is the owner of the `Heroes` components and is making them available to
components in the `AppModule` via the `HeroesModule`. components in the `AppModule` via the `HeroesModule`.
从`AppModule`的`declarations`中移除`HeroListComponent`,因为它现在已经改由`HeroesModule`提供了。
这一步很重要!因为一个组件只能声明在*一个*属主模块中。
这个例子中,`Heroes`模块就是`Heroes`组件的属主模块,而`AppModule`要通过导入`HeroesModule`才能使用这些组件。
As a result, the `AppModule` no longer has specific knowledge of the hero feature, its components, or its route details. As a result, the `AppModule` no longer has specific knowledge of the hero feature, its components, or its route details.
You can evolve the hero feature with more components and different routes. You can evolve the hero feature with more components and different routes.
That's a key benefit of creating a separate module for each feature area. That's a key benefit of creating a separate module for each feature area.
最终,`AppModule`不再了解那些特定于"英雄"特性的知识,比如它的组件、路由细节等。
我们可以让"英雄"特性独立演化,添加更多的组件或各种各样的路由。
这是我们为每个特性区创建独立模块后获得的核心优势。
After these steps, the `AppModule` should look like this: After these steps, the `AppModule` should look like this:
经过这些步骤,`AppModule`变成了这样:
+makeExample('src/app/app.module.3.ts','','src/app/app.module.ts') +makeExample('src/app/app.module.3.ts','','src/app/app.module.ts')
a#routing-module-order a#routing-module-order
:marked :marked
### Module import order matters ### Module import order matters
### 导入模块的顺序很重要
Look at the module `imports` array. Notice that the `AppRoutingModule` is _last_. Look at the module `imports` array. Notice that the `AppRoutingModule` is _last_.
Most importantly, it comes _after_ the `HeroesModule`. Most importantly, it comes _after_ the `HeroesModule`.
看看该模块的`imports`数组。注意,`AppRoutingModule`是*最后一个*。最重要的是,它位于`HeroesModule`之后。
+makeExample('src/app/app.module.3.ts','module-imports','src/app/app.module.ts (module-imports)')(format='.') +makeExample('src/app/app.module.3.ts','module-imports','src/app/app.module.ts (module-imports)')(format='.')
:marked :marked
The order of route configuration matters. The order of route configuration matters.
The router accepts the first route that matches a navigation request path. The router accepts the first route that matches a navigation request path.
路由配置的顺序很重要。
路由器会接受第一个匹配上导航所要求的路径的那个路由。
When all routes were in one `AppRoutingModule`, When all routes were in one `AppRoutingModule`,
you put the default and [wildcard](#wildcard) routes last, after the `/heroes` route, you put the default and [wildcard](#wildcard) routes last, after the `/heroes` route,
so that the router had a chance to match a URL to the `/heroes` route _before_ so that the router had a chance to match a URL to the `/heroes` route _before_
hitting the wildcard route and navigating to "Page not found". hitting the wildcard route and navigating to "Page not found".
当所有路由都在同一个`AppRoutingModule`时,我们要把默认路由和[通配符路由](#wildcard)放在最后(这里是在`/heroes`路由后面),
这样路由器才有机会匹配到`/heroes`路由,否则它就会先遇到并匹配上该通配符路由,并导航到"页面未找到"路由。
The routes are no longer in one file. The routes are no longer in one file.
They are distributed across two modules, `AppRoutingModule` and `HeroesRoutingModule`. They are distributed across two modules, `AppRoutingModule` and `HeroesRoutingModule`.
这些路由不再位于单一文件中。他们分布在两个不同的模块中:`AppRoutingModule`和`HeroesRoutingModule`。
Each routing module augments the route configuration _in the order of import_. Each routing module augments the route configuration _in the order of import_.
If you list `AppRoutingModule` first, the wildcard route will be registered If you list `AppRoutingModule` first, the wildcard route will be registered
_before_ the hero routes. _before_ the hero routes.
The wildcard route — which matches _every_ URL — The wildcard route — which matches _every_ URL —
will intercept the attempt to navigate to a hero route. will intercept the attempt to navigate to a hero route.
每个路由模块都会根据*导入的顺序*把自己的路由配置追加进去。
如果我们先列出了`AppRoutingModule`,那么通配符路由就会被注册在"英雄管理"路由*之前*。
通配符路由(它匹配*任意*URL将会拦截住每一个到"英雄管理"路由的导航,因此事实上屏蔽了所有"英雄管理"路由。
.l-sub-section .l-sub-section
:marked :marked
Reverse the routing modules and see for yourself that Reverse the routing modules and see for yourself that
@ -1891,6 +1923,9 @@ a#routing-module-order
Learn about inspecting the runtime router configuration Learn about inspecting the runtime router configuration
[below](#inspect-config "Inspect the router config"). [below](#inspect-config "Inspect the router config").
反转路由模块的导入顺序,我们就会看到当点击英雄相关的链接时被导向了"页面未找到"路由。
要学习如何在运行时查看路由器配置,参见[稍后的内容](#inspect-config "Inspect the router config")。
a#route-def-with-parameter a#route-def-with-parameter
:marked :marked
### Route definition with a parameter ### Route definition with a parameter
@ -1991,6 +2026,8 @@ a#route-parameters
:marked :marked
### Setting the route parameters in the list view ### Setting the route parameters in the list view
### 在列表视图中设置路由参数
After navigating to the `HeroDetailComponent`, you expect to see the details of the selected hero. After navigating to the `HeroDetailComponent`, you expect to see the details of the selected hero.
You need *two* pieces of information: the routing path to the component and the hero's `id`. You need *two* pieces of information: the routing path to the component and the hero's `id`.
@ -2028,8 +2065,7 @@ a#activated-route
:marked :marked
### ActivatedRoute: the one-stop-shop for route information ### ActivatedRoute: the one-stop-shop for route information
每个路由都包含路径、数据参数、URL片段等很多信息。 ### ActivatedRoute一站式获取路由信息
所有这些信息都可以通过有路由器提供的一个叫[ActivatedRoute](../api/router/index/ActivatedRoute-interface.html)的服务提供商来获取。
The route path and parameters are available through an injected router service called the The route path and parameters are available through an injected router service called the
[ActivatedRoute](../api/router/index/ActivatedRoute-interface.html). [ActivatedRoute](../api/router/index/ActivatedRoute-interface.html).
@ -2090,6 +2126,8 @@ a#activated-route
:marked :marked
Import the `switchMap` operator because you need it later to process the `Observable` route parameters. Import the `switchMap` operator because you need it later to process the `Observable` route parameters.
这里导入`switchMap`操作符是因为我们稍后将会处理路由参数的可观察对象`Observable`。
+makeExcerpt('src/app/heroes/hero-detail.component.ts (switchMap operator import)', 'rxjs-operator-import') +makeExcerpt('src/app/heroes/hero-detail.component.ts (switchMap operator import)', 'rxjs-operator-import')
a#hero-detail-ctor a#hero-detail-ctor
@ -2113,14 +2151,21 @@ a#hero-detail-ctor
Angular calls the `ngOnInit` method shortly after creating an instance of the `HeroDetailComponent` Angular calls the `ngOnInit` method shortly after creating an instance of the `HeroDetailComponent`
so the hero will be retrieved in time to use it. so the hero will be retrieved in time to use it.
把数据访问逻辑放进`ngOnInit`方法中,而不是构造函数中可以提升组件的可测试性。
Angular会在创建完`HeroDetailComponent`的实例之后调用`ngOnInit`方法,因此该英雄会在即将使用时接收到。
Learn more about the `ngOnInit` method and other component lifecycle hooks in the [Lifecycle Hooks](lifecycle-hooks.html) guide. Learn more about the `ngOnInit` method and other component lifecycle hooks in the [Lifecycle Hooks](lifecycle-hooks.html) guide.
要了解关于`ngOnInit`方法和其它组件生命周期钩子的更多知识,参见[生命周期钩子](lifecycle-hooks.html)一章。
+makeExcerpt('src/app/heroes/hero-detail.component.ts (ngOnInit)', 'ngOnInit') +makeExcerpt('src/app/heroes/hero-detail.component.ts (ngOnInit)', 'ngOnInit')
:marked :marked
Since the parameters are provided as an `Observable`, you use the `switchMap` operator to Since the parameters are provided as an `Observable`, you use the `switchMap` operator to
provide them for the `id` parameter by name and tell the `HeroService` to fetch the hero with that `id`. provide them for the `id` parameter by name and tell the `HeroService` to fetch the hero with that `id`.
由于参数是作为`Observable`提供的,所以我们得用`switchMap`操作符来根据名字取得`id`参数,并告诉`HeroService`来获取带有那个`id`的英雄。
The `switchMap` operator allows you to perform an action with the current value of the `Observable`, The `switchMap` operator allows you to perform an action with the current value of the `Observable`,
and map it to a new `Observable`. As with many `rxjs` operators, `switchMap` handles and map it to a new `Observable`. As with many `rxjs` operators, `switchMap` handles
an `Observable` as well as a `Promise` to retrieve the value they emit. an `Observable` as well as a `Promise` to retrieve the value they emit.
@ -2181,14 +2226,24 @@ a#reuse
:marked :marked
When subscribing to an observable in a component, you almost always arrange to unsubscribe when the component is destroyed. When subscribing to an observable in a component, you almost always arrange to unsubscribe when the component is destroyed.
当在组件中订阅一个可观察对象时,我们通常总是要在组件销毁时取消这个订阅。
There are a few exceptional observables where this is not necessary. There are a few exceptional observables where this is not necessary.
The `ActivatedRoute` observables are among the exceptions. The `ActivatedRoute` observables are among the exceptions.
但是也有少数例外情况不需要取消订阅。
`ActivateRoute`中的各种可观察对象就是属于这种情况。
The `ActivatedRoute` and its observables are insulated from the `Router` itself. The `ActivatedRoute` and its observables are insulated from the `Router` itself.
The `Router` destroys a routed component when it is no longer needed and the injected `ActivatedRoute` dies with it. The `Router` destroys a routed component when it is no longer needed and the injected `ActivatedRoute` dies with it.
`ActivateRoute`及其可观察对象都是由`Router`本身负责管理的。
`Router`会在不再需要时销毁这个路由组件,而注入进去的`ActivateRoute`也随之销毁了。
Feel free to unsubscribe anyway. It is harmless and never a bad practice. Feel free to unsubscribe anyway. It is harmless and never a bad practice.
不过,我们仍然可以随意取消订阅,这不会造成任何损害,而且也不是一项坏的实践。
a#snapshot a#snapshot
:marked :marked
#### _Snapshot_: the _no-observable_ alternative #### _Snapshot_: the _no-observable_ alternative
@ -2318,9 +2373,13 @@ a#optionally-selecting
:marked :marked
### Heroes list: optionally selecting a hero ### Heroes list: optionally selecting a hero
### 英雄列表:选定一个英雄(也可不选)
When navigating to the `HeroDetailComponent` you specified the _required_ `id` of the hero-to-edit in the When navigating to the `HeroDetailComponent` you specified the _required_ `id` of the hero-to-edit in the
*route parameter* and made it the second item of the [_link parameters array_](#link-parameters-array). *route parameter* and made it the second item of the [_link parameters array_](#link-parameters-array).
当导航到`HeroDetailComponent`时,我们可以在*路由参数*中指定一个所要编辑的英雄`id`,只要把它作为[链接参数数组](#link-parameters-array)中的第二个条目就可以了。
+makeExcerpt('src/app/heroes/hero-list.component.1.ts', 'link-parameters-array') +makeExcerpt('src/app/heroes/hero-list.component.1.ts', 'link-parameters-array')
:marked :marked
@ -2547,35 +2606,62 @@ a#route-animation
:marked :marked
This file does the following: This file does the following:
该文件做了如下工作:
* Imports the animation symbols that build the animation triggers, control state, and manage transitions between states. * Imports the animation symbols that build the animation triggers, control state, and manage transitions between states.
导入动画符号以构建动画触发器、控制状态并管理状态之间的过渡。
* Exports a constant named `slideInDownAnimation` set to an animation trigger named *`routeAnimation`*; * Exports a constant named `slideInDownAnimation` set to an animation trigger named *`routeAnimation`*;
animated components will refer to this name. animated components will refer to this name.
导出了一个名叫`slideInDownAnimation`的常量,并把它设置为一个名叫*`routeAnimation`的动画触发器。带动画的组件将会引用这个名字。
* Specifies the _wildcard state_ , `*`, that matches any animation state that the route component is in. * Specifies the _wildcard state_ , `*`, that matches any animation state that the route component is in.
指定了一个*通配符状态* —— `*`,它匹配该路由组件存在时的任何动画状态。
* Defines two *transitions*, one to ease the component in from the left of the screen as it enters the application view (`:enter`), * Defines two *transitions*, one to ease the component in from the left of the screen as it enters the application view (`:enter`),
the other to animate the component down as it leaves the application view (`:leave`). the other to animate the component down as it leaves the application view (`:leave`).
定义两个*过渡效果*,其中一个(`:enter`在组件进入应用视图时让它从屏幕左侧缓动进入ease-in另一个`:leave`)在组件离开应用视图时让它向下飞出。
You could create more triggers with different transitions for other route components. This trigger is sufficient for the current milestone. You could create more triggers with different transitions for other route components. This trigger is sufficient for the current milestone.
我们可以为其它路由组件用不同的转场效果创建更多触发器。现在这个触发器已经足够当前的里程碑用了。
:marked :marked
Back in the `HeroDetailComponent`, import the `slideInDownAnimation` from `'./animations.ts`. Back in the `HeroDetailComponent`, import the `slideInDownAnimation` from `'./animations.ts`.
Add the `HostBinding` decorator to the imports from `@angular/core`; you'll need it in a moment. Add the `HostBinding` decorator to the imports from `@angular/core`; you'll need it in a moment.
返回`HeroDetailComponent`,从`'./animations.ts`中导入`slideInDownAnimation`。
从`@angular/core`中导入`HostBinding`装饰器,我们很快就会用到它。
Add an `animations` array to the `@Component` metadata's that contains the `slideInDownAnimation`. Add an `animations` array to the `@Component` metadata's that contains the `slideInDownAnimation`.
把一个包含`slideInDownAnimation`的`animations`数组添加到`@Component`的元数据中。
Then add three `@HostBinding` properties to the class to set the animation and styles for the route component's element. Then add three `@HostBinding` properties to the class to set the animation and styles for the route component's element.
然后把三个`@HostBinding`属性添加到类中以设置这个路由组件元素的动画和样式。
+makeExcerpt('src/app/heroes/hero-detail.component.ts (host bindings)', 'host-bindings') +makeExcerpt('src/app/heroes/hero-detail.component.ts (host bindings)', 'host-bindings')
:marked :marked
The `'@routeAnimation'` passed to the first `@HostBinding` matches The `'@routeAnimation'` passed to the first `@HostBinding` matches
the name of the `slideInDownAnimation` _trigger_, `routeAnimation`. the name of the `slideInDownAnimation` _trigger_, `routeAnimation`.
Set the `routeAnimation` property to `true` because you only care about the `:enter` and `:leave` states. Set the `routeAnimation` property to `true` because you only care about the `:enter` and `:leave` states.
传给了第一个`@HostBinding`的`'@routeAnimation'`匹配了`slideInDownAnimation`*触发器*的名字`routeAnimation`。
把`routeAnimation`属性设置为`true`,因为我们只关心`:enter`和`:leave`这两个状态。
The other two `@HostBinding` properties style the display and position of the component. The other two `@HostBinding` properties style the display and position of the component.
另外两个`@HostBinding`属性指定组建的外观和位置。
The `HeroDetailComponent` will ease in from the left when routed to and will slide down when navigating away. The `HeroDetailComponent` will ease in from the left when routed to and will slide down when navigating away.
当进入该路由时,`HeroDetailComponent`将会从左侧缓动进入屏幕,而离开路由时,将会向下划出。
.l-sub-section .l-sub-section
:marked :marked
Applying route animations to individual components works for a simple demo, but in a real life app, Applying route animations to individual components works for a simple demo, but in a real life app,
@ -2586,6 +2672,7 @@ a#route-animation
a#milestone-3-wrap-up a#milestone-3-wrap-up
:marked :marked
### Milestone 3 wrap up ### Milestone 3 wrap up
### 里程碑#3的总结 ### 里程碑#3的总结
You've learned how to do the following: You've learned how to do the following:
@ -2647,23 +2734,23 @@ a#milestone-3-wrap-up
这里是当前版本的范例程序相关文件。 这里是当前版本的范例程序相关文件。
+makeTabs( +makeTabs(
`router/ts/src/app/app.component.1.ts, `router/ts/src/app/app.component.1.ts,
router/ts/src/app/app.module.3.ts, router / ts / src / app / app.module.3.ts,
router/ts/src/app/app-routing.module.2.ts, router / ts / src / app / app - routing.module.2.ts,
router/ts/src/app/heroes/hero-list.component.ts, router / ts / src / app / heroes / hero - list.component.ts,
router/ts/src/app/heroes/hero-detail.component.ts, router / ts / src / app / heroes / hero - detail.component.ts,
router/ts/src/app/heroes/hero.service.ts, router / ts / src / app / heroes / hero.service.ts,
router/ts/src/app/heroes/heroes.module.ts, router / ts / src / app / heroes / heroes.module.ts,
router/ts/src/app/heroes/heroes-routing.module.ts`, router / ts / src / app / heroes / heroes - routing.module.ts`,
'', '',
`app.component.ts, `app.component.ts,
app.module.ts, app.module.ts,
app-routing.module.ts, app - routing.module.ts,
hero-list.component.ts, hero - list.component.ts,
hero-detail.component.ts, hero - detail.component.ts,
hero.service.ts, hero.service.ts,
heroes.module.ts, heroes.module.ts,
heroes-routing.module.ts`) heroes - routing.module.ts`)
a#milestone-4 a#milestone-4
.l-main-section .l-main-section
@ -2674,15 +2761,32 @@ a#milestone-4
It's time to add real features to the app's current placeholder crisis center. It's time to add real features to the app's current placeholder crisis center.
是时候往该应用的危机中心(现在是占位符)中添加一些真实的特性了。
Begin by imitating the heroes feature: Begin by imitating the heroes feature:
我们先从模仿"英雄管理"中的特性开始:
* Delete the placeholder crisis center file. * Delete the placeholder crisis center file.
删除危机中心的占位文件。
* Create an `app/crisis-center` folder. * Create an `app/crisis-center` folder.
创建`app/crisis-center`文件夹。
* Copy the files from `app/heroes` into the new crisis center folder. * Copy the files from `app/heroes` into the new crisis center folder.
把`app/heroes`中的文件复制到新的危机中心文件夹。
* In the new files, change every mention of "hero" to "crisis", and "heroes" to "crises". * In the new files, change every mention of "hero" to "crisis", and "heroes" to "crises".
在这些新文件中,把每一个对"hero"替换为"crisis",并把"heroes"替换为"crises"。
You'll turn the `CrisisService` into a purveyor of mock crises instead of mock heroes: You'll turn the `CrisisService` into a purveyor of mock crises instead of mock heroes:
我们将会把`CrisisService`转换成模拟的危机列表,而不再是模拟的英雄列表:
+makeExcerpt('src/app/crisis-center/crisis.service.ts', 'mock-crises') +makeExcerpt('src/app/crisis-center/crisis.service.ts', 'mock-crises')
:marked :marked
@ -2690,25 +2794,50 @@ a#milestone-4
You can leave *Heroes* in its current state as a contrast with the *Crisis Center* You can leave *Heroes* in its current state as a contrast with the *Crisis Center*
and decide later if the differences are worthwhile. and decide later if the differences are worthwhile.
最终的危机中心可以作为引入**子路由**这个新概念的基础。
我们把*英雄管理*保持在当前状态,以便和*危机中心*进行对比,以后再根据这些差异是否有价值来决定后续行动。
.alert.is-helpful .alert.is-helpful
:marked :marked
In keeping with the In keeping with the
<a href="https://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html" target="_blank" title="Separation of Concerns">*Separation of Concerns* principle</a>, <a href="https://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html" target="_blank" title="Separation of Concerns">*Separation of Concerns* principle</a>,
changes to the *Crisis Center* won't affect the `AppModule` or changes to the *Crisis Center* won't affect the `AppModule` or
any other feature's component. any other feature's component.
遵循<a href="https://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html" target="_blank" title="Separation of Concerns">*关注点分离Separation of Concerns*原则</a>
对*危机中心*的修改不会影响`AppModule`或其它特性模块中的组件。
a#crisis-child-routes a#crisis-child-routes
:marked :marked
### A crisis center with child routes ### A crisis center with child routes
### 带有子路由的危机中心
This section shows you how to organize the crisis center This section shows you how to organize the crisis center
to conform to the following recommended pattern for Angular applications: to conform to the following recommended pattern for Angular applications:
本节会展示如何组织危机中心来满足Angular应用所推荐的模式
* Each feature area resides in its own folder. * Each feature area resides in its own folder.
把每个特性放在自己的目录中。
* Each feature has its own Angular feature module. * Each feature has its own Angular feature module.
每个特性都有自己的Angular特性模块。
* Each area has its own area root component. * Each area has its own area root component.
每个特性区都有自己的根组件。
* Each area root component has its own router outlet and child routes. * Each area root component has its own router outlet and child routes.
每个特性区的根组件中都有自己的路由插座及其子路由。
* Feature area routes rarely (if ever) cross with routes of other features. * Feature area routes rarely (if ever) cross with routes of other features.
特性区的路由很少(或完全不)与其它特性区的路由交叉。
If your app had many feature areas, the app component trees might look like this: If your app had many feature areas, the app component trees might look like this:
如果我们有更多特性区,它们的组件树是这样的: 如果我们有更多特性区,它们的组件树是这样的:
@ -2731,23 +2860,38 @@ a#child-routing-component
:marked :marked
The `CrisisCenterComponent` has the following in common with the `AppComponent`: The `CrisisCenterComponent` has the following in common with the `AppComponent`:
`CrisisCenterComponent`和`AppComponent`有下列共同点:
* It is the *root* of the crisis center area, * It is the *root* of the crisis center area,
just as `AppComponent` is the root of the entire application. just as `AppComponent` is the root of the entire application.
它是危机中心特性区的*根*,正如`AppComponent`是整个应用的根。
* It is a *shell* for the crisis management feature area, * It is a *shell* for the crisis management feature area,
just as the `AppComponent` is a shell to manage the high-level workflow. just as the `AppComponent` is a shell to manage the high-level workflow.
它是危机管理特性区的*壳*,正如`AppComponent`是管理高层工作流的壳。
Like most shells, the `CrisisCenterComponent` class is very simple, simpler even than `AppComponent`: Like most shells, the `CrisisCenterComponent` class is very simple, simpler even than `AppComponent`:
it has no business logic, and its template has no links, just a title and it has no business logic, and its template has no links, just a title and
`<router-outlet>` for the crisis center child views. `<router-outlet>` for the crisis center child views.
就像大多数的壳一样,`CrisisCenterComponent`类也非常简单,甚至比`AppComponent`更简单:
它没有业务逻辑,它的模板中没有链接,只有一个标题和用于放置危机中心的子视图的`<router-outlet>`。
Unlike `AppComponent`, and most other components, it _lacks a selector_. Unlike `AppComponent`, and most other components, it _lacks a selector_.
It doesn't _need_ one since you don't *embed* this component in a parent template, It doesn't _need_ one since you don't *embed* this component in a parent template,
instead you use the router to *navigate* to it. instead you use the router to *navigate* to it.
与`AppComponent`和大多数其它组件不同的是,它甚至都*没有指定选择器`selector`*。
它不*需要*选择器,因为我们不会把这个组件嵌入到某个父模板中,而是使用路由器*导航*到它。
a#child-route-config a#child-route-config
:marked :marked
### Child route configuration ### Child route configuration
### 子路由配置
The `CrisisCenterComponent` is a *routing component* like the `AppComponent`. The `CrisisCenterComponent` is a *routing component* like the `AppComponent`.
It has its own `RouterOutlet` and its own child routes. It has its own `RouterOutlet` and its own child routes.
@ -2817,6 +2961,8 @@ a#child-route-config
Apply that logic to navigation within the crisis center for which the parent path is `/crisis-center`. Apply that logic to navigation within the crisis center for which the parent path is `/crisis-center`.
如果把该逻辑应用到危机中心中的导航,那么父路径就是`/crisis-center`。
* To navigate to the `CrisisCenterHomeComponent`, the full URL is `/crisis-center` (`/crisis-center` + `''` + `''`). * To navigate to the `CrisisCenterHomeComponent`, the full URL is `/crisis-center` (`/crisis-center` + `''` + `''`).
要导航到`CrisisCenterHomeComponent`完整的URL是`/crisis-center` (`/crisis-center` + `''` + `''`)。 要导航到`CrisisCenterHomeComponent`完整的URL是`/crisis-center` (`/crisis-center` + `''` + `''`)。
@ -2844,6 +2990,8 @@ a#import-crisis-module
:marked :marked
### Import crisis center module into the *AppModule* routes ### Import crisis center module into the *AppModule* routes
### 把危机中心模块导入到`AppModule`的路由中
As with the `HeroesModule`, you must add the `CrisisCenterModule` to the `imports` array of the `AppModule` As with the `HeroesModule`, you must add the `CrisisCenterModule` to the `imports` array of the `AppModule`
_before_ the `AppRoutingModule`: _before_ the `AppRoutingModule`:
@ -2855,9 +3003,10 @@ a#import-crisis-module
Remove the initial crisis center route from the `app-routing.module.ts`. Remove the initial crisis center route from the `app-routing.module.ts`.
The feature routes are now provided by the `HeroesModule` and the `CrisisCenter` modules. The feature routes are now provided by the `HeroesModule` and the `CrisisCenter` modules.
我们还从`app.routing.ts`中移除了危机中心的初始路由。我们的路由现在是由`HeroesModule`和`CrisisCenter`特性模块提供的。
The `app-routing.module.ts` file retains the top-level application routes such as the default and wildcard routes. The `app-routing.module.ts` file retains the top-level application routes such as the default and wildcard routes.
我们还从`app.routing.ts`中移除了危机中心的初始路由。我们的路由现在是由`HeroesModule`和`CrisisCenter`特性模块提供的。
我们将保持`app.routing.ts`文件中只有通用路由,本章稍后会讲解它。 我们将保持`app.routing.ts`文件中只有通用路由,本章稍后会讲解它。
+makeExcerpt('src/app/app-routing.module.3.ts (v3)') +makeExcerpt('src/app/app-routing.module.3.ts (v3)')
@ -2872,44 +3021,69 @@ a#relative-navigation
While building out the crisis center feature, you navigated to the While building out the crisis center feature, you navigated to the
crisis detail route using an **absolute path** that begins with a _slash_. crisis detail route using an **absolute path** that begins with a _slash_.
虽然构建出了危机中心特性区,我们却仍在使用以斜杠开头的**绝对路径**来导航到危机详情的路由。
The router matches such _absolute_ paths to routes starting from the top of the route configuration. The router matches such _absolute_ paths to routes starting from the top of the route configuration.
路由器会从路由配置的顶层来匹配像这样的*绝对路径*。
You could continue to use absolute paths like this to navigate inside the *Crisis Center* You could continue to use absolute paths like this to navigate inside the *Crisis Center*
feature, but that pins the links to the parent routing structure. feature, but that pins the links to the parent routing structure.
If you changed the parent `/crisis-center` path, you would have to change the link parameters array. If you changed the parent `/crisis-center` path, you would have to change the link parameters array.
我们固然可以继续像*危机中心*特性区一样使用绝对路径,但是那样会把链接钉死在特定的父路由结构上。
如果我们修改了父路径`/crisis-center`,那就不得不修改每一个链接参数数组。
You can free the links from this dependency by defining paths that are **relative** to the current URL segment. You can free the links from this dependency by defining paths that are **relative** to the current URL segment.
Navigation _within_ the feature area remains intact even if you change the parent route path to the feature. Navigation _within_ the feature area remains intact even if you change the parent route path to the feature.
通过改成定义*相对于*当前URL的路径我们可以把链接从这种依赖中解放出来。
当我们修改了该特性区的父路由路径时,该特性区*内部*的导航仍然完好无损。
Here's an example: Here's an example:
例子如下:
.l-sub-section .l-sub-section
:marked :marked
The router supports directory-like syntax in a _link parameters list_ to help guide route name lookup: The router supports directory-like syntax in a _link parameters list_ to help guide route name lookup:
在*链接参数数组*中,路由器支持"目录式"语法来指导我们如何查询路由名:
`./` or `no leading slash` is relative to the current level. `./` or `no leading slash` is relative to the current level.
`./`或`无前导斜线`形式是相对于当前级别的。
`../` to go up one level in the route path. `../` to go up one level in the route path.
`../`会回到当前路由路径的上一级。
You can combine relative navigation syntax with an ancestor path. You can combine relative navigation syntax with an ancestor path.
If you must navigate to a sibling route, you could use the `../<sibling>` convention to go up If you must navigate to a sibling route, you could use the `../<sibling>` convention to go up
one level, then over and down the sibling route path. one level, then over and down the sibling route path.
我们可以把相对导航语法和一个祖先路径组合起来用。
如果不得不导航到一个兄弟路由,我们可以用`../<sibling>`来回到上一级,然后进入兄弟路由路径中。
:marked :marked
To navigate a relative path with the `Router.navigate` method, you must supply the `ActivatedRoute` To navigate a relative path with the `Router.navigate` method, you must supply the `ActivatedRoute`
to give the router knowledge of where you are in the current route tree. to give the router knowledge of where you are in the current route tree.
用`Router.navigate`方法导航到相对路径时,我们必须提供当前的`ActivatedRoute`,来让路由器知道我们现在位于路由树中的什么位置。
After the _link parameters array_, add an object with a `relativeTo` property set to the `ActivatedRoute`. After the _link parameters array_, add an object with a `relativeTo` property set to the `ActivatedRoute`.
The router then calculates the target URL based on the active route's location. The router then calculates the target URL based on the active route's location.
“重定向redirect路由”需要一个`pathMatch`属性来告诉路由器如何把URL和路由中的路径进行匹配 在*链接参数数组*中,添加一个带有`relativeTo`属性的对象,并把它设置为当前的`ActivatedRoute`
本应用中,路由器应该只有在*完整的URL*匹配`''`时才选择指向`CrisisListComponent`的路由,因此,我们把`pathMatch`的值设置为`'full'` 这样路由器就会基于当前激活路由的位置来计算出目标URL
.l-sub-section .l-sub-section
:marked :marked
**Always** specify the complete _absolute_ path when calling router's `navigateByUrl` method. **Always** specify the complete _absolute_ path when calling router's `navigateByUrl` method.
当调用路由器的`navigateByUrl`时,**总是**要指定完整的*绝对路径*。
a#nav-to-crisis a#nav-to-crisis
:marked :marked
### Navigate to crisis detail with a relative URL ### Navigate to crisis detail with a relative URL
@ -2992,11 +3166,11 @@ figure.image-display
Here's the component and its template: Here's the component and its template:
+makeTabs( +makeTabs(
`router/ts/src/app/compose-message.component.ts, `router/ts/src/app/compose-message.component.ts,
router/ts/src/app/compose-message.component.html`, router / ts / src / app / compose - message.component.html`,
null, null,
`src/app/compose-message.component.ts, `src/app/compose-message.component.ts,
src/app/compose-message.component.html`) src / app / compose - message.component.html`)
:marked :marked
It looks about the same as any other component you've seen in this guide. It looks about the same as any other component you've seen in this guide.
@ -3056,7 +3230,7 @@ a#secondary-route-navigation
you should see something like the following URL in the browser address bar. you should see something like the following URL in the browser address bar.
code-example. code-example.
http://.../crisis-center(popup:compose) http://.../crisis-center(popup:compose)
:marked :marked
The interesting part of the URL follows the `...`: The interesting part of the URL follows the `...`:
@ -3068,7 +3242,7 @@ code-example.
Click the _Heroes_ link and look at the URL again. Click the _Heroes_ link and look at the URL again.
code-example. code-example.
http://.../heroes(popup:compose) http://.../heroes(popup:compose)
:marked :marked
The primary navigation part has changed; the secondary route is the same. The primary navigation part has changed; the secondary route is the same.
@ -3270,18 +3444,18 @@ a#can-activate-guard
管理特性模块包含`AdminComponent`,它用于在特性模块内的仪表盘路由以及两个尚未完成的用于管理危机和英雄的组件之间进行路由。 管理特性模块包含`AdminComponent`,它用于在特性模块内的仪表盘路由以及两个尚未完成的用于管理危机和英雄的组件之间进行路由。
+makeTabs( +makeTabs(
`router/ts/src/app/admin/admin-dashboard.component.1.ts, `router/ts/src/app/admin/admin-dashboard.component.1.ts,
router/ts/src/app/admin/admin.component.ts, router / ts / src / app / admin / admin.component.ts,
router/ts/src/app/admin/admin.module.ts, router / ts / src / app / admin / admin.module.ts,
router/ts/src/app/admin/manage-crises.component.ts, router / ts / src / app / admin / manage - crises.component.ts,
router/ts/src/app/admin/manage-heroes.component.ts router / ts / src / app / admin / manage - heroes.component.ts
`, `,
null, null,
`src/app/admin/admin-dashboard.component.ts, `src/app/admin/admin-dashboard.component.ts,
src/app/admin/admin.component.ts, src / app / admin / admin.component.ts,
src/app/admin/admin.module.ts, src / app / admin / admin.module.ts,
src/app/admin/manage-crises.component.ts, src / app / admin / manage - crises.component.ts,
src/app/admin/manage-heroes.component.ts src / app / admin / manage - heroes.component.ts
`) `)
.l-sub-section .l-sub-section
@ -3465,14 +3639,14 @@ a#add-login-component
同时在`AppModule`中导入并添加`LoginRoutingModule`。 同时在`AppModule`中导入并添加`LoginRoutingModule`。
+makeTabs( +makeTabs(
`router/ts/src/app/app.module.ts, `router/ts/src/app/app.module.ts,
router/ts/src/app/login.component.1.ts, router / ts / src / app / login.component.1.ts,
router/ts/src/app/login-routing.module.ts router / ts / src / app / login - routing.module.ts
`, `,
null, null,
`src/app/app.module.ts, `src/app/app.module.ts,
src/app/login.component.ts, src / app / login.component.ts,
src/app/login-routing.module.ts src / app / login - routing.module.ts
`) `)
.l-sub-section .l-sub-section
@ -3789,33 +3963,33 @@ a#fetch-before-navigating
本里程碑中与*危机中心*有关的代码如下: 本里程碑中与*危机中心*有关的代码如下:
+makeTabs( +makeTabs(
`router/ts/src/app/app.component.ts, `router/ts/src/app/app.component.ts,
router/ts/src/app/crisis-center/crisis-center-home.component.ts, router / ts / src / app / crisis - center / crisis - center - home.component.ts,
router/ts/src/app/crisis-center/crisis-center.component.ts, router / ts / src / app / crisis - center / crisis - center.component.ts,
router/ts/src/app/crisis-center/crisis-center-routing.module.4.ts, router / ts / src / app / crisis - center / crisis - center - routing.module.4.ts,
router/ts/src/app/crisis-center/crisis-list.component.ts, router / ts / src / app / crisis - center / crisis - list.component.ts,
router/ts/src/app/crisis-center/crisis-detail.component.ts, router / ts / src / app / crisis - center / crisis - detail.component.ts,
router/ts/src/app/crisis-center/crisis-detail-resolver.service.ts, router / ts / src / app / crisis - center / crisis - detail - resolver.service.ts,
router/ts/src/app/crisis-center/crisis.service.ts router / ts / src / app / crisis - center / crisis.service.ts
`, `,
null, null,
`app.component.ts, `app.component.ts,
crisis-center-home.component.ts, crisis - center - home.component.ts,
crisis-center.component.ts, crisis - center.component.ts,
crisis-center-routing.module.ts, crisis - center - routing.module.ts,
crisis-list.component.ts, crisis - list.component.ts,
crisis-detail.component.ts, crisis - detail.component.ts,
crisis-detail-resolver.service.ts, crisis - detail - resolver.service.ts,
crisis.service.ts crisis.service.ts
`) `)
+makeTabs( +makeTabs(
`router/ts/src/app/auth-guard.service.3.ts, `router/ts/src/app/auth-guard.service.3.ts,
router/ts/src/app/can-deactivate-guard.service.ts router / ts / src / app / can - deactivate - guard.service.ts
`, `,
null, null,
`auth-guard.service.ts, `auth-guard.service.ts,
can-deactivate-guard.service.ts can - deactivate - guard.service.ts
`) `)
a#query-parameters a#query-parameters
@ -4081,14 +4255,14 @@ a#lazy-load-crisis-center
下面是打开预加载之前的模块修改版: 下面是打开预加载之前的模块修改版:
+makeTabs( +makeTabs(
`router/ts/src/app/app.module.ts, `router/ts/src/app/app.module.ts,
router/ts/src/app/app-routing.module.6.ts, router / ts / src / app / app - routing.module.6.ts,
router/ts/src/app/crisis-center/crisis-center-routing.module.ts router / ts / src / app / crisis - center / crisis - center - routing.module.ts
`, `,
',preload-v1,', ',preload-v1,',
`app.module.ts, `app.module.ts,
app-routing.module.ts, app - routing.module.ts,
crisis-center-routing.module.ts crisis - center - routing.module.ts
`) `)
:marked :marked
You could try this now and confirm that the `CrisisCenterModule` loads after you click the "Crisis Center" button. You could try this now and confirm that the `CrisisCenterModule` loads after you click the "Crisis Center" button.