(译)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来实现高级的路由场景


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


路由和路径(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路由器允许你使用一下两种不同的方式访问路由参数:

  • 使用ActivatedRoute服务
  • 从angular4开始可以使用ParamMap

你可以使用冒号语法创建一个路由参数,下面是一个带有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指令来创建导航链接。这个指令采用路由中与组件相关联的路径进行导航,形式如下:

Contacts

多路由出口和辅助路由

angular路由器支持在同一应用中使用多个路由出口(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: '[email protected]' },
     {  id:  2,  name:  'Contact 2', email: '[email protected]' },
     {  id:  3,  name:  'Contact 3', email: '[email protected]' },
     {  id:  4,  name:  'Contact 4', email: '[email protected]' }
   ];

   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文件,添加


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,在上面添加如下代码:

Contacts

接下来,我们需要在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文件,添加如下代码:

Name Email Actions
{{ contact.name }} {{ contact.email }} Go to details

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

下面是组件截屏:

(译)angular路由完全指南_第1张图片
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文件,添加:

Contact # {{contact.id}}

Name: {{contact.name}}

Email: {{contact.email}}

(译)angular路由完全指南_第2张图片
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/

你可能感兴趣的:((译)angular路由完全指南)