路由器使用浏览器的history.pushState
进行导航,正因为有它,我们才可以按照期望的样子显示应用内部的url路径。这种风格也就是“html5风格url”。
我们必须往本应用的index.html
中添加一个<base href>
元素,这样pushState
才能正常工作。也就是告诉浏览器,以当前页面为应用的基底。
比如,我们目前的app目录是应用的根目录,可以在index.html
文件的head
中这样设置<base href="/">
。
从路由库中导入
路由器在它自己的@angular/router
包中,而不是angular的内核中。该路由器是可选服务,如果需要,还可以引入另外的路由库。
在app.module.ts中引入:
1 | import { RouterModule, Routes } from '@angular/router'; |
定义路由
每个带路由的angular应用都有一个Router服务的单例对象,当浏览器的url变化时,路由器会查找对应的路由,并据此决定该显示哪个组件。定义路由的两个基本参数:
- path 字符串,标记路径
- component 组件,需要实例化的组件
基本用法,例如:
1 | const appRoutes: Routes = [ |
将链接localhost:4200/crisis-center
映射到CrisisListComponent
组件。
这里的路由数组appRoutes
来描述如何进行导航。每个Route
都会把一个URL的path映射到一个组件。
注意:path
不能以斜杠开头。路由器会解析和构建为最终的url,这样当你在应用的多个试图之间进行导航时,可以使用任意相对滤镜和绝对路径。
路由器使用先匹配者优先的策略来匹配路由,所以具体路由应该放在通配路由前面。
使用路由
用RouterModule.forRoot
方法来将配置好的路由在应用中使用起来:
1 | imports: [ |
enableTracing
属性用来把每个导航生命周期中的事件输出到控制台,便于跟踪。
路由出口
路由出口来告诉浏览器在哪里显示组件,使用指令<router-outlet></router-outlet>
。路由器匹配到path
的路径之后,会在宿主视图中的RouterOutlet
之后显示组件。
路由器链接
我们可以在浏览器url中直接输入地址来跳转页面,那么如何在页面中使用路由?
RouterLink
指令让路由器控制一个元素,可以直接赋值为一个字符串进行“一次性绑定”,也可以绑定到一个返回链接参数数组的模板表达式,路由器会把这个表达式解析为一个完整的url。
RouterLinkActive
指令可以帮用户在外观上区分出当前选中的“活动”路由,等号右侧的模板表达式包含用空格分隔的一些css类。当与这个关联的路由被激活时,路由器会把一些css类加到这个元素上。
1 | <a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a> |
路由状态
在导航时的每个生命周期成功完成时,路由器会构建出一个ActivatedRoute
组成的树,它表示路由器的当前状态,可以在应用中的任何地方使用Router
服务的routerState
属性来访问当前的RouterState
的值。例如:
1 | ngOnInit() { |
激活的路由
当前路由的页面的路径url和参数可以通过注入一个ActivatedRoute
的路由参数来获取。它有一大堆有用的信息。包括:
- url 路由路径的 Observable 对象,是一个由路由路径中的各个部分组成的字符串数组。
- data 一个 Observable,其中包含提供给路由的 data 对象。也包含由解析守卫(resolve guard)解析而来的值。
- paramMap 一个 Observable,其中包含一个由当前路由的必要参数和可选参数组成的map对象。用这个 map 可以获取来自同名参数的单一值或多重值。
- queryParamMap 一个 Observable,其中包含一个对所有路由都有效的查询参数组成的map对象。 用这个 map 可以获取来自查询参数的单一值或多重值。
- fragment 一个适用于所有路由的 URL 的 fragment(片段)的 Observable。
- outlet 要把该路由渲染到的 RouterOutlet 的名字。对于无名路由,它的路由名是 primary,而不是空串。
- routeConfig 用于该路由的路由配置信息,其中包含原始路径。
- parent 当该路由是一个子路由时,表示该路由的父级 ActivatedRoute。
- firstChild 包含该路由的子路由列表中的第一个 ActivatedRoute。
- children 包含当前路由下所有已激活的子路由。
以paramMap
为例:
1 | this.activatedRouter.paramMap.pipe( |
使用switchMap操作符,可以复用这个组件,也就是可以检测到url中的变量变更后开始一系列操作。
通配符路由
当路由器收到未定义的路由时,会崩溃,所以我们需要考虑如何处理这些无效的url,让我们的应用健壮。
可以添加一个通配符路由来优雅的处理无效的url。通配符路由的path
是两个星号(**),他们会匹配任何url,当路由器匹配不上前面定义的任何路由,就会选择这个路由,可以让这个路由导航到自定义的“404”组件,也可以重定向到现有的路由。务必确保它是最后一个路由。
1 | { |
默认路由
当我们访问locahost:4200
时,由于没有定义到这个路由,所以会匹配到通配符路由,显示404。但是这个是正常且有效的路由,所以我们需要一个默认路由,我们可以添加个路由,把空路径重定向到某个已定义路由。
1 | { |
重定向路由需要一个pathMatch
属性,来告诉路由器如何用url去匹配路由的路径,否则路由会报错。这里需要完整匹配url等于''
时才匹配,所以需要把pathMatch
设置为true
。
pathMatch
的另一个属性是prefix
,这个属性告诉路由器,如果跳转路径是以这个path
开头时,就会匹配上这个跳转路由。
路由模块
为了方便扩展,需要将路由单独抽出来称为一个路由模块。将关注点从文件app.module.ts
中挪出去。
在app/
目录下创建路由模块文件app-routing.module.ts
,将路由的导入语句和路由的配置以及RouterModule.forRoot()
移入这个文件,并添加一个AppRoutingModule
类并导出它,以便在AppModule
中使用它。
1 | import { NgModule } from '@angular/core'; |