(译)angular路由完全指南

摘要:在本教程中,Ahmed Bouchefra 介绍了angular路由器(router),以及如何使用它创建客户端应用和带路由导航的单页面应用。


如果你对angular7还不熟悉的话,我将带你近距离了解这个令人印象深刻的前端框架所提供的一切。我会向你演示一个angular demo,通过这个demo你将了解与路由相关的一些概念,比如:

  • 路由出口(The router outlet)
  • 路由(Routes )和 路径(paths)
  • 导航(Navigation)

我也将向你演示如何使用Angular CLI v7生成一个demo应用,在这个应用中,我们将使用angular路由器实现路由和导航。但在介绍angluar路由之前,让我们一起重温一下angular以及它在最新版本中增加的一些新特性。


Anguar7 的介绍

angular是最受欢迎的构建移动端和pc端应用的前端框架之一,它遵循基于组件的体系结构,其中每个组件都是一段独立的、可重用的代码,控制着应用程序UI的一部分。

angular中的每一个组件都是一个用@Component装饰器装饰的TypeScritp类。它有一个附加模板和用于形成组件视图的css样式表。

angular7是angular最近刚发布的最新版本,它给angular带来了很多新特性,尤其是在CLI和性能表现方面有了很大的提升,比如:
·CLI提示: 像 ng add 和ng new 这样的普通命令现在可以提示用户选择要向项目中添加的命令,比如路由和样式格式

  • Angular Material CDK添加虚拟滚动功能
  • Angular Material CDK添加对拖放功能的支持
  • 新项目会默认使用CLI中的Budgets Bundle ,这将在app大小超过限定尺寸时向开发者发出警告。默认情况下,当初始 bundle 超过2MB,新应用程序将发出警告,超过5MB时将会报错。你也可以在angular.json文件中修改这些限制值。

Angular Router 介绍

Angular Router是由angular 核心团队构建和维护的一款强大的JavaScript路由器,可以从@angular/router包中安装使用。它给开发者提供了一个拥有众功能的完整的路由库,这些功能包括 多路由出口,不同的路由匹配策略,易获取的路由参数,保护组件免受未授权访问的路由守卫。

Angular Router是angular平台的核心部分,它使得开发者能够使用多个视图构建单页面应用,并允许在这些视图之间导航。

现在,让我们来更详细的了解路由器的基本概念。


路由出口(the router-outlet)

router-outlet 是路由库中提供的一个指令,路由器根据当前浏览器的URL插入相匹配的组件。你可以在你的应用中加入多个outlet来实现高级的路由场景

<router-outlet></router-outlet>

路由器将把它匹配的任何组件呈现为路由出口的同级组件


路由和路径(routes and paths)

Routes 是一系列定义(对象集)(译者注:也就是路由配置对象的集合),每个对象包含至少一个path属性和一个component属性(或者一个redirectTo属性)。path指URL中确定应该显示的唯一视图的部分,component指需要与path相关联的angular组件。根据我们提供的一个路由定义(通过静态方法RouterModule.forRoot(routes)),路由器能够将用户导航到特定的视图

每个路由将一个URL路径映射到唯一对应的组件

path的值可以为空(''),这表示应用的一个默认路径,空路径通常是应用的开始。

path的值可以使用通配符字符串(**)。如果请求的URL无法匹配路由数组中任何定义的路由,路由器将会选择通配符路由。通配符路由可以用来展示"Not Found"视图,或者在没有路由匹配的情况下重定向到特定的视图

下面是一个路由的例子:

{ path:  'contacts', component:  ContactListComponent}

如果将这个路由添加到路由器配置中,当web应用程序的浏览器URL变为 /contacts时,路由器将呈现ContactListComponent组件。


路由匹配策略

angular路由器提供了多种不同的路由匹配策略。默认的策略是检查当前浏览器URL是否以定义的path值为前缀。
例如我们的上一个路由:

{ path:  'contacts', component:  ContactListComponent}

也可以写成下面的形式:

{ path:  'contacts',pathMatch: 'prefix', component:  ContactListComponent}

pathMatch属性指定了路由匹配策略,默认值是例子中的prefix.
pathMatch的另一个值是full,当指定一个路由的匹配策略为full时,路由器将检查path的值是否与当前浏览器URL完全匹配:

{ path:  'contacts',pathMatch: 'full', component:  ContactListComponent}

路由参数

创建带参数的路由是webapp的一个常见特性,Angular路由器允许你使用一下两种不同的方式访问路由参数:

你可以使用冒号语法创建一个路由参数,下面是一个带有id参数的路由例子:

{ path:  'contacts/:id', component:  ContactDetailComponent}

路由守卫

路由守卫是angular的路由器的一个特性,它允许开发者在一个路由被请求时运行一些处理逻辑,并根据运行结果决定允许或者拒绝用户访问此路由。路由守卫普遍被用在用户访问一个页面时,检查用户是否已登录并被授权。

你可以通过实现@angular/router模块中的CanActive接口,重写canActivate()方法来添加路由守卫。canActivate()方法用于存放是否允许访问路由的处理逻辑的。下面的守卫将始终允许用户访问一个路由:

class MyGuard implements CanActivate {
  canActivate() {
    return true;
  }
}

然后,你可以使用canActive属性将路由守卫添加到路由上,达到保护路由的目的:

{ path:  'contacts/:id', canActivate: [MyGuard], component:  ContactDetailComponent}

导航指令

angular路由器提供了routerLink指令来创建导航链接。这个指令采用路由中与组件相关联的路径进行导航,形式如下:

<a [routerLink]="'/contacts'">Contacts</a>

多路由出口和辅助路由

angular路由器支持在同一应用中使用多个路由出口(outlet)。

每个组件都有一个相关联的主路由,也可以有多个辅助路由。辅助路由是开发者能够同时导航多个路由。

要创建辅助路由,你需要一个被命名的路由出口,与辅助路由相关联的组件将会在这个命名的路由出口渲染。

<router-outlet></router-outlet>
<router-outlet name="outlet1"></router-outlet>
  • 没有name属性的路由出口为主路由出口
  • 除了主路由出口外,所有的路由出口都应该有一个name

然后,你可以使用outlet属性指定你想渲染你的组件的那个路由出口:

{ path: "contacts", component: ContactListComponent, outlet: "outlet1" }

创建一个 angular7 Demo

在这一部分,我们将看到一个如何安装和使用angular路由器的实际例子。这里是我们将创建的demo演示以及github项目地址

安装angular7

使用Angular CLI需要Nodejs 8.9+版本,npm 5.5.1+版本,开发前确保你的系统中安装了它们。然后运行下面的命令行安装最新版的Angular CLI

$ npm install -g @angular/cli

这条命令将会全局安装Angular CLI

Note: 你或许想使用sudo命令全装安装angluar-cli,这取决于你的npm配置


创建一个angular7 项目

简单的运行下面这条命令,就可以创建一个新的项目:

$ ng new angular7-router-demo

CLI会询问你是否想添加路由(输入N拒绝,因为我们将会在demo看到如何手动添加路由),想使用哪种样式表,选择css ,然后按下Enter。CLI将会创建包含项目必需文件的文件夹目录,安装项目所需的依赖。


创建虚拟后台(fake back-end)服务

因为我们没有真实的后台可以交互,所以我们使用angular-in-memeory-web-api库来创建一个虚拟后台。这个库是angluar用来演示和测试的内存中的web api,它模拟了REST API的CRUD操作.

这个库的工作方式是拦截HttpClient向远程服务器发送的请求,并将请求重定向到我们创建的本地内存数据存储区。

为了创建一个虚拟后台,我们需要遵循以下步骤:

01 安装 angular-in-memeory-web-api模块
02 创建一个返回假数据的服务
03 配置应用程序使用虚拟后台

在终端输入下面的命令安装angular-in-memory-web-api模块:

$ npm install --save angular-in-memory-web-api

然后,生成一个将要使用的后台服务:

ng g s backend

打开 src/app/backend.service.ts文件,从angular-in-memory-web-api模块中引入 InMemoryDbService:

import {InMemoryDbService} from 'angular-in-memory-web-api'

前面生成的BackendService服务需要实现InMemoryDbService接口并重写里面的createDb()方法:

@Injectable({
  providedIn: 'root'
})
export class BackendService implements InMemoryDbService{

  constructor() { }
  createDb(){
    
   let  contacts =  [
     {  id:  1,  name:  'Contact 1', email: 'contact1@email.com' },
     {  id:  2,  name:  'Contact 2', email: 'contact2@email.com' },
     {  id:  3,  name:  'Contact 3', email: 'contact3@email.com' },
     {  id:  4,  name:  'Contact 4', email: 'contact4@email.com' }
   ];

   return {contacts};
    
  }
}

我们在BackendService中简单的创建了一个联系人数组并返回了他们,每一个联系人都有一个id属性。

最后,我们将InMemoryWebApiModule模块引入到app.module.ts中,并提供我们创建的BackendService:

import { InMemoryWebApiModule } from “angular-in-memory-web-api”;  
import { BackendService } from “./backend.service”;
/* ... */

