Angular 8 路由和导航


导航是 Web 应用程序中的重要方面之一。尽管单页面应用程序 (SPA) 没有多页面概念,但它确实从一个视图(费用列表)移动到另一个视图(费用详细信息)。提供清晰易懂的导航元素决定了应用程序的成功与否。

Angular 提供了广泛的导航功能集以适应简单场景到复杂场景。定义导航元素和对应视图的过程称为 Routing . Angular 提供了一个单独的模块, 路由器模块 在 Angular 应用程序中设置导航。让我们在本章学习如何在 Angular 应用程序中进行路由。

配置路由


Angular CLI 为在应用程序创建过程以及运行应用程序期间设置路由提供了完整的支持。让我们使用以下命令创建一个启用路由器的新应用程序:

ng new routing-app --routing

Angular CLI 生成一个新模块 AppRoutingModuele 用于路由目的。生成的代码如下:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';


const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Here,

  • 导入路由器模块 和来自@angular/router 包的路由。

  • RouterMoudle 提供了在应用程序中配置和执行路由的功能。

  • Routes 是用于设置导航规则的类型。

  • Routes 是用于配置应用程序实际导航规则的局部变量(Routes 类型)。

  • RouterMoudle.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 { }

Here,

应用组件 进口 应用路由模块 使用导入元数据的模块。

Angular CLI 也提供了在现有应用程序中设置路由的选项。在现有应用程序中包含路由的一般命令如下:

ng generate module my-module --routing

这将生成启用了路由功能的新模块。要在现有模块(AppModule)中启用路由功能,我们需要包含额外的选项,如下所示:

ng generate module app-routing --module app --flat

Here,

--模块应用 配置新创建的路由模块, 应用路由模块 在 AppModule 模块中。

让我们在中配置路由模块 费用经理 应用。

打开命令提示符并转到项目根文件夹。

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)

Here,

CLI 生成 应用路由模块 然后,将其配置为 应用模块

创建路线


创建路线简单易行。创建路由的基本信息如下:

  • 要调用的目标组件。
  • 访问目标组件的路径。

创建简单路由的代码如下:

const routes: Routes = [
  { path: 'about', component: AboutComponent },
];

Here,

  • Routes 是 AppRoutingModule 中的变量。

  • about 是路径,AboutComponent 是目标/目标组件。当用户请求 http://localhost:4200/about url 时,路径与 about 规则匹配,然后将调用 AboutComponent。

访问路线


让我们学习如何在应用程序中使用配置的路由。

访问路线是一个两步过程。

Include 路由器插座 根组件模板中的标记。

<router-outlet></router-outlet>

Use 路由器链接 and 路由器LinkActive 财产在所需的地方。

<a routerLink="/about" routerLinkActive="active">First Component</a>

Here,

  • 路由器链接 使用路径设置要调用的路由。

  • 路由器LinkActive 设置激活路由时要使用的 CSS 类。

有时,我们需要访问组件内部的路由而不是模板。然后,我们需要按照以下步骤进行:

注入实例 Router and 激活路由 在相应的组件中。

import { Router, ActivatedRoute } from '@angular/router'; 
constructor(private router: Router, private route: ActivatedRoute)

Here,

  • Router 提供功能做 路由操作 .

  • Route 指当前 激活路线。

使用路由器的导航功能。

this.router.navigate(['about']);

Here,

navigate 函数需要一个包含必要路径信息的数组。

使用相对路径

路由路径类似于网页 URL,也支持相对路径。访问 关于组件 从另一个组件,说 主页组件 ,简单地使用 .. 表示法,如 web url 或文件夹路径。

<a routerLink="../about">Relative Route to about component</a>

访问组件中的相对路径:

import { NavigationExtras } from '@angular/router'; 
this.router.navigate(['about'], { relativeTo: this.route });

Here,

关系到 可在 导航附加功能 class.

路线排序

路线排序 在路由配置中非常重要。如果多次配置相同的路径,则将调用第一个匹配的路径。如果第一场比赛由于某种原因失败,那么第二场比赛将被调用。

重定向路线

Angular 路由允许将路径重定向到另一个路径。 重定向到 是设置重定向路径的选项。示例路线如下:

const routes: Routes = [ 
    { path: '', redirectTo: '/about' },
];

Here,

  • 重定向到 如果实际路径与空字符串匹配,则设置为重定向路径。

通配符路线

通配符路由将匹配任何路径。它是使用 ** 创建的,将用于处理应用程序中不存在的路径。将通配符路由放在配置的末尾,使其在其他路径不匹配时调用。

示例代码如下:

const routes: Routes = [
  { path: 'about', component: AboutComponent },
  { path: '',   redirectTo: '/about', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent },  // 404 页面的通配符路由
];

Here,

如果调用不存在的页面,则前两个路由失败。但是,最终的通配符路由将成功,并且 PageNotFoundComponent 被调用。

访问路由参数


在 Angular 中,我们可以使用参数在路径中附加额外的信息。可以通过 paramMap 接口在组件中访问该参数。在路由中新建参数的语法如下:

const routes: Routes = [
  { path: 'about', component: AboutComponent },
  { path: 'item/:id', component: ItemComponent },
  { path: '',   redirectTo: '/about', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent },  // 404 页面的通配符路由
];

在这里,我们附上了 id 在路径中。 id 可以在 项目组件 使用两种技术。

  • 使用可观察的。
  • 使用快照(不可观察的选项)。

使用可观察的

Angular 提供了一个特殊的接口 paramMap 来访问路径的参数。 parmaMap 有以下方法:

  • 有(姓名) :如果指定的名称在路径(参数列表)中可用,则返回true。

  • 获取(名称) :返回路径(参数列表)中指定名称的值。

  • 全部获取(名称) :返回路径中指定名称的倍数。当有多个值可用时,get() 方法仅返回第一个值。

  • keys :返回路径中所有可用的参数。

使用访问参数的步骤 paramMap 如下面所述:

  • Import paramMap 可在 @角/路由器 package.

  • Use paramMap in the ngOnInit() 访问参数并将其设置为局部变量。

ngOnInit() {
    this.route.paramMap.subscribe(params => {
        this.id = params.get('id);
    });
}

我们可以直接在 rest 服务中使用它 pipe method.

this.item$ = this.route.paramMap.pipe(
    switchMap(params => {
        this.selectedId = Number(params.get('id'));
        return this.service.getItem(this.selectedId);
    })
);

使用快照

snapshot 类似于 可观察的 但是,它不支持 observable 并立即获取参数值。

let id = this.route.snapshot.paramMap.get('id');

嵌套路由


一般来说, 路由器插座 将被放置在根组件中 (应用组件) 的应用程序。但是,路由器插座可以在任何组件中使用。当在根组件以外的组件中使用 router-outlet 时,必须将特定组件的路由配置为父组件的子组件。这就是所谓的 嵌套路由 .

让我们考虑一个组件,比如说 项目组件 配置有 路由器插座 并且有两个 路由器链接 具体如下:

<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
    }
    ]
}]

工作示例

让我们将本章学到的路由概念应用到我们的 费用经理 应用。

打开命令提示符并转到项目根文件夹。

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)

Here,

CLI 生成 应用路由模块 然后将其配置为 应用模块 .

Update 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 { }

在这里,我们为我们的费用清单和费用明细组件添加了路由。

Update 应用组件 template (src/app/app.component.html) 包括 路由器插座 and 路由器链接。

<!-- 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>

Open 费用条目列表组件 模板(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>

在这里,我们更新了费用清单表并添加了一个新列来显示查看选项。

Open ExpenseEntryComponent (src/app/expense-entry/expense-entry.component.ts) 并添加功能以获取当前选定的费用条目。可以通过首先通过 paramMap 然后,使用 获取费用条目() 方法从 费用录入服务 .

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']);
    }
}

Open 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>

在这里,我们添加了 前往列表 之前的按钮 Edit button.

使用以下命令运行应用程序:

ng serve

应用程序的最终输出如下:

Nested routing

单击第一个条目的查看选项将导航到详细信息页面并显示所选费用条目,如下图所示:

Nested routing