- Angular 8 教程
- Angular 8 - 首页
- Angular 8 - 简介
- Angular 8 - 安装
- 创建第一个应用程序
- Angular 8 - 架构
- Angular 组件和模板
- Angular 8 - 数据绑定
- Angular 8 - 指令
- Angular 8 - 管道
- Angular 8 - 响应式编程
- 服务和依赖注入
- Angular 8 - Http 客户端编程
- Angular 8 - Angular Material
- 路由和导航
- Angular 8 - 动画
- Angular 8 - 表单
- Angular 8 - 表单验证
- 身份验证和授权
- Angular 8 - Web Workers
- Service Workers 和 PWA
- Angular 8 - 服务器端渲染
- Angular 8 - 国际化 (i18n)
- Angular 8 - 可访问性
- Angular 8 - CLI 命令
- Angular 8 - 测试
- Angular 8 - Ivy 编译器
- Angular 8 - 使用 Bazel 构建
- Angular 8 - 向后兼容性
- Angular 8 - 工作示例
- Angular 9 - 新增功能?
- Angular 8 有用资源
- Angular 8 - 快速指南
- Angular 8 - 有用资源
- Angular 8 - 讨论
Angular 8 - 路由和导航
导航是 web 应用程序中一个重要的方面。即使单页面应用程序 (SPA) 没有多页面的概念,它也会从一个视图(例如支出列表)移动到另一个视图(例如支出详情)。提供清晰易懂的导航元素决定了应用程序的成功。
Angular 提供了广泛的导航功能,可以适应从简单场景到复杂场景的需求。定义导航元素和相应视图的过程称为路由。Angular 提供了一个单独的模块,RouterModule,用于在 Angular 应用程序中设置导航。本章我们将学习如何在 Angular 应用程序中进行路由。
配置路由
Angular CLI 完全支持在应用程序创建过程中以及在应用程序运行过程中设置路由。让我们使用以下命令创建一个启用路由的新应用程序:
ng new routing-app --routing
Angular CLI 生成一个新的模块,AppRoutingModule,用于路由目的。生成的代码如下:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = []; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
这里:
从 @angular/router 包中导入 RouterModule 和 Routes。
RouterModule 提供了在应用程序中配置和执行路由的功能。
Routes 是用于设置导航规则的类型。
routes 是用于配置应用程序实际导航规则的局部变量(Routes 类型)。
RouterModule.forRoot() 方法将设置在 routes 变量中配置的导航规则。
Angular CLI 将生成的 AppRoutingModule 包含在 AppComponent 中,如下所示:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
这里:
AppComponent 使用 imports 元数据导入AppRoutingModule 模块。
Angular CLI 也提供在现有应用程序中设置路由的选项。在现有应用程序中包含路由的通用命令如下:
ng generate module my-module --routing
这将生成一个启用了路由功能的新模块。要在现有模块 (AppModule) 中启用路由功能,我们需要包含如下指定的额外选项:
ng generate module app-routing --module app --flat
这里:
–module app 将新创建的路由模块AppRoutingModule 配置到 AppModule 模块中。
让我们在ExpenseManager 应用程序中配置路由模块。
打开命令提示符并转到项目根文件夹。
cd /go/to/expense-manager
使用以下命令生成路由模块:
ng generate module app-routing --module app --flat
输出
输出如下:
CREATE src/app/app-routing.module.ts (196 bytes) UPDATE src/app/app.module.ts (785 bytes)
这里:
CLI 生成AppRoutingModule,然后将其配置到AppModule 中。
创建路由
创建路由简单易行。创建路由的基本信息如下:
- 要调用的目标组件。
- 访问目标组件的路径。
创建简单路由的代码如下:
const routes: Routes = [ { path: 'about', component: AboutComponent }, ];
这里:
Routes 是 AppRoutingModule 中的变量。
about 是路径,AboutComponent 是目标/目标组件。当用户请求 https://127.0.0.1:4200/about url 时,路径与 about 规则匹配,然后将调用 AboutComponent。
访问路由
让我们学习如何在应用程序中使用已配置的路由。
访问路由是一个两步过程。
在根组件模板中包含router-outlet 标签。
<router-outlet></router-outlet>
在需要的地方使用routerLink 和routerLinkActive 属性。
<a routerLink="/about" routerLinkActive="active">First Component</a>
这里:
routerLink 使用路径设置要调用的路由。
routerLinkActive 设置在路由激活时使用的 CSS 类。
有时,我们需要在组件内部而不是模板中访问路由。然后,我们需要遵循以下步骤:
在相应的组件中注入Router 和ActivatedRoute 的实例。
import { Router, ActivatedRoute } from '@angular/router'; constructor(private router: Router, private route: ActivatedRoute)
这里:
Router 提供执行路由操作的功能。
Route 指的是当前激活的路由。
使用路由器的 navigate 函数。
this.router.navigate(['about']);
这里:
navigate 函数需要一个包含必要路径信息的数组。
使用相对路径
路由路径类似于网页 URL,它也支持相对路径。要从另一个组件(例如HomePageComponent)访问AboutComponent,只需像在 web url 或文件夹路径中一样使用 .. 符号。
<a routerLink="../about">Relative Route to about component</a>
要在组件中访问相对路径:
import { NavigationExtras } from '@angular/router'; this.router.navigate(['about'], { relativeTo: this.route });
这里:
relativeTo 可在NavigationExtras 类中找到。
路由排序
路由排序在路由配置中非常重要。如果同一路径多次配置,则将调用第一次匹配的路径。如果由于某种原因第一次匹配失败,则将调用第二次匹配。
重定向路由
Angular 路由允许将一个路径重定向到另一个路径。redirectTo 是设置重定向路径的选项。示例路由如下:
const routes: Routes = [ { path: '', redirectTo: '/about' }, ];
这里:
- 如果实际路径与空字符串匹配,则redirectTo 将 about 设置为重定向路径。
通配符路由
通配符路由将匹配任何路径。它是使用 ** 创建的,将用于处理应用程序中不存在的路径。将通配符路由放在配置的末尾,使其在其他路径不匹配时被调用。
示例代码如下:
const routes: Routes = [ { path: 'about', component: AboutComponent }, { path: '', redirectTo: '/about', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent }, // Wildcard route for a 404 page ];
这里:
如果调用了不存在的页面,则前两个路由将失败。但是,最终的通配符路由将成功,并调用PageNotFoundComponent。
访问路由参数
在 Angular 中,我们可以使用参数在路径中附加额外信息。可以使用 paramMap 接口在组件中访问参数。在路由中创建新参数的语法如下:
const routes: Routes = [ { path: 'about', component: AboutComponent }, { path: 'item/:id', component: ItemComponent }, { path: '', redirectTo: '/about', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent }, // Wildcard route for a 404 page ];
在这里,我们在路径中添加了id。可以使用两种技术在ItemComponent 中访问id。
- 使用 Observable。
- 使用 snapshot(非 Observable 选项)。
使用 Observable
Angular 提供了一个特殊的接口 paramMap 来访问路径的参数。paramMap 具有以下方法:
has(name) - 如果路径(参数列表)中存在指定的名称,则返回 true。
get(name) - 返回路径(参数列表)中指定名称的值。
getAll(name) - 返回路径中指定名称的多个值。当有多个值可用时,get() 方法只返回第一个值。
keys - 返回路径中所有可用的参数。
使用paramMap 访问参数的步骤如下:
导入paramMap,它位于@angular/router 包中。
在ngOnInit() 中使用paramMap 访问参数并将其设置为局部变量。
ngOnInit() { this.route.paramMap.subscribe(params => { this.id = params.get('id); }); }
我们可以使用pipe 方法直接在其余服务中使用它。
this.item$ = this.route.paramMap.pipe( switchMap(params => { this.selectedId = Number(params.get('id')); return this.service.getItem(this.selectedId); }) );
使用 snapshot
snapshot 与Observable 类似,只是它不支持 Observable 并立即获取参数值。
let id = this.route.snapshot.paramMap.get('id');
嵌套路由
通常,router-outlet 将放置在应用程序的根组件(AppComponent) 中。但是,router-outlet 可以用于任何组件。当 router-outlet 用于根组件以外的组件时,特定组件的路由必须配置为父组件的子组件。这称为嵌套路由。
让我们考虑一个组件,例如ItemComponent 配置了router-outlet 并具有两个routerLink,如下所示:
<h2>Item Component</h2> <nav> <ul> <li><a routerLink="view">View</a></li> <li><a routerLink="edit">Edit</a></li> </ul> </nav> <router-outlet></router-outlet>
ItemComponent 的路由必须配置为嵌套路由,如下所示:
const routes: Routes = [ { path: 'item', component: ItemComponent, children: [ { path: 'view', component: ItemViewComponent }, { path: 'edit', component: ItemEditComponent } ] }]
工作示例
让我们将本章中学到的路由概念应用到我们的ExpenseManager 应用程序中。
打开命令提示符并转到项目根文件夹。
cd /go/to/expense-manager
如果之前没有完成,请使用以下命令生成路由模块。
ng generate module app-routing --module app --flat
输出
输出如下:
CREATE src/app/app-routing.module.ts (196 bytes) UPDATE src/app/app.module.ts (785 bytes)
这里:
CLI 生成AppRoutingModule,然后将其配置到AppModule 中。
更新AppRoutingModule (src/app/app.module.ts),如下所示:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { ExpenseEntryComponent } from './expense-entry/expense-entry.component'; import { ExpenseEntryListComponent } from './expense-entry-list/expense-entry-list.component'; const routes: Routes = [ { path: 'expenses', component: ExpenseEntryListComponent }, { path: 'expenses/detail/:id', component: ExpenseEntryComponent }, { path: '', redirectTo: 'expenses', pathMatch: 'full' }]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
在这里,我们为我们的支出列表和支出详情组件添加了路由。
更新AppComponent 模板(src/app/app.component.html) 以包含router-outlet 和routerLink。
<!-- Navigation --> <nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top"> <div class="container"> <a class="navbar-brand" href="#">{{ title }}</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarResponsive"> <ul class="navbar-nav ml-auto"> <li class="nav-item active"> <a class="nav-link" href="#">Home <span class="sr-only" routerLink="/">(current)</span> </a> </li> <li class="nav-item"> <a class="nav-link" routerLink="/expenses">Report</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Add Expense</a> </li> <li class="nav-item"> <a class="nav-link" href="#">About</a> </li> </ul> </div> </div> </nav> <router-outlet></router-outlet>
打开ExpenseEntryListComponent 模板 (src/app/expense-entry-list/expense-entry-list.component.html) 并为每个支出条目包含查看选项。
<table class="table table-striped"> <thead> <tr> <th>Item</th> <th>Amount</th> <th>Category</th> <th>Location</th> <th>Spent On</th> <th>View</th> </tr> </thead> <tbody> <tr *ngFor="let entry of expenseEntries"> <th scope="row">{{ entry.item }}</th> <th>{{ entry.amount }}</th> <td>{{ entry.category }}</td> <td>{{ entry.location }}</td> <td>{{ entry.spendOn | date: 'medium' }}</td> <td><a routerLink="../expenses/detail/{{ entry.id }}">View</a></td> </tr> </tbody> </table>
在这里,我们更新了支出列表表并添加了一列来显示查看选项。
打开ExpenseEntryComponent (src/app/expense-entry/expense-entry.component.ts) 并添加功能以获取当前选择的支出条目。这可以通过首先通过paramMap 获取 id,然后使用ExpenseEntryService 的getExpenseEntry() 方法来完成。
this.expenseEntry$ = this.route.paramMap.pipe( switchMap(params => { this.selectedId = Number(params.get('id')); return this.restService.getExpenseEntry(this.selectedId); })); this.expenseEntry$.subscribe( (data) => this.expenseEntry = data );
更新 ExpenseEntryComponent 并添加转到支出列表的选项。
goToList() { this.router.navigate(['/expenses']); }
ExpenseEntryComponent 的完整代码如下:
import { Component, OnInit } from '@angular/core'; import { ExpenseEntry } from '../expense-entry'; import { ExpenseEntryService } from '../expense-entry.service'; import { Router, ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { switchMap } from 'rxjs/operators'; @Component({ selector: 'app-expense-entry', templateUrl: './expense-entry.component.html', styleUrls: ['./expense-entry.component.css'] }) export class ExpenseEntryComponent implements OnInit { title: string; expenseEntry$ : Observable<ExpenseEntry>; expenseEntry: ExpenseEntry = {} as ExpenseEntry; selectedId: number; constructor(private restService : ExpenseEntryService, private router : Router, private route : ActivatedRoute ) { } ngOnInit() { this.title = "Expense Entry"; this.expenseEntry$ = this.route.paramMap.pipe( switchMap(params => { this.selectedId = Number(params.get('id')); return this.restService.getExpenseEntry(this.selectedId); })); this.expenseEntry$.subscribe( (data) => this.expenseEntry = data ); } goToList() { this.router.navigate(['/expenses']); } }
打开ExpenseEntryComponent (src/app/expense-entry/expense-entry.component.html) 模板并添加一个新按钮以导航回支出列表页面。
<div class="col-sm" style="text-align: right;"> <button type="button" class="btn btn-primary" (click)="goToList()">Go to List</button> <button type="button" class="btn btn-primary">Edit</button> </div>
在这里,我们在编辑按钮之前添加了转到列表按钮。
使用以下命令运行应用程序:
ng serve
应用程序的最终输出如下:
单击第一个条目的查看选项将导航到详细信息页面并显示所选的支出条目,如下所示: