` 来支持异步检查,或 `boolean` 或 `UrlTree` 来支持同步检查。
这里返回的或者是 `true` 以便允许用户访问管理特性模块,或者是 `UrlTree` 以便把用户重定向到登录页:
Add the same `AuthGuard` to the `component-less` admin route to protect all other child routes at one time
instead of adding the `AuthGuard` to each route individually.
同样把这个 `AuthGuard` 添加到“无组件的”管理路由,来同时保护它的所有子路由,而不是为每个路由单独添加这个 `AuthGuard`。
{@a can-deactivate-guard}
### `CanDeactivate`: handling unsaved changes
### `CanDeactivate`:处理未保存的更改
Back in the "Heroes" workflow, the app accepts every change to a hero immediately without validation.
回到 “Heroes” 工作流,该应用会立即接受对英雄的每次更改,而不进行验证。
In the real world, you might have to accumulate the users changes, validate across fields, validate on the server, or hold changes in a pending state until the user confirms them as a group or cancels and reverts all changes.
在现实世界,你可能不得不积累来自用户的更改,跨字段验证,在服务器上验证,或者把变更保持在待定状态,直到用户确认这一组字段或取消并还原所有变更为止。
When the user navigates away, you can let the user decide what to do with unsaved changes.
If the user cancels, you'll stay put and allow more changes.
If the user approves, the app can save.
当用户要导航离开时,你可以让用户自己决定该怎么处理这些未保存的更改。
如果用户选择了取消,你就留下来,并允许更多改动。
如果用户选择了确认,那就进行保存。
You still might delay navigation until the save succeeds.
If you let the user move to the next screen immediately and saving were to fail (perhaps the data is ruled invalid), you would lose the context of the error.
在保存成功之前,你还可以继续推迟导航。如果你让用户立即移到下一个界面,而保存却失败了(可能因为数据不符合有效性规则),你就会丢失该错误的上下文环境。
You need to stop the navigation while you wait, asynchronously, for the server to return with its answer.
你需要用异步的方式等待,在服务器返回答复之前先停止导航。
The `CanDeactivate` guard helps you decide what to do with unsaved changes and how to proceed.
`CanDeactivate` 守卫能帮助你决定如何处理未保存的更改,以及如何处理。
{@a cancel-save}
#### Cancel and save
#### 取消与保存
Users update crisis information in the `CrisisDetailComponent`.
Unlike the `HeroDetailComponent`, the user changes do not update the crisis entity immediately.
Instead, the app updates the entity when the user presses the Save button and discards the changes when the user presses the Cancel button.
用户在 `CrisisDetailComponent` 中更新危机信息。
与 `HeroDetailComponent` 不同,用户的改动不会立即更新危机的实体对象。当用户按下了 Save 按钮时,应用就更新这个实体对象;如果按了 Cancel 按钮,那就放弃这些更改。
Both buttons navigate back to the crisis list after save or cancel.
这两个按钮都会在保存或取消之后导航回危机列表。
In this scenario, the user could click the heroes link, cancel, push the browser back button, or navigate away without saving.
在这种情况下,用户可以点击 heroes 链接,取消,按下浏览器后退按钮,或者不保存就离开。
This example app asks the user to be explicit with a confirmation dialog box that waits asynchronously for the user's
response.
这个示例应用会弹出一个确认对话框,它会异步等待用户的响应,等用户给出一个明确的答复。
You could wait for the user's answer with synchronous, blocking code, however, the app is more responsive—and can do other work—by waiting for the user's answer asynchronously.
你也可以用同步的方式等用户的答复,阻塞代码。但如果能用异步的方式等待用户的答复,应用就会响应性更好,还能同时做别的事。
Generate a `Dialog` service to handle user confirmation.
生成一个 `Dialog` 服务,以处理用户的确认操作。
ng generate service dialog
Add a `confirm()` method to the `DialogService` to prompt the user to confirm their intent.
The `window.confirm` is a blocking action that displays a modal dialog and waits for user interaction.
为 `DialogService` 添加一个 `confirm()` 方法,以提醒用户确认。`window.confirm` 是一个阻塞型操作,它会显示一个模态对话框,并等待用户的交互。
It returns an `Observable` that resolves when the user eventually decides what to do: either to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`).
它返回*observable*,当用户最终决定了如何去做时,它就会被*解析* —— 或者决定放弃更改直接导航离开(`true`),或者保留未完成的修改,留在危机编辑器中(`false`)。
{@a CanDeactivate}
Generate a guard that checks for the presence of a `canDeactivate()` method in a component—any component.
生成一个守卫(guard),以检查组件(任意组件均可)中是否存在 `canDeactivate()` 方法。
ng generate guard can-deactivate
Paste the following code into your guard.
把下面的代码粘贴到守卫中。
While the guard doesn't have to know which component has a deactivate method, it can detect that the `CrisisDetailComponent` component has the `canDeactivate()` method and call it.
The guard not knowing the details of any component's deactivation method makes the guard reusable.
守卫不需要知道哪个组件有 `deactivate` 方法,它可以检测 `CrisisDetailComponent` 组件有没有 `canDeactivate()` 方法并调用它。守卫在不知道任何组件 `deactivate` 方法细节的情况下,就能让这个守卫重复使用。
Alternatively, you could make a component-specific `CanDeactivate` guard for the `CrisisDetailComponent`.
The `canDeactivate()` method provides you with the current instance of the `component`, the current `ActivatedRoute`, and `RouterStateSnapshot` in case you needed to access some external information.
This would be useful if you only wanted to use this guard for this component and needed to get the component's properties or confirm whether the router should allow navigation away from it.
另外,你也可以为 `CrisisDetailComponent` 创建一个特定的 `CanDeactivate` 守卫。
在需要访问外部信息时,`canDeactivate()` 方法为你提供了组件、`ActivatedRoute` 和 `RouterStateSnapshot` 的当前实例。
如果只想为这个组件使用该守卫,并且需要获取该组件属性或确认路由器是否允许从该组件导航出去时,这会非常有用。
Looking back at the `CrisisDetailComponent`, it implements the confirmation workflow for unsaved changes.
看看 `CrisisDetailComponent` 组件,它已经实现了对未保存的更改进行确认的工作流。
Notice that the `canDeactivate()` method can return synchronously; it returns `true` immediately if there is no crisis or there are no pending changes.
But it can also return a `Promise` or an `Observable` and the router will wait for that to resolve to truthy (navigate) or falsy (stay on the current route).
注意,`canDeactivate()` 方法可以同步返回;如果没有危机,或者没有待处理的更改,它会立即返回 `true`。但它也能返回一个 `Promise` 或一个 `Observable`,路由器也会等待它解析为真值(导航)或伪造(停留在当前路由上)。
Add the `Guard` to the crisis detail route in `crisis-center-routing.module.ts` using the `canDeactivate` array property.
往 `crisis-center.routing.module.ts` 的危机详情路由中用 `canDeactivate` 数组添加一个 `Guard`(守卫)。
Now you have given the user a safeguard against unsaved changes.
现在,你已经给了用户一个能保护未保存更改的安全守卫。
{@a Resolve}
{@a resolve-guard}
### _Resolve_: pre-fetching component data
### _Resolve_: 预先获取组件数据
In the `Hero Detail` and `Crisis Detail`, the app waited until the route was activated to fetch the respective hero or crisis.
在 `Hero Detail` 和 `Crisis Detail` 中,它们等待路由读取完对应的英雄和危机。
If you were using a real world API, there might be some delay before the data to display is returned from the server.
You don't want to display a blank component while waiting for the data.
如果你在使用真实 api,很有可能数据返回有延迟,导致无法即时显示。
在这种情况下,直到数据到达前,显示一个空的组件不是最好的用户体验。
To improve this behavior, you can pre-fetch data from the server using a resolver so it's ready the
moment the route is activated.
This also allows you to handle errors before routing to the component.
There's no point in navigating to a crisis detail for an `id` that doesn't have a record.
It'd be better to send the user back to the `Crisis List` that shows only valid crisis centers.
最好使用解析器预先从服务器上获取完数据,这样在路由激活的那一刻数据就准备好了。
还要在路由到此组件之前处理好错误。
但当某个 `id` 无法对应到一个危机详情时,就没办法处理它。
这时最好把用户带回到“危机列表”中,那里显示了所有有效的“危机”。
In summary, you want to delay rendering the routed component until all necessary data has been fetched.
总之,你希望的是只有当所有必要数据都已经拿到之后,才渲染这个路由组件。
{@a fetch-before-navigating}
#### Fetch data before navigating
#### 导航前预先加载路由信息
At the moment, the `CrisisDetailComponent` retrieves the selected crisis.
If the crisis is not found, the router navigates back to the crisis list view.
目前,`CrisisDetailComponent` 会接收选中的危机。
如果该危机没有找到,路由器就会导航回危机列表视图。
The experience might be better if all of this were handled first, before the route is activated.
A `CrisisDetailResolver` service could retrieve a `Crisis` or navigate away, if the `Crisis` did not exist, _before_ activating the route and creating the `CrisisDetailComponent`.
如果能在该路由将要激活时提前处理了这个问题,那么用户体验会更好。
`CrisisDetailResolver` 服务可以接收一个 `Crisis`,而如果这个 `Crisis` 不存在,就会在激活该路由并创建 `CrisisDetailComponent` 之前先行离开。
Generate a `CrisisDetailResolver` service file within the `Crisis Center` feature area.
在 `Crisis Center` 特性区生成一个 `CrisisDetailResolver` 服务文件。
ng generate service crisis-center/crisis-detail-resolver
Move the relevant parts of the crisis retrieval logic in `CrisisDetailComponent.ngOnInit()` into the `CrisisDetailResolverService`.
Import the `Crisis` model, `CrisisService`, and the `Router` so you can navigate elsewhere if you can't fetch the crisis.
把 `CrisisDetailComponent.ngOnInit()` 中与危机检索有关的逻辑移到 `CrisisDetailResolverService` 中。
导入 `Crisis` 模型、`CrisisService` 和 `Router` 以便让你可以在找不到指定的危机时导航到别处。
Be explicit and implement the `Resolve` interface with a type of `Crisis`.
为了更明确一点,可以实现一个带有 `Crisis` 类型的 `Resolve` 接口。
Inject the `CrisisService` and `Router` and implement the `resolve()` method.
That method could return a `Promise`, an `Observable`, or a synchronous return value.
注入 `CrisisService` 和 `Router`,并实现 `resolve()` 方法。
该方法可以返回一个 `Promise`、一个 `Observable` 来支持异步方式,或者直接返回一个值来支持同步方式。
The `CrisisService.getCrisis()` method returns an observable in order to prevent the route from loading until the data is fetched.
The `Router` guards require an observable to `complete`, which means it has emitted all
of its values.
You use the `take` operator with an argument of `1` to ensure that the `Observable` completes after retrieving the first value from the Observable returned by the `getCrisis()` method.
`CrisisService.getCrisis()` 方法返回一个可观察对象,以防止在数据获取完之前加载本路由。
`Router` 守卫要求这个可观察对象必须可结束(`complete`),也就是说它已经发出了所有值。
你可以为 `take` 操作符传入一个参数 `1`,以确保这个可观察对象会在从 `getCrisis` 方法所返回的可观察对象中取到第一个值之后就会结束。
If it doesn't return a valid `Crisis`, then return an empty `Observable`, cancel the previous in-progress navigation to the `CrisisDetailComponent`, and navigate the user back to the `CrisisListComponent`.
The updated resolver service looks like this:
如果它没有返回有效的 `Crisis`,就会返回一个 `Observable`,以取消以前到 `CrisisDetailComponent` 的在途导航,并把用户导航回 `CrisisListComponent`。修改后的 `resolver` 服务是这样的:
Import this resolver in the `crisis-center-routing.module.ts` and add a `resolve` object to the `CrisisDetailComponent` route configuration.
把这个解析器(resolver)导入到 `crisis-center-routing.module.ts` 中,并往 `CrisisDetailComponent` 的路由配置中添加一个 `resolve` 对象。
The `CrisisDetailComponent` should no longer fetch the crisis.
When you re-configured the route, you changed where the crisis is.
Update the `CrisisDetailComponent` to get the crisis from the `ActivatedRoute.data.crisis` property instead;
`CrisisDetailComponent` 不应该再去获取这个危机的详情。
你只要重新配置路由,就可以修改从哪里获取危机的详情。
把 `CrisisDetailComponent` 改成从 `ActivatedRoute.data.crisis` 属性中获取危机详情,这正是你重新配置路由的恰当时机。
Note the following three important points:
注意以下三个要点:
1. The router's `Resolve` interface is optional.
The `CrisisDetailResolverService` doesn't inherit from a base class.
The router looks for that method and calls it if found.
路由器的这个 `Resolve` 接口是可选的。`CrisisDetailResolverService` 没有继承自某个基类。路由器只要找到了这个方法,就会调用它。
1. The router calls the resolver in any case where the the user could navigate away so you don't have to code for each use case.
路由器会在用户可以导航的任何情况下调用该解析器,这样你就不用针对每个用例都编写代码了。
1. Returning an empty `Observable` in at least one resolver will cancel navigation.
在任何一个解析器中返回空的 `Observable` 就会取消导航。
The relevant Crisis Center code for this milestone follows.
与里程碑相关的危机中心代码如下。
Guards
路由守卫
{@a query-parameters}
{@a fragment}
### Query parameters and fragments
### 查询参数及片段
In the [route parameters](#optional-route-parameters) section, you only dealt with parameters specific to the route.
However, you can use query parameters to get optional parameters available to all routes.
在[路由参数](#optional-route-parameters)部分,你只需要处理该路由的专属参数。但是,你也可以用查询参数来获取对所有路由都可用的可选参数。
[Fragments](https://en.wikipedia.org/wiki/Fragment_identifier) refer to certain elements on the page
identified with an `id` attribute.
[片段](https://en.wikipedia.org/wiki/Fragment_identifier)可以引用页面中带有特定 `id` 属性的元素.
Update the `AuthGuard` to provide a `session_id` query that will remain after navigating to another route.
修改 `AuthGuard` 以提供 `session_id` 查询参数,在导航到其它路由后,它还会存在。
Add an `anchor` element so you can jump to a certain point on the page.
再添加一个锚点(`A`)元素,来让你能跳转到页面中的正确位置。
Add the `NavigationExtras` object to the `router.navigate()` method that navigates you to the `/login` route.
为 `router.navigate()` 方法添加一个 `NavigationExtras` 对象,用来导航到 `/login` 路由。
You can also preserve query parameters and fragments across navigations without having to provide them again when navigating.
In the `LoginComponent`, you'll add an *object* as the second argument in the `router.navigateUrl()` function and provide the `queryParamsHandling` and `preserveFragment` to pass along the current query parameters and fragment to the next route.
还可以在导航之间**保留**查询参数和片段,而无需再次在导航中提供。在 `LoginComponent` 中的 `router.navigateUrl()` 方法中,添加一个对象作为第二个参数,该**对象**提供了 `queryParamsHandling` 和 `preserveFragment`,用于传递当前的查询参数和片段到下一个路由。
The `queryParamsHandling` feature also provides a `merge` option, which preserves and combines the current query parameters with any provided query parameters when navigating.
`queryParamsHandling` 特性还提供了 `merge` 选项,它将会在导航时保留当前的查询参数,并与其它查询参数合并。
To navigate to the Admin Dashboard route after logging in, update `admin-dashboard.component.ts` to handle the
query parameters and fragment.
要在登录后导航到 Admin Dashboard 路由,请更新 `admin-dashboard.component.ts` 以处理这些查询参数和片段。
Query parameters and fragments are also available through the `ActivatedRoute` service.
Just like route parameters, the query parameters and fragments are provided as an `Observable`.
The updated Crisis Admin component feeds the `Observable` directly into the template using the `AsyncPipe`.
查询参数和片段可通过 `Router` 服务的 `routerState` 属性使用。和路由参数类似,全局查询参数和片段也是 `Observable` 对象。
在修改过的英雄管理组件中,你将借助 `AsyncPipe` 直接把 `Observable` 传给模板。
Now, you can click on the Admin button, which takes you to the Login page with the provided `queryParamMap` and `fragment`.
After you click the login button, notice that you have been redirected to the `Admin Dashboard` page with the query parameters and fragment still intact in the address bar.
按照下列步骤试验下:点击 Admin 按钮,它会带着你提供的 `queryParamMap` 和 `fragment` 跳转到登录页。
点击 Login 按钮,你就会被重定向到 `Admin Dashboard` 页。
注意,它仍然带着上一步提供的 `queryParamMap` 和 `fragment`。
You can use these persistent bits of information for things that need to be provided across pages like authentication tokens or session ids.
你可以用这些持久化信息来携带需要为每个页面都提供的信息,如认证令牌或会话的 ID 等。
The `query params` and `fragment` can also be preserved using a `RouterLink` with
the `queryParamsHandling` and `preserveFragment` bindings respectively.
“查询参数”和“片段”也可以分别用 `RouterLink` 中的 **queryParamsHandling** 和 **preserveFragment** 保存。
{@a asynchronous-routing}
## Milestone 6: Asynchronous routing
## 里程碑 6:异步路由
As you've worked through the milestones, the application has naturally gotten larger.
At some point you'll reach a point where the application takes a long time to load.
完成上面的里程碑后,应用程序很自然地长大了。在某一个时间点,你将达到一个顶点,应用将会需要过多的时间来加载。
To remedy this issue, use asynchronous routing, which loads feature modules lazily, on request.
Lazy loading has multiple benefits.
为了解决这个问题,请使用异步路由,它会根据请求来惰性加载某些特性模块。惰性加载有很多好处。
* You can load feature areas only when requested by the user.
你可以只在用户请求时才加载某些特性区。
* You can speed up load time for users that only visit certain areas of the application.
对于那些只访问应用程序某些区域的用户,这样能加快加载速度。
* You can continue expanding lazy loaded feature areas without increasing the size of the initial load bundle.
你可以持续扩充惰性加载特性区的功能,而不用增加初始加载的包体积。
You're already part of the way there.
By organizing the application into modules—`AppModule`,
`HeroesModule`, `AdminModule` and `CrisisCenterModule`—you
have natural candidates for lazy loading.
你已经完成了一部分。通过把应用组织成一些模块:`AppModule`、`HeroesModule`、`AdminModule` 和 `CrisisCenterModule`,
你已经有了可用于实现惰性加载的候选者。
Some modules, like `AppModule`, must be loaded from the start.
But others can and should be lazy loaded.
The `AdminModule`, for example, is needed by a few authorized users, so
you should only load it when requested by the right people.
有些模块(比如 `AppModule`)必须在启动时加载,但其它的都可以而且应该惰性加载。
比如 `AdminModule` 就只有少数已认证的用户才需要它,所以你应该只有在正确的人请求它时才加载。
{@a lazy-loading-route-config}
### Lazy Loading route configuration
### 惰性加载路由配置
Change the `admin` path in the `admin-routing.module.ts` from `'admin'` to an empty string, `''`, the empty path.
把 `admin-routing.module.ts` 中的 `admin` 路径从 `'admin'` 改为空路径 `''`。
Use empty path routes to group routes together without adding any additional path segments to the URL.
Users will still visit `/admin` and the `AdminComponent` still serves as the Routing Component containing child routes.
可以用*空路径*路由来对路由进行分组,而不用往 URL 中添加额外的路径片段。
用户仍旧访问 `/admin`,并且 `AdminComponent` 仍然作为用来包含子路由的路由组件。
Open the `AppRoutingModule` and add a new `admin` route to its `appRoutes` array.
打开 `AppRoutingModule`,并把一个新的 `admin` 路由添加到它的 `appRoutes` 数组中。
Give it a `loadChildren` property instead of a `children` property.
The `loadChildren` property takes a function that returns a promise using the browser's built-in syntax for lazy loading code using dynamic imports `import('...')`.
The path is the location of the `AdminModule` (relative to the app root).
After the code is requested and loaded, the `Promise` resolves an object that contains the `NgModule`, in this case the `AdminModule`.
给它一个 `loadChildren` 属性(替换掉 `children` 属性)。
`loadChildren` 属性接收一个函数,该函数使用浏览器内置的动态导入语法 `import('...')` 来惰性加载代码,并返回一个承诺(Promise)。
其路径是 `AdminModule` 的位置(相对于应用的根目录)。
当代码请求并加载完毕后,这个 `Promise` 就会解析成一个包含 `NgModule` 的对象,也就是 `AdminModule`。
*Note*: When using absolute paths, the `NgModule` file location must begin with `src/app` in order to resolve correctly. For custom [path mapping with absolute paths](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping), you must configure the `baseUrl` and `paths` properties in the project `tsconfig.json`.
**注意**: 当使用绝对路径时,`NgModule` 的文件位置必须以 `src/app` 开头,以便正确解析。对于自定义的 [使用绝对路径的路径映射表](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping),你必须在项目的 `tsconfig.json` 中必须配置好 `baseUrl` 和 `paths` 属性。
When the router navigates to this route, it uses the `loadChildren` string to dynamically load the `AdminModule`.
Then it adds the `AdminModule` routes to its current route configuration.
Finally, it loads the requested route to the destination admin component.
当路由器导航到这个路由时,它会用 `loadChildren` 字符串来动态加载 `AdminModule`,然后把 `AdminModule` 添加到当前的路由配置中,
最后,它把所请求的路由加载到目标 `admin` 组件中。
The lazy loading and re-configuration happen just once, when the route is first requested; the module and routes are available immediately for subsequent requests.
惰性加载和重新配置工作只会发生一次,也就是在该路由首次被请求时。在后续的请求中,该模块和路由都是立即可用的。
Angular provides a built-in module loader that supports SystemJS to load modules asynchronously. If you were
using another bundling tool, such as Webpack, you would use the Webpack mechanism for asynchronously loading modules.
Angular 提供一个内置模块加载器,支持**`SystemJS`**来异步加载模块。如果你使用其它捆绑工具比如 **Webpack**,则使用 Webpack 的机制来异步加载模块。
Take the final step and detach the admin feature set from the main application.
The root `AppModule` must neither load nor reference the `AdminModule` or its files.
最后一步是把管理特性区从主应用中完全分离开。
根模块 `AppModule` 既不能加载也不能引用 `AdminModule` 及其文件。
In `app.module.ts`, remove the `AdminModule` import statement from the top of the file
and remove the `AdminModule` from the NgModule's `imports` array.
在 `app.module.ts` 中,从顶部移除 `AdminModule` 的导入语句,并且从 NgModule 的 `imports` 数组中移除 `AdminModule`。
{@a can-load-guard}
### `CanLoad`: guarding unauthorized loading of feature modules
### `CanLoad`:保护对特性模块的未授权加载
You're already protecting the `AdminModule` with a `CanActivate` guard that prevents unauthorized users from accessing the admin feature area.
It redirects to the login page if the user is not authorized.
你已经使用 `CanActivate` 保护 `AdminModule` 了,它会阻止未授权用户访问管理特性区。如果用户未登录,它就会跳转到登录页。
But the router is still loading the `AdminModule` even if the user can't visit any of its components.
Ideally, you'd only load the `AdminModule` if the user is logged in.
但是路由器仍然会加载 `AdminModule` —— 即使用户无法访问它的任何一个组件。
理想的方式是,只有在用户已登录的情况下你才加载 `AdminModule`。
Add a `CanLoad` guard that only loads the `AdminModule` once the user is logged in _and_ attempts to access the admin feature area.
添加一个 `CanLoad` 守卫,它只在用户已登录*并且*尝试访问管理特性区的时候,才加载 `AdminModule` 一次。
The existing `AuthGuard` already has the essential logic in its `checkLogin()` method to support the `CanLoad` guard.
现有的 `AuthGuard` 的 `checkLogin()` 方法中已经有了支持 `CanLoad` 守卫的基础逻辑。
Open `auth.guard.ts`.
Import the `CanLoad` interface from `@angular/router`.
Add it to the `AuthGuard` class's `implements` list.
Then implement `canLoad()` as follows:
打开 `auth.guard.ts`,从 `@angular/router` 中导入 `CanLoad` 接口。
把它添加到 `AuthGuard` 类的 `implements` 列表中。
然后实现 `canLoad`,代码如下:
The router sets the `canLoad()` method's `route` parameter to the intended destination URL.
The `checkLogin()` method redirects to that URL once the user has logged in.
路由器会把 `canLoad()` 方法的 `route` 参数设置为准备访问的目标 URL。
如果用户已经登录了,`checkLogin()` 方法就会重定向到那个 URL。
Now import the `AuthGuard` into the `AppRoutingModule` and add the `AuthGuard` to the `canLoad`
array property for the `admin` route.
The completed admin route looks like this:
现在,把 `AuthGuard` 导入到 `AppRoutingModule` 中,并把 `AuthGuard` 添加到 `admin` 路由的 `canLoad` 数组中。
完整的 `admin` 路由是这样的:
{@a preloading}
### Preloading: background loading of feature areas
### 预加载:特性区的后台加载
In addition to loading modules on-demand, you can load modules asynchronously with preloading.
除了按需加载模块外,还可以通过预加载方式异步加载模块。
The `AppModule` is eagerly loaded when the application starts, meaning that it loads right away.
Now the `AdminModule` loads only when the user clicks on a link, which is called lazy loading.
当应用启动时,`AppModule` 被急性加载,这意味着它会立即加载。而 `AdminModule` 只在用户点击链接时加载,这叫做惰性加载。
Preloading allows you to load modules in the background so that the data is ready to render when the user activates a particular route.
Consider the Crisis Center.
It isn't the first view that a user sees.
By default, the Heroes are the first view.
For the smallest initial payload and fastest launch time, you should eagerly load the `AppModule` and the `HeroesModule`.
预加载允许你在后台加载模块,以便当用户激活某个特定的路由时,就可以渲染这些数据了。
考虑一下危机中心。
它不是用户看到的第一个视图。
默认情况下,英雄列表才是第一个视图。为了获得最小的初始有效负载和最快的启动时间,你应该急性加载 `AppModule` 和 `HeroesModule`。
You could lazy load the Crisis Center.
But you're almost certain that the user will visit the Crisis Center within minutes of launching the app.
Ideally, the app would launch with just the `AppModule` and the `HeroesModule` loaded and then, almost immediately, load the `CrisisCenterModule` in the background.
By the time the user navigates to the Crisis Center, its module will have been loaded and ready.
你可以惰性加载危机中心。
但是,你几乎可以肯定用户会在启动应用之后的几分钟内访问危机中心。
理想情况下,应用启动时应该只加载 `AppModule` 和 `HeroesModule`,然后几乎立即开始后台加载 `CrisisCenterModule`。
在用户浏览到危机中心之前,该模块应该已经加载完毕,可供访问了。
{@a how-preloading}
#### How preloading works
#### 预加载的工作原理
After each successful navigation, the router looks in its configuration for an unloaded module that it can preload.
Whether it preloads a module, and which modules it preloads, depends upon the preload strategy.
在每次成功的导航后,路由器会在自己的配置中查找尚未加载并且可以预加载的模块。
是否加载某个模块,以及要加载哪些模块,取决于*预加载策略*。
The `Router` offers two preloading strategies:
`Router` 提供了两种预加载策略:
* No preloading, which is the default. Lazy loaded feature areas are still loaded on-demand.
完全不预加载,这是默认值。惰性加载的特性区仍然会按需加载。
* Preloading of all lazy loaded feature areas.
预加载所有惰性加载的特性区。
The router either never preloads, or preloads every lazy loaded module.
The `Router` also supports [custom preloading strategies](#custom-preloading) for fine control over which modules to preload and when.
路由器或者完全不预加载或者预加载每个惰性加载模块。
路由器还支持[自定义预加载策略](guide/router#custom-preloading),以便完全控制要预加载哪些模块以及何时加载。
This section guides you through updating the `CrisisCenterModule` to load lazily by default and use the `PreloadAllModules` strategy to load all lazy loaded modules.
本节将指导你把 `CrisisCenterModule` 改成惰性加载的,并使用 `PreloadAllModules` 策略来预加载所有惰性加载模块。
{@a lazy-load-crisis-center}
#### Lazy load the crisis center
#### 惰性加载危机中心
Update the route configuration to lazy load the `CrisisCenterModule`.
Take the same steps you used to configure `AdminModule` for lazy loading.
修改路由配置,来惰性加载 `CrisisCenterModule`。修改的步骤和配置惰性加载 `AdminModule` 时一样。
1. Change the `crisis-center` path in the `CrisisCenterRoutingModule` to an empty string.
把 `CrisisCenterRoutingModule` 中的路径从 `crisis-center` 改为空字符串。
1. Add a `crisis-center` route to the `AppRoutingModule`.
往 `AppRoutingModule` 中添加一个 `crisis-center` 路由。
1. Set the `loadChildren` string to load the `CrisisCenterModule`.
设置 `loadChildren` 字符串来加载 `CrisisCenterModule`。
1. Remove all mention of the `CrisisCenterModule` from `app.module.ts`.
从 `app.module.ts` 中移除所有对 `CrisisCenterModule` 的引用。
Here are the updated modules _before enabling preload_:
下面是打开预加载之前的模块修改版:
You could try this now and confirm that the `CrisisCenterModule` loads after you click the "Crisis Center" button.
你可以现在尝试它,并确认在点击了“Crisis Center”按钮之后加载了 `CrisisCenterModule`。
To enable preloading of all lazy loaded modules, import the `PreloadAllModules` token from the Angular router package.
要为所有惰性加载模块启用预加载功能,请从 Angular 的路由模块中导入 `PreloadAllModules`。
The second argument in the `RouterModule.forRoot()` method takes an object for additional configuration options.
The `preloadingStrategy` is one of those options.
Add the `PreloadAllModules` token to the `forRoot()` call:
`RouterModule.forRoot()` 方法的第二个参数接受一个附加配置选项对象。
`preloadingStrategy` 就是其中之一。
把 `PreloadAllModules` 添加到 `forRoot()` 调用中:
This configures the `Router` preloader to immediately load all lazy loaded routes (routes with a `loadChildren` property).
这项配置会让 `Router` 预加载器立即加载*所有*惰性加载路由(带 `loadChildren` 属性的路由)。
When you visit `http://localhost:4200`, the `/heroes` route loads immediately upon launch and the router starts loading the `CrisisCenterModule` right after the `HeroesModule` loads.
当访问 `http://localhost:4200` 时,`/heroes` 路由立即随之启动,并且路由器在加载了 `HeroesModule` 之后立即开始加载 `CrisisCenterModule`。
Currently, the `AdminModule` does not preload because `CanLoad` is blocking it.
目前,`AdminModule` 并没有预加载,因为 `CanLoad` 阻塞了它。
{@a preload-canload}
#### `CanLoad` blocks preload
#### `CanLoad` 会阻塞预加载
The `PreloadAllModules` strategy does not load feature areas protected by a [CanLoad](#can-load-guard) guard.
`PreloadAllModules` 策略不会加载被[CanLoad](guide/router#can-load-guard)守卫所保护的特性区。
You added a `CanLoad` guard to the route in the `AdminModule` a few steps back to block loading of that module until the user is authorized.
That `CanLoad` guard takes precedence over the preload strategy.
几步之前,你刚刚给 `AdminModule` 中的路由添加了 `CanLoad` 守卫,以阻塞加载那个模块,直到用户认证结束。
`CanLoad` 守卫的优先级高于预加载策略。
If you want to preload a module as well as guard against unauthorized access, remove the `canLoad()` guard method and rely on the [canActivate()](#can-activate-guard) guard alone.
如果你要加载一个模块并且保护它防止未授权访问,请移除 `canLoad` 守卫,只单独依赖[CanActivate](guide/router#can-activate-guard)守卫。
{@a custom-preloading}
### Custom Preloading Strategy
### 自定义预加载策略
Preloading every lazy loaded module works well in many situations.
However, in consideration of things such as low bandwidth and user metrics, you can use a custom preloading strategy for specific feature modules.
在很多场景下,预加载的每个惰性加载模块都能正常工作。但是,考虑到低带宽和用户指标等因素,可以为特定的特性模块使用自定义预加载策略。
This section guides you through adding a custom strategy that only preloads routes whose `data.preload` flag is set to `true`.
Recall that you can add anything to the `data` property of a route.
本节将指导你添加一个自定义策略,它只预加载 `data.preload` 标志为 `true` 路由。回想一下,你可以在路由的 `data` 属性中添加任何东西。
Set the `data.preload` flag in the `crisis-center` route in the `AppRoutingModule`.
在 `AppRoutingModule` 的 `crisis-center` 路由中设置 `data.preload` 标志。
Generate a new `SelectivePreloadingStrategy` service.
生成一个新的 `SelectivePreloadingStrategy` 服务。
ng generate service selective-preloading-strategy
Replace the contents of `selective-preloading-strategy.service.ts` with the following:
使用下列内容替换 `selective-preloading-strategy.service.ts`:
`SelectivePreloadingStrategyService` implements the `PreloadingStrategy`, which has one method, `preload()`.
`SelectivePreloadingStrategyService` 实现了 `PreloadingStrategy`,它有一个方法 `preload()`。
The router calls the `preload()` method with two arguments:
路由器会用两个参数来调用 `preload()` 方法:
1. The route to consider.
要加载的路由。
1. A loader function that can load the routed module asynchronously.
一个加载器(loader)函数,它能异步加载带路由的模块。
An implementation of `preload` must return an `Observable`.
If the route does preload, it returns the observable returned by calling the loader function.
If the route does not preload, it returns an `Observable` of `null`.
`preload` 的实现要返回一个 `Observable`。
如果该路由应该预加载,它就会返回调用加载器函数所返回的 `Observable`。
如果该路由*不*应该预加载,它就返回一个 `null` 值的 `Observable` 对象。
In this sample, the `preload()` method loads the route if the route's `data.preload` flag is truthy.
在这个例子中,如果路由的 `data.preload` 标志是真值,则 `preload()` 方法会加载该路由。
As a side-effect, `SelectivePreloadingStrategyService` logs the `path` of a selected route in its public `preloadedModules` array.
它的副作用是 `SelectivePreloadingStrategyService` 会把所选路由的 `path` 记录在它的公共数组 `preloadedModules` 中。
Shortly, you'll extend the `AdminDashboardComponent` to inject this service and display its `preloadedModules` array.
很快,你就会扩展 `AdminDashboardComponent` 来注入该服务,并且显示它的 `preloadedModules` 数组。
But first, make a few changes to the `AppRoutingModule`.
但是首先,要对 `AppRoutingModule` 做少量修改。
1. Import `SelectivePreloadingStrategyService` into `AppRoutingModule`.
把 `SelectivePreloadingStrategyService` 导入到 `AppRoutingModule` 中。
1. Replace the `PreloadAllModules` strategy in the call to `forRoot()` with this `SelectivePreloadingStrategyService`.
把 `PreloadAllModules` 策略替换成对 `forRoot()` 的调用,并且传入这个 `SelectivePreloadingStrategyService`。
1. Add the `SelectivePreloadingStrategyService` strategy to the `AppRoutingModule` providers array so you can inject it elsewhere in the app.
把 `SelectivePreloadingStrategyService` 策略添加到 `AppRoutingModule` 的 `providers` 数组中,以便它可以注入到应用中的任何地方。
Now edit the `AdminDashboardComponent` to display the log of preloaded routes.
现在,编辑 `AdminDashboardComponent` 以显示这些预加载路由的日志。
1. Import the `SelectivePreloadingStrategyService`.
导入 `SelectivePreloadingStrategyService`(它是一个服务)。
1. Inject it into the dashboard's constructor.
把它注入到仪表盘的构造函数中。
1. Update the template to display the strategy service's `preloadedModules` array.
修改模板来显示这个策略服务的 `preloadedModules` 数组。
Now the file is as follows:
现在文件如下:
Once the application loads the initial route, the `CrisisCenterModule` is preloaded.
Verify this by logging in to the `Admin` feature area and noting that the `crisis-center` is listed in the `Preloaded Modules`.
It also logs to the browser's console.
一旦应用加载完了初始路由,`CrisisCenterModule` 也被预加载了。
通过 `Admin` 特性区中的记录就可以验证它,“Preloaded Modules”中列出了 `crisis-center`。
它也被记录到了浏览器的控制台。
{@a redirect-advanced}
### Migrating URLs with redirects
### 使用重定向迁移 URL
You've setup the routes for navigating around your application and used navigation imperatively and declaratively.
But like any application, requirements change over time.
You've setup links and navigation to `/heroes` and `/hero/:id` from the `HeroListComponent` and `HeroDetailComponent` components.
If there were a requirement that links to `heroes` become `superheroes`, you would still want the previous URLs to navigate correctly.
You also don't want to update every link in your application, so redirects makes refactoring routes trivial.
你已经设置好了路由,并且用命令式和声明式的方式导航到了很多不同的路由。但是,任何应用的需求都会随着时间而改变。
你把链接 `/heroes` 和 `hero/:id` 指向了 `HeroListComponent` 和 `HeroDetailComponent` 组件。
如果有这样一个需求,要把链接 `heroes` 变成 `superheroes`,你可能仍然希望以前的 URL 能正常导航。
但你也不想在应用中找到并修改每一个链接,这时候,重定向就可以省去这些琐碎的重构工作。
{@a url-refactor}
#### Changing `/heroes` to `/superheroes`
#### 把 `/heroes` 改为 `/superheroes`
This section guides you through migrating the `Hero` routes to new URLs.
The `Router` checks for redirects in your configuration before navigating, so each redirect is triggered when needed. To support this change, add redirects from the old routes to the new routes in the `heroes-routing.module`.
本节将指导你将 `Hero` 路由迁移到新的 URL。在导航之前,`Router` 会检查路由配置中的重定向语句,以便将来按需触发重定向。要支持这种修改,你就要在 `heroes-routing.module` 文件中把老的路由重定向到新的路由。
Notice two different types of redirects.
The first change is from `/heroes` to `/superheroes` without any parameters.
The second change is from `/hero/:id` to `/superhero/:id`, which includes the `:id` route parameter.
Router redirects also use powerful pattern-matching, so the `Router` inspects the URL and replaces route parameters in the `path` with their appropriate destination.
Previously, you navigated to a URL such as `/hero/15` with a route parameter `id` of `15`.
注意,这里有两种类型的重定向。第一种是不带参数的从 `/heroes` 重定向到 `/superheroes`。这是一种非常直观的重定向。第二种是从 `/hero/:id` 重定向到 `/superhero/:id`,它还要包含一个 `:id` 路由参数。
路由器重定向时使用强大的模式匹配功能,这样,路由器就会检查 URL,并且把 `path` 中带的路由参数替换成相应的目标形式。以前,你导航到形如 `/hero/15` 的 URL 时,带了一个路由参数 `id`,它的值是 `15`。
The `Router` also supports [query parameters](#query-parameters) and the [fragment](#fragment) when using redirects.
在重定向的时候,路由器还支持[查询参数](#query-parameters)和[片段(fragment)](#fragment)。
* When using absolute redirects, the `Router` will use the query parameters and the fragment from the `redirectTo` in the route config.
当使用绝对地址重定向时,路由器将会使用路由配置的 `redirectTo` 属性中规定的查询参数和片段。
* When using relative redirects, the `Router` use the query params and the fragment from the source URL.
当使用相对地址重定向时,路由器将会使用源地址(跳转前的地址)中的查询参数和片段。
Currently, the empty path route redirects to `/heroes`, which redirects to `/superheroes`.
This won't work because the `Router` handles redirects once at each level of routing configuration.
This prevents chaining of redirects, which can lead to endless redirect loops.
目前,空路径被重定向到了 `/heroes`,它又被重定向到了 `/superheroes`。这样不行,因为 `Router` 在每一层的路由配置中只会处理一次重定向。这样可以防止出现无限循环的重定向。
Instead, update the empty path route in `app-routing.module.ts` to redirect to `/superheroes`.
所以,你要在 `app-routing.module.ts` 中修改空路径路由,让它重定向到 `/superheroes`。
A `routerLink` isn't tied to route configuration, so update the associated router links to remain active when the new route is active.
Update the `app.component.ts` template for the `/heroes` `routerLink`.
由于 `routerLink` 与路由配置无关,所以你要修改相关的路由链接,以便在新的路由激活时,它们也能保持激活状态。还要修改 `app.component.ts` 模板中的 `/heroes` 这个 `routerLink`。
Update the `goToHeroes()` method in the `hero-detail.component.ts` to navigate back to `/superheroes` with the optional route parameters.
修改 `hero-detail.component.ts` 中的 `goToHeroes()` 方法,使用可选的路由参数导航回 `/superheroes`。
With the redirects setup, all previous routes now point to their new destinations and both URLs still function as intended.
当这些重定向设置好之后,所有以前的路由都指向了它们的新目标,并且每个 URL 也仍然能正常工作。
{@a inspect-config}
### Inspect the router's configuration
### 审查路由器配置
To determine if your routes are actually evaluated [in the proper order](#routing-module-order), you can inspect the router's configuration.
要确定你的路由是否真的[按照正确的顺序](#routing-module-order)执行的,你可以审查路由器的配置。
Do this by injecting the router and logging to the console its `config` property.
For example, update the `AppModule` as follows and look in the browser console window
to see the finished route configuration.
可以通过注入路由器并在控制台中记录其 `config` 属性来实现。
例如,把 `AppModule` 修改为这样,并在浏览器的控制台窗口中查看最终的路由配置。
{@a final-app}
## Final app
## 最终的应用
For the completed router app, see the for the final source code.
对这个已完成的路由器应用,参见 的最终代码。
{@a link-parameters-array}
## Link parameters array
## 链接参数数组
A link parameters array holds the following ingredients for router navigation:
链接参数数组保存路由导航时所需的成分:
* The path of the route to the destination component.
指向目标组件的那个路由的路径(path)
* Required and optional route parameters that go into the route URL.
必备路由参数和可选路由参数,它们将进入该路由的 URL
You can bind the `RouterLink` directive to such an array like this:
你可以把 `RouterLink` 指令绑定到一个数组,就像这样:
The following is a two-element array when specifying a route parameter:
在指定路由参数时,使用如下的两元素数组:
You can provide optional route parameters in an object, as in `{ foo: 'foo' }`:
你可以在对象中提供可选的路由参数,比如 `{ foo: 'foo' }` :
These three examples cover the needs of an app with one level of routing.
However, with a child router, such as in the crisis center, you create new link array possibilities.
这三个例子涵盖了你在单级路由的应用中所需的一切。不过,在你添加一个像*危机中心*一样的子路由时,你可以创建新链接数组。
The following minimal `RouterLink` example builds upon a specified [default child route](guide/router#a-crisis-center-with-child-routes) for the crisis center.
下面这个最小化 `RouterLink` 例子是基于危机中心指定的[默认子路由](guide/router#a-crisis-center-with-child-routes)构建的。
Note the following:
请注意以下事项:
* The first item in the array identifies the parent route (`/crisis-center`).
数组中的第一个条目标记出了父路由(`/crisis-center`)。
* There are no parameters for this parent route.
这个父路由没有参数。
* There is no default for the child route so you need to pick one.
没有默认的子路由,因此你得选取一个。
* You're navigating to the `CrisisListComponent`, whose route path is `/`, but you don't need to explicitly add the slash.
你决定跳转到 `CrisisListComponent`,它的路由路径是'/',但你不用显式的添加它。
Consider the following router link that navigates from the root of the application down to the Dragon Crisis:
考虑以下路由器链接,它将从应用的根目录导航到巨龙危机(Dragon Crisis):
* The first item in the array identifies the parent route (`/crisis-center`).
数组中的第一个条目标记出了父路由(`/crisis-center`)。
* There are no parameters for this parent route.
这个父路由没有参数。
* The second item identifies the child route details about a particular crisis (`/:id`).
数组中的第二个条目('/:id')用来标记出到指定危机的详情页的子路由。
* The details child route requires an `id` route parameter.
详细的子路由需要一个 `id` 路由参数。
* You added the `id` of the Dragon Crisis as the second item in the array (`1`).
你把*巨龙危机*的 `id` 添加为该数组中的第二个条目(`1`)。
* The resulting path is `/crisis-center/1`.
最终生成的路径是 `/crisis-center/1`。
You could also redefine the `AppComponent` template with Crisis Center routes exclusively:
你也可以把危机中心的路由单独重新定义为 `AppComponent` 的模板:
In summary, you can write applications with one, two or more levels of routing.
The link parameters array affords the flexibility to represent any routing depth and any legal sequence of route paths, (required) router parameters, and (optional) route parameter objects.
总之,你可以用一级、两级或多级路由来写应用程序。
链接参数数组提供了用来表示任意深度路由的链接参数数组以及任意合法的路由参数序列、必须的路由器参数以及可选的路由参数对象。
{@a browser-url-styles}
{@a location-strategy}
## `LocationStrategy` and browser URL styles
## `LocationStrategy` 和浏览器的网址样式
When the router navigates to a new component view, it updates the browser's location and history with a URL for that view.
As this is a strictly local URL the browser won't send this URL to the server and will not reload the page.
当路由器导航到一个新的组件视图时,它会用该视图的 URL 来更新浏览器的当前地址以及历史。
严格来说,这个 URL 其实是本地的,浏览器不会把该 URL 发给服务器,并且不会重新加载此页面。
Modern HTML5 browsers support history.pushState, a technique that changes a browser's location and history without triggering a server page request.
The router can compose a "natural" URL that is indistinguishable from one that would otherwise require a page load.
现代 HTML 5 浏览器支持[history.pushState](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries) API,
这是一项可以改变浏览器的当前地址和历史,却又不会触发服务端页面请求的技术。
路由器可以合成出一个“自然的”URL,它看起来和那些需要进行页面加载的 URL 没什么区别。
Here's the Crisis Center URL in this "HTML5 pushState" style:
下面是危机中心的 URL 在“HTML 5 pushState”风格下的样子:
localhost:3002/crisis-center/
Older browsers send page requests to the server when the location URL changes unless the change occurs after a "#" (called the "hash").
Routers can take advantage of this exception by composing in-application route URLs with hashes.
Here's a "hash URL" that routes to the Crisis Center.
老旧的浏览器在当前地址的 URL 变化时总会往服务器发送页面请求……唯一的例外规则是:当这些变化位于“#”(被称为“hash”)后面时不会发送。通过把应用内的路由 URL 拼接在 `#` 之后,路由器可以获得这条“例外规则”带来的优点。下面是到*危机中心*路由的“hash URL”:
localhost:3002/src/#/crisis-center/
The router supports both styles with two `LocationStrategy` providers:
路由器通过两种 `LocationStrategy` 提供者来支持所有这些风格:
1. `PathLocationStrategy`—the default "HTML5 pushState" style.
`PathLocationStrategy` - 默认的策略,支持“HTML 5 pushState”风格。
1. `HashLocationStrategy`—the "hash URL" style.
`HashLocationStrategy` - 支持“hash URL”风格。
The `RouterModule.forRoot()` function sets the `LocationStrategy` to the `PathLocationStrategy`, which makes it the default strategy.
You also have the option of switching to the `HashLocationStrategy` with an override during the bootstrapping process.
`RouterModule.forRoot()` 函数把 `LocationStrategy` 设置成了 `PathLocationStrategy`,使其成为了默认策略。
你还可以在启动过程中改写(override)它,来切换到 `HashLocationStrategy` 风格。
For more information on providers and the bootstrap process, see [Dependency Injection](guide/dependency-injection#bootstrap).
有关提供程序和引导过程的更多信息,请参阅[依赖注入](guide/dependency-injection#bootstrap)。
## Choosing a routing strategy
## 选择路由策略
You must choose a routing strategy early in the development of you project because once the application is in production, visitors to your site use and depend on application URL references.
你必须在开发项目的早期就选择一种路由策略,因为一旦该应用进入了生产阶段,你网站的访问者就会使用并依赖应用的这些 URL 引用。
Almost all Angular projects should use the default HTML5 style.
It produces URLs that are easier for users to understand and it preserves the option to do server-side rendering.
几乎所有的 Angular 项目都会使用默认的 HTML 5 风格。它生成的 URL 更易于被用户理解,它也为将来做**服务端渲染**预留了空间。
Rendering critical pages on the server is a technique that can greatly improve perceived responsiveness when the app first loads.
An app that would otherwise take ten or more seconds to start could be rendered on the server and delivered to the user's device in less than a second.
在服务器端渲染指定的页面,是一项可以在该应用首次加载时大幅提升响应速度的技术。那些原本需要十秒甚至更长时间加载的应用,可以预先在服务端渲染好,并在少于一秒的时间内完整渲染在用户的设备上。
This option is only available if application URLs look like normal web URLs without hashes (#) in the middle.
只有当应用的 URL 看起来像是标准的 Web URL,中间没有 hash(#)时,这个选项才能生效。
## ``
The router uses the browser's history.pushState for navigation.
`pushState` allows you to customize in-app URL paths; for example, `localhost:4200/crisis-center`.
The in-app URLs can be indistinguishable from server URLs.
路由器使用浏览器的 history.pushState API 进行导航。借助 `pushState` 你自定义应用中的 URL 路径 `localhost:4200/crisis-center`,应用内的 URL 和服务器的 URL 没有区别。
Modern HTML5 browsers were the first to support `pushState` which is why many people refer to these URLs as "HTML5 style" URLs.
现代的 HTML5 浏览器都支持 `pushState`,这也就是为什么很多人把这种 URL 形式称为 "HTML 5" 风格的 URL。
HTML5 style navigation is the router default.
In the [LocationStrategy and browser URL styles](#browser-url-styles) section, learn why HTML5 style is preferable, how to adjust its behavior, and how to switch to the older hash (#) style, if necessary.
路由器默认使用 HTML5 风格的导航。
在 [LocationStrategy 与浏览器 URL 风格](#browser-url-styles)部分,你可以了解为何推荐使用 HTML5 风格的 URL,如何调整其行为,以及必要时如何切换到老式的 hash(#)风格。
You must add a <base href> element to the app's `index.html` for `pushState` routing to work.
The browser uses the `` value to prefix relative URLs when referencing CSS files, scripts, and images.
你必须在应用的 `index.html` 中**添加一个 <base href> 元素**才能让 `pushState` 路由正常工作。
浏览器要用 `` 的值为引用 CSS、脚本和图片文件时使用的*相对* URL 添加前缀。
Add the `` element just after the `` tag.
If the `app` folder is the application root, as it is for this application,
set the `href` value in `index.html` as shown here.
请把 `` 元素添加在 `` 标签的紧后面。如果应用的根目录是 `app` 目录,那么就可以像这个应用程序一样,设置 **`index.html`** 中的 `href` 值。代码如下。
### HTML5 URLs and the ``
### HTML5 网址和 ``
While the router uses the HTML5 pushState style by default, you must configure that strategy with a ``.
由于路由器默认使用 “HTML 5 pushState” 风格,所以你*必须*用一个 `` 来配置该策略(Strategy)。
The preferred way to configure the strategy is to add a <base href> element tag in the `` of the `index.html`.
配置该策略的首选方式是往 `index.html` 的 `` 中添加一个[<base href> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)标签。
Without that tag, the browser may not be able to load resources
(images, CSS, scripts) when "deep linking" into the app.
如果没有该标记,浏览器就可能无法在“深度链接”进入应用时加载资源(图片,CSS,脚本)。
Some developers may not be able to add the `` element, perhaps because they don't have access to `` or the `index.html`.
有些开发人员可能无法添加 `` 元素,这可能是因为它们没有访问 `` 或 `index.html` 的权限。
Those developers may still use HTML5 URLs by taking the following two steps:
它们仍然可以使用 HTML 5 格式的 URL,但要采取如下步骤进行补救:
1. Provide the router with an appropriate [APP_BASE_HREF][] value.
用适当的[APP_BASE_HREF][]值提供(provide)路由器。
1. Use root URLs for all web resources: CSS, images, scripts, and template HTML files.
对所有 Web 资源使用绝对地址:CSS、图片、脚本、模板 HTML。
{@a hashlocationstrategy}
### `HashLocationStrategy`
You can use `HashLocationStrategy` by providing the `useHash: true` in an object as the second argument of the `RouterModule.forRoot()` in the `AppModule`.
你可以在根模块的 `RouterModule.forRoot()` 的第二个参数中传入一个带有 `useHash: true` 的对象,以回到基于 `HashLocationStrategy` 的传统方式。
## Router Reference
## 路由器参考手册
The folllowing sections highlight some core router concepts.
下面的部分重点介绍了一些路由器的核心概念。
{@a basics-router-imports}
### Router imports
### 路由器导入
The Angular Router is an optional service that presents a particular component view for a given URL.
It is not part of the Angular core and thus is in its own library package, `@angular/router`.
Angular 的 Router 是一个可选服务,它为指定的 URL 提供特定的组件视图。它不是 Angular 核心的一部分,因此它位于自己的包 `@angular/router` 中。
Import what you need from it as you would from any other Angular package.
从任何其它的 Angular 包中导入你需要的东西。
For more on browser URL styles, see [`LocationStrategy` and browser URL styles](#browser-url-styles).
有关浏览器 URL 风格的更多信息,请参阅 [`LocationStrategy` 和浏览器 URL 风格](#browser-url-styles)。
{@a basics-config}
### Configuration
### 配置
A routed Angular application has one singleton instance of the `Router` service.
When the browser's URL changes, that router looks for a corresponding `Route` from which it can determine the component to display.
带路由的 Angular 应用中有一个 `Router` 服务的单例实例。当浏览器的 URL 发生变化时,该路由器会查找相应的 `Route`,以便根据它确定要显示的组件。
A router has no routes until you configure it.
The following example creates five route definitions, configures the router via the `RouterModule.forRoot()` method, and adds the result to the `AppModule`'s `imports` array.
在配置之前,路由器没有任何路由。下面的例子创建了五个路由定义,通过 `RouterModule.forRoot()` 方法配置路由器,并把结果添加到 `AppModule` 的 `imports` 数组中。
{@a example-config}
The `appRoutes` array of routes describes how to navigate.
Pass it to the `RouterModule.forRoot()` method in the module `imports` to configure the router.
`appRoutes` 路由数组描述了如何导航。把它传给模块的 `imports` 数组中的 `RouterModule.forRoot()` 方法来配置路由器。
Each `Route` maps a URL `path` to a component.
There are no leading slashes in the path.
The router parses and builds the final URL for you, which allows you to use both relative and absolute paths when navigating between application views.
每个 `Route` 都会把一个 URL `path` 映射到一个组件。路径中没有前导斜杠。路由器会为你解析并构建最终的 URL,这样你就可以在应用视图中导航时使用相对路径和绝对路径了。
The `:id` in the second route is a token for a route parameter.
In a URL such as `/hero/42`, "42" is the value of the `id` parameter.
The corresponding `HeroDetailComponent` uses that value to find and present the hero whose `id` is 42.
第二个路由中的 `:id` 是路由参数的令牌。在像 `/hero/42` 这样的 URL 中,“42”是 `id` 参数的值。相应的 `HeroDetailComponent` 用这个值来查找并显示 `id` 为 42 的英雄。
The `data` property in the third route is a place to store arbitrary data associated with
this specific route.
The data property is accessible within each activated route. Use it to store items such as page titles, breadcrumb text, and other read-only, static data.
You can use the [resolve guard](#resolve-guard) to retrieve dynamic data.
第三个路由中的 `data` 属性是存放与该特定路由关联的任意数据的地方。每个激活的路由都可以访问 `data` 属性。可以用它来存储页面标题,面包屑文本和其它只读静态数据等项目。你可以尝试使用[解析器守卫](#resolve-guard)来检索动态数据。
The empty path in the fourth route represents the default path for the application—the place to go when the path in the URL is empty, as it typically is at the start.
This default route redirects to the route for the `/heroes` URL and, therefore, displays the `HeroesListComponent`.
第四个路由中的空路径表示该应用的默认路径 - 当 URL 中的路径为空时通常要去的地方,就像它在刚进来时一样。这个默认路由重定向到了 `/heroes` 这个 URL 的路由,因此会显示 `HeroesListComponent`。
If you need to see what events are happening during the navigation lifecycle, there is the `enableTracing` option as part of the router's default configuration.
This outputs each router event that took place during each navigation lifecycle to the browser console.
Use `enableTracing` only for debugging purposes.
You set the `enableTracing: true` option in the object passed as the second argument to the `RouterModule.forRoot()` method.
如果你需要查看导航生命周期中发生了什么事件,可以把 `enableTracing` 选项作为路由器默认配置的一部分。这会把每个导航生命周期中发生的每个路由器事件都输出到浏览器控制台中。`enableTracing` 只会用于调试目的。你可以把 `enableTracing: true` 选项作为第二个参数传给 `RouterModule.forRoot()` 方法。
{@a basics-router-outlet}
### Router outlet
### 路由出口
The `RouterOutlet` is a directive from the router library that is used like a component.
It acts as a placeholder that marks the spot in the template where the router should
display the components for that outlet.
`RouterOutlet` 是一个来自路由器库的指令,虽然它的用法像组件一样。它充当占位符,用于在模板中标记出路由器应该显示把该组件显示在那个出口的位置。
<router-outlet></router-outlet>
<!-- Routed components go here -->
Given the configuration above, when the browser URL for this application becomes `/heroes`, the router matches that URL to the route path `/heroes` and displays the `HeroListComponent` as a sibling element to the `RouterOutlet` that you've placed in the host component's template.
对于上面的配置,当这个应用的浏览器 URL 变为 `/heroes` 时,路由器就会把这个 URL 与路由路径 `/heroes` 匹配,并把 `HeroListComponent` 作为兄弟元素显示在宿主组件模板中的 `RouterOutlet` 下方。
{@a basics-router-links}
{@a router-link}
### Router links
### 路由链接
To navigate as a result of some user action such as the click of an anchor tag, use `RouterLink`.
要想通过某些用户操作(比如单击一下 a 标签)进行导航,请使用 `RouterLink`。
Consider the following template:
考虑下面的模板:
The `RouterLink` directives on the anchor tags give the router control over those elements.
The navigation paths are fixed, so you can assign a string to the `routerLink` (a "one-time" binding).
a 标签上的 `RouterLink` 指令让路由器可以控制这些元素。导航路径是固定的,所以你可以给 `routerLink` 赋值一个字符串(“一次性”绑定)。
Had the navigation path been more dynamic, you could have bound to a template expression that returned an array of route link parameters; that is, the [link parameters array](guide/router#link-parameters-array).
The router resolves that array into a complete URL.
如果导航路径更加动态,你可以给它绑定到一个模板表达式,该表达式要返回一个[链接参数数组](guide/router#link-parameters-array)。路由器会把该数组解析成一个完整的 URL。
{@a router-link-active}
### Active router links
### 活动路由链路
The `RouterLinkActive` directive toggles CSS classes for active `RouterLink` bindings based on the current `RouterState`.
`RouterLinkActive` 指令会根据当前的 `RouterState` 切换活动 `RouterLink` 上所绑定的 CSS 类。
On each anchor tag, you see a [property binding](guide/template-syntax#property-binding) to the `RouterLinkActive` directive that looks like `routerLinkActive="..."`.
在每个 a 标签上,你会看到一个到 `RouterLinkActive` 指令的[属性绑定](guide/template-syntax#property-binding),就像 `routerLinkActive="..."`。
The template expression to the right of the equal sign, `=`, contains a space-delimited string of CSS classes that the Router adds when this link is active (and removes when the link is inactive).
You set the `RouterLinkActive` directive to a string of classes such as `[routerLinkActive]="'active fluffy'"` or bind it to a component property that returns such a string.
等号 `=` 右侧的模板表达式,包含一个以空格分隔的 CSS 类字符串,当这个链接处于活动状态时,路由器就会加上这些字符串(并在非活动状态时删除)。你可以把 `RouterLinkActive` 指令设置成一串类的字符串,比如 `[routerLinkActive]="'active fluffy'"`,也可以把它绑定到一个返回这样一个字符串的组件属性上。
Active route links cascade down through each level of the route tree, so parent and child router links can be active at the same time.
To override this behavior, you can bind to the `[routerLinkActiveOptions]` input binding with the `{ exact: true }` expression. By using `{ exact: true }`, a given `RouterLink` will only be active if its URL is an exact match to the current URL.
活动路由链接会级联到路由树的每个级别,这样父路由和子路由链接就可以同时处于活动状态。要覆盖这种行为,你可以用 `{ exact: true }` 表达式绑定到 `[routerLinkActiveOptions]` 输入绑定。使用 `{ exact: true }` 之后,给定的 `RouterLink` 只有在 URL 与当前 URL 完全匹配时才会激活。
{@a basics-router-state}
### Router state
### 路由器状态
After the end of each successful navigation lifecycle, the router builds a tree of `ActivatedRoute` objects that make up the current state of the router. You can access the current `RouterState` from anywhere in the application using the `Router` service and the `routerState` property.
每个成功的导航生命周期结束后,路由器都会构建一个 `ActivatedRoute` 对象树,它构成了路由器的当前状态。你可以从任何地方使用应用的 `Router` 服务和 `routerState` 属性来访问当前的 `RouterState`。
Each `ActivatedRoute` in the `RouterState` provides methods to traverse up and down the route tree to get information from parent, child and sibling routes.
`RouterState` 中的每个 `ActivatedRoute` 都提供了向上或向下遍历路由树的方法,用于从父路由、子路由和兄弟路由中获取信息。
{@a activated-route}
### Activated route
### 激活路由
The route path and parameters are available through an injected router service called the [ActivatedRoute](api/router/ActivatedRoute).
It has a great deal of useful information including:
路由的路径和参数可以通过注入名为 [ActivatedRoute](api/router/ActivatedRoute) 的路由服务获得。它提供了大量有用的信息,包括:
|
Property
属性
|
Description
说明
|
url
|
An `Observable` of the route path(s), represented as an array of strings for each part of the route path.
一个路由路径的 `Observable`,是一个由路由路径的各个部分组成的字符串数组。
|
data
|
An `Observable` that contains the `data` object provided for the route.
Also contains any resolved values from the [resolve guard](#resolve-guard).
包含提供给当前路由的 `data` 对象的 `Observable`。
也包含任何由[解析守卫](#resolve-guard)解析出的值。
|
paramMap
|
An `Observable` that contains a [map](api/router/ParamMap) of the required and [optional parameters](#optional-route-parameters) specific to the route.
The map supports retrieving single and multiple values from the same parameter.
一个包含该路由的必要参数和[可选参数](#optional-route-parameters) [map](api/router/ParamMap) 的 `Observable`。
这个 map 支持从同一个参数中获得单个或多个值。
|
queryParamMap
|
An `Observable` that contains a [map](api/router/ParamMap) of the [query parameters](#query-parameters) available to all routes.
The map supports retrieving single and multiple values from the query parameter.
一个包含适用于所有路由的[查询参数](#query-parameters) [map](api/router/ParamMap) 的 `Observable`。
这个 map 支持从同一个查询参数中获得单个或多个值。
|
fragment
|
An `Observable` of the URL [fragment](#fragment) available to all routes.
一个适用于所有路由的 URL [片段](#fragment)的 `Observable`。
|
outlet
|
The name of the `RouterOutlet` used to render the route.
For an unnamed outlet, the outlet name is primary.
用来渲染该路由的 `RouterOutlet` 的名字。
对于无名出口,这个出口的名字是 `primary`。
|
routeConfig
|
The route configuration used for the route that contains the origin path.
包含原始路径的那个路由的配置信息。
|
parent
|
The route's parent `ActivatedRoute` when this route is a [child route](#child-routing-component).
当该路由是[子路由](#child-routing-component)时,表示该路由的父级 `ActivatedRoute`。
|
firstChild
|
Contains the first `ActivatedRoute` in the list of this route's child routes.
包含该路由的子路由列表中的第一个 `ActivatedRoute`。
|
children
|
Contains all the [child routes](#child-routing-component) activated under the current route.
包含当前路由下所有激活的[子路由](#child-routing-component)。
|
Two older properties are still available, however, their replacements are preferable as they may be deprecated in a future Angular version.
还有两个较旧的属性,但更推荐使用它们的替代品,因为它们可能会在以后的 Angular 版本中弃用。
* `params`: An `Observable` that contains the required and [optional parameters](#optional-route-parameters) specific to the route. Use `paramMap` instead.
`params` :一个 `Observable`,它包含专属于该路由的必要参数和[可选参数](#optional-route-parameters)。请改用 `paramMap`。
* `queryParams`: An `Observable` that contains the [query parameters](#query-parameters) available to all routes.
Use `queryParamMap` instead.
`queryParams`:一个包含可用于所有路由的[查询参数](#query-parameters)的 `Observable`。请改用 `queryParamMap`。
### Router events
### 路由器事件
During each navigation, the `Router` emits navigation events through the `Router.events` property.
These events range from when the navigation starts and ends to many points in between. The full list of navigation events is displayed in the table below.
`Router` 在每次导航过程中都会通过 `Router.events` 属性发出导航事件。这些事件的范围贯穿从导航开始和结束之间的多个时间点。导航事件的完整列表如下表所示。
|
Router Event
路由事件
|
Description
说明
|
NavigationStart
|
An [event](api/router/NavigationStart) triggered when navigation starts.
导航开始时触发的[事件](api/router/NavigationStart)。
|
RouteConfigLoadStart
|
An [event](api/router/RouteConfigLoadStart) triggered before the `Router`
[lazy loads](#asynchronous-routing) a route configuration.
在 `Router` [惰性加载](#asynchronous-routing)路由配置之前触发的[事件](api/router/RouteConfigLoadStart)。
|
RouteConfigLoadEnd
|
An [event](api/router/RouteConfigLoadEnd) triggered after a route has been lazy loaded.
在某个路由已经惰性加载完毕时触发的[事件](api/router/RouteConfigLoadEnd)。
|
RoutesRecognized
|
An [event](api/router/RoutesRecognized) triggered when the Router parses the URL and the routes are recognized.
当路由器解析了 URL,而且路由已经识别完毕时触发的[事件](api/router/RoutesRecognized)。
|
GuardsCheckStart
|
An [event](api/router/GuardsCheckStart) triggered when the Router begins the Guards phase of routing.
当路由器开始进入路由守卫阶段时触发的[事件](api/router/GuardsCheckStart)。
|
ChildActivationStart
|
An [event](api/router/ChildActivationStart) triggered when the Router begins activating a route's children.
当路由器开始激活某路由的子路由时触发的[事件](api/router/ChildActivationStart)。
|
ActivationStart
|
An [event](api/router/ActivationStart) triggered when the Router begins activating a route.
当路由器开始激活某个路由时触发的[事件](api/router/ActivationStart)。
|
GuardsCheckEnd
|
An [event](api/router/GuardsCheckEnd) triggered when the Router finishes the Guards phase of routing successfully.
当路由器成功结束了路由守卫阶段时触发的[事件](api/router/GuardsCheckEnd)。
|
ResolveStart
|
An [event](api/router/ResolveStart) triggered when the Router begins the Resolve phase of routing.
当路由器开始路由解析阶段时触发的[事件](api/router/ResolveStart)。
|
ResolveEnd
|
An [event](api/router/ResolveEnd) triggered when the Router finishes the Resolve phase of routing successfuly.
当路由器的路由解析阶段成功完成时触发的[事件](api/router/ResolveEnd)。
|
ChildActivationEnd
|
An [event](api/router/ChildActivationEnd) triggered when the Router finishes activating a route's children.
当路由器成功激活某路由的子路由时触发的[事件](api/router/ChildActivationEnd)。
|
ActivationEnd
|
An [event](api/router/ActivationStart) triggered when the Router finishes activating a route.
当路由器成功激活了某个路由时触发的[事件](api/router/ActivationStart)。
|
NavigationEnd
|
An [event](api/router/NavigationEnd) triggered when navigation ends successfully.
当导航成功结束时触发的[事件](api/router/NavigationEnd)。
|
NavigationCancel
|
An [event](api/router/NavigationCancel) triggered when navigation is canceled.
This can happen when a [Route Guard](#guards) returns false during navigation,
or redirects by returning a `UrlTree`.
当导航被取消时触发的[事件](api/router/NavigationCancel)。
这可能在导航期间某个[路由守卫](#guards)返回了 false 或返回了 `UrlTree` 以进行重定向时发生。
|
NavigationError
|
An [event](api/router/NavigationError) triggered when navigation fails due to an unexpected error.
当导航由于非预期的错误而失败时触发的[事件](api/router/NavigationError)。
|
Scroll
|
An [event](api/router/Scroll) that represents a scrolling event.
用来表示滚动的[事件](api/router/Scroll)。
|
When you enable the `enableTracing` option, Angular logs these events to the console.
For an example of filtering router navigation events, see the [router section](guide/observables-in-angular#router) of the [Observables in Angular](guide/observables-in-angular) guide.
当启用了 `enableTracing` 选项时,Angular 会把这些事件都记录到控制台。有关筛选路由器导航事件的示例,请参阅 [Angular 中的 Observables](guide/observables-in-angular) 一章的[路由器部分](guide/observables-in-angular#router)。
### Router terminology
### 路由器术语
Here are the key `Router` terms and their meanings:
这里是一些关键的 `Router` 术语及其含义:
|
Router Part
路由器部件
|
Meaning
含义
|
Router
|
Displays the application component for the active URL.
Manages navigation from one component to the next.
为活动 URL 显示应用中的组件。
管理从一个组件到另一个的导航。
|
RouterModule
|
A separate NgModule that provides the necessary service providers
and directives for navigating through application views.
一个单独的 NgModule,它提供了一些必要的服务提供者和一些用于在应用视图间导航的指令。
|
Routes
|
Defines an array of Routes, each mapping a URL path to a component.
定义一个路由数组,每一个条目都会把一个 URL 路径映射到组件。
|
Route
|
Defines how the router should navigate to a component based on a URL pattern.
Most routes consist of a path and a component type.
定义路由器如何基于一个 URL 模式导航到某个组件。
大部分路由都由一个路径和一个组件类组成。
|
RouterOutlet
|
The directive (<router-outlet>) that marks where the router displays a view.
该指令 (<router-outlet>) 用于指出路由器应该把视图显示在哪里。
|
RouterLink
|
The directive for binding a clickable HTML element to a route. Clicking an element with a routerLink directive that is bound to a string or a link parameters array triggers a navigation.
用于将可点击的 HTML 元素绑定到某个路由的指令。单击带有 routerLink 指令且绑定到字符串或链接参数数组的元素,将触发导航。
|
RouterLinkActive
|
The directive for adding/removing classes from an HTML element when an associated routerLink contained on or inside the element becomes active/inactive.
该指令会在元素上或元素内包含的相关 routerLink 处于活动/非活动状态时,从 HTML 元素上添加/移除类。
|
ActivatedRoute
|
A service that is provided to each route component that contains route specific information such as route parameters, static data, resolve data, global query params, and the global fragment.
一个提供给每个路由组件的服务,其中包含当前路由专属的信息,例如路由参数、静态数据、解析数据、全局查询参数和全局片段。
|
RouterState
|
The current state of the router including a tree of the currently activated routes together with convenience methods for traversing the route tree.
路由器的当前状态,包括一棵当前激活路由的树以及遍历这棵路由树的便捷方法。
|
|
Link parameters array
链接参数数组
|
An array that the router interprets as a routing instruction.
You can bind that array to a RouterLink or pass the array as an argument to the Router.navigate method.
一个由路由器将其解释为路由指南的数组。你可以将该数组绑定到 RouterLink 或将该数组作为参数传递给 Router.navigate 方法。
|
|
Routing component
路由组件
|
An Angular component with a RouterOutlet that displays views based on router navigations.
一个带有 RouterOutlet 的 Angular 组件,可基于路由器的导航来显示视图。
|