@NgModule({
  declarations: [
    /*...*/
  ],
  imports: [
    /*...*/
    InMemoryWebApiModule.forRoot(BackendService)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

接下来创建一个ContactService服务,它封装了处理联系人的代码:

$ ng g s contact

打开src/app/contact.service.ts文件,将它修改为下面的内容:

import { HttpClient } from “@angular/common/http”;

@Injectable({
  providedIn: 'root'
})
export class ContactService {

  API_URL: string = "/api/";
  constructor(private http: HttpClient) { }
  getContacts(){    
   return this.http.get(this.API_URL + 'contacts')
  }
  getContact(contactId){
   return this.http.get(`${this.API_URL + 'contacts'}/${contactId}`) 
  }
}

ContactService中,我们添加了两个方法:

  • getContacts() - 获取全部联系人
  • getContact() - 通过id获取某一联系人

你可以将 API_URL设置成任何URL值,因为我们不会使用一个真实后台。所有的请求都将被拦截并发送到我们所创建的虚拟后台中。


创建我们的 Angular组件

在我们学习如何使用不同的路由特性之前,让我们先为我们的项目创建一些组件。

打开终端运行下面的这些命令:

$ ng g c contact-list
$ ng g c contact-detail

这两条命令将会生成一个ContactListComponent组件和一个ContactListComponent组将。将它们添加到根模块中(译者注:此模块位于 app.module.ts文件中)

配置路由模块

在大多数情况下,你会使用Angular CLI创建带路由配置的项目,但在这里,我们将手动添加路由,如此一来我们能够更好的理解angular中的路由是如何工作的。

添加路由模块

我们需要添加AppRoutingModule,它包含我们的应用程序路由和一个路由器出口,Angular将根据浏览器的当前URL插入当前匹配的组件。

我们将会看到:

  • 如何为路由创建一个angular模块并引入它
  • 如何为不同的组件配置路由
  • 如何配置路由出口

首先,让我们在一个 app-routing.module.ts中创建路由模块,在src/app路径下使用如下命令创建app-routing.module.ts文件:

$ cd angular7-router-demo/src/app
$ touch app-routing.module.ts

译者注:
touch可能是作者使用的linux创建文件的命令,读者可以直接在src/app目录下使用angular中自带的ng g m app-routing命令创建此路由模块文件,然后根据作者下面的步骤修改此文件即可

打开app-routing.module.ts文件,将它修改为如下内容:

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/core包中引入了NgModule,这是一个用来创建Angular模块的TypesScript装饰器。

我们还从@angular/core包引入了RouterModule类和Routes类。
RouterModule提供了像RouterModule.forRoot()这样的静态方法,用于将配置对象传递个路由器。

接下来,我们定义了一个类型为Routes的常量数组routes用来存放每一个路由的信息。

最后,我们创建并导出了一个名为AppRoutingModule的模块(你可以起任何你喜欢的名字),这个模块是一个由@NgModule装饰器装饰的简单的TypeScript类,@NgModule 装饰器接受一个元数据对象,该对象的属性用来描述这个模块。在这个对象的 imports属性中,我们调用了RouterModule.forRoot(routes)方法,并将路由数组作为参数传了进去。在exports数组中,我们添加了RouterModule.


引入路由模块

接下来,我们将刚才创建的路由模块引入位于src/app/app.module.ts文件的根模块中:

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

我们从./app-routing.module中引入了AppRoutingModule,并将它添加到了根模块的imports数组中。


添加路由出口

最后,我们需要添加路由出口(router outlet)。打开包含了主应用模板的src/app/app.component.html文件,添加<router-outlet>

<router-outlet></router-outlet>

Angular路由器将在这里渲染与当前浏览器路径相对应的组件。

以上是在angular项目中手动配置路由所需要遵循的所有步骤。

创建路由

现在,让我们给我们的两个组件添加路由。打开src/app/app-routing.module.ts,将下面的路由添加到routes数组中:

const routes: Routes = [
   {path: 'contacts' , component: ContactListComponent},
   {path: 'contact/:id' , component: ContactDetailComponent}
];

要确保在路由模块中引入了这两个组件:

import { ContactListComponent } from './contact-list/contact-list.component';
import { ContactDetailComponent } from './contact-detail/contact-detail.component';

现在,我们就可以通过/contactscontact:id访问这两个组件了。

添加导航链接

接下来,让我们使用routerLink指令将导航链接添加到我们的app模板上。打开src/app/app.component.html,在<router-outlet></router-outlet>上面添加如下代码:

<h2><a [routerLink] = "'/contacts'">Contacts</a></h2>

接下来,我们需要在ContactListComponent组件中展示联系人列表。打开 src/app/contact-list.component.ts,然后添加如下代码:

import { Component, OnInit } from '@angular/core';
import { ContactService } from '../contact.service';

@Component({
  selector: 'app-contact-list',
  templateUrl: './contact-list.component.html',
  styleUrls: ['./contact-list.component.css']
})
export class ContactListComponent implements OnInit {

  contacts: any[] = [];
  

  constructor(private contactService: ContactService) { }

  ngOnInit() {
    this.contactService.getContacts().subscribe((data : any[])=>{
        console.log(data);
        this.contacts = data;
    })
  }
}

我们创建了一个contacts数组来存放联系人数据,接下来,我们在该组件中注入ContactService服务,在ngOnInit钩子函数中调用服务实例的getContacts()方法来获取联系人数据(前文BackendService中创建的fake data)并将它赋值给contacts数组。
下一步,打开src/app/contact-list/contact-list.component.html文件,添加如下代码:

<table style="width:100%">
  <tr>
    <th>Name</th>
    <th>Email</th>
    <th>Actions</th>
  </tr>
  <tr *ngFor="let contact of contacts" >
    <td>{{ contact.name }}</td>
    <td>{{ contact.email }}</td> 
    <td>
    <a [routerLink]="['/contact', contact.id]">Go to details</a>
    </td>
  </tr>
</table>

我们遍历contacts数组并展示每一个联系人的姓名和email.同时,我们也使用routerLink指令创建了一个指向每一个联系人详情组件的链接(<a [routerLink]="['/contact', contact.id]">Go to details</a>)。

下面是组件截屏:

Contact list

当我们点击Go to detail链接,路由器会将我们导航到ContactDetailsComponent组件。导航路径中有一个id参数,让我们看看如何在详情组件中访问这个参数。

打开src/app/contact-detail/contact-detail.component.ts文件,将它修改至如下形式:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ContactService } from '../contact.service';

@Component({
  selector: 'app-contact-detail',
  templateUrl: './contact-detail.component.html',
  styleUrls: ['./contact-detail.component.css']
})
export class ContactDetailComponent implements OnInit {
  
  contact: any;
  constructor(private contactService: ContactService, private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.paramMap.subscribe(params => {
    console.log(params.get('id'))
     this.contactService.getContact(params.get('id')).subscribe(c =>{
        console.log(c);
        this.contact = c;
    })   
    });
     
  }
}

我们在组件中注入了ContactServiceActivatedRoute,在钩子函数ngOnInit()中检索将从路由中传递过来的id属性,并使用它获取我们分配给contact对象的联系人详细信息。

打开src/app/contact-detail/contact-detail.component.html文件,添加:

<h1> Contact # {{contact.id}}</h1>
<p>
  Name: {{contact.name}} 
</p>
<p>
 Email: {{contact.email}}
</p>
Contact details

当我们第一次从127.0.0.1:4200/访问我们的应用时,路由出口不会渲染任何组件,因此让我们向路由数组中添加如下路由来将空路径重定向到contacts:

{path: '', pathMatch: 'full', redirectTo: 'contacts'}  

我们想匹配完全空的路径,所以我们将路由匹配策略指定为full.

总结

在本教程中,我们了解了如何使用Angular路由器向应用程序中添加路由和导航。我们了解了很多概念,例如路由出口,路由和路径,并且我们创建了一个demo来实际展示这些不同的概念。你可以在github上获取这些代码。

关于本文
作者:@Ahmed
原文:https://www.smashingmagazine.com/2018/11/a-complete-guide-to-routing-in-angular/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容

  • 一、SPA的概念 首先明确一个概念,SPA,全称为Single Page Application单页应用,一个单页...
    耦耦阅读 5,942评论 0 3
  • 一:路由基础 什么是路由: 在web开发中,路由的概念由来已久,简而言之,就是利用URL的唯一性来指定特定的事物,...
    真的稻城阅读 6,013评论 2 7
  • 版本:4.0.0+2 有一些英雄指南应用的新需求: 添加一个仪表盘 视图。 添加在英雄 视图和 仪表盘 视图之间导...
    soojade阅读 1,284评论 0 0
  • 路由是 Angular 应用程序的核心,它加载与所请求路由相关联的组件,以及获取特定路由的相关数据。这允许我们通过...
    semlinker阅读 12,174评论 4 16
  • 路由要解决的核心问题是通过建立URL和页面的对应关系,使得不同的页面可以用不同的URL表示。在angular中,页...
    oWSQo阅读 1,289评论 0 1