路由简介

路由是 Angular 应用程序的核心,它加载与所请求路由相关联的组件,以及获取特定路由的相关数据。这允许我们通过控制不同的路由,获取不同的数据,从而渲染不同的页面。

相关的类

Routes

Routes其实是一个Route类的数组。

Route的参数如下图所示,一般情况下,pathcomponent是必选的两个参数。

比如:path:/a,component:A则说明,当地址为/a时,应该展示组件A的内容。

其余类的简介见下图:

应用

新建项目

输入命令ng new router --routing新建一个名叫router的项目,其中--routing命令参数代表在项目基础上添加一个路由配置文件app-routingcodule.ts

可以看到路由配置文件已经生成,其初始内容是:

  1. import { NgModule } from '@angular/core';
  2. import { Routes, RouterModule } from '@angular/router';
  3. const routes: Routes = [];
  4. @NgModule({
  5. imports: [RouterModule.forRoot(routes)],
  6. exports: [RouterModule]
  7. })
  8. export class AppRoutingModule { }

新增组件

在项目根路径下运行命令ng g component homeng g component books来新增homebooks两个组件,此时可能会出现too many symbolic links encountered的错误:

解决办法:

  1. 删除根目录下的node_modules目录
  2. 更新cli命令行工具npm install -g @angular/cli
  3. 重新安装:npm install

配置路由

在路由配置文件中为routes赋值:

  1. const routes: Routes = [{path: '', component: HomeComponent}, {path: 'books', component: BooksComponent}];

其中HomeComponentBooksComponent分别对应我们新增的两个组件。

另外需要注意path参数中不需要输入/

定义模板

打开app.component.html,输入一下代码:

  1. <a [routerLink]="['/']">主页</a>
  2. <a [routerLink]="['/books']">书籍</a>
  3. <router-outlet></router-outlet>

启动项目就可以看到效果了:

使用Router

要使用Router对象,首先需要在控制器文件中定义它,一般是在构造函数constructor中传递相关参数:

  1. constructor(private router: Router) {
  2. }

这样就可以在控制器中使用router来跳转路由了,比如写个方法toBookDetails

  1. toBookDetails() {
  2. router.navigate(['/books']);
  3. }

如何调用呢,在模板文件中通过定义一个按钮并用(click)来绑定即可:

  1. <input type="button" value="书籍" (click)="toBookDetails()">

效果如下:

通配符

以上我们都假设输入的路径都是存在的,但是假如用户不小心输入了一个不存在的路径,我们有义务给予一些善意的提醒,如何实现呢?这就要使用通配符了,其用法如下:

首先创建一个新增模块err404模块,用于输入不存在的路径时展示,命令如下:

ng g component err404;

然后在路由配置文件中添加一条路由信息:

  1. const routes: Routes = [
  2. {path: '', component: HomeComponent},
  3. {path: 'books', component: BooksComponent},
  4. {path: '**', component: Err404Component}
  5. ];

程序会根据用户定义的路由顺序依次匹配,当所有明确指定的路由均不满足匹配条件时,才会进入Err404Component组件,由于是依次匹配,所以将其放入最后。

最后,输入http://localhost:4200/test,结果如下:

参数传递

参数传递的几种方式:

  1. 普通方式传递数据:/product?id=1&name=iphone => ActivatedRoute.queryParams[id];
  2. rest方式传递数据:{path:/product/:id} => /product/1 => ActivatedRoute.params[id];
  3. 路由配置传递数据:{path:/product,component:ProductComponent,data:[{madeInChina:true}]} => ActivatedRoute.data[0][madeInChina];

注入ActivatedRoute

与注入Router对象一样,将其置于组件控制器的构造函数中,此处以BooksComponent为例并为其增加一个bookname的属性,让其在初始化时直接显示属性bookname的值:

  1. import {Component, OnInit} from '@angular/core';
  2. import {ActivatedRoute, Params} from '@angular/router';
  3. @Component({
  4. selector: 'app-books',
  5. templateUrl: './books.component.html',
  6. styleUrls: ['./books.component.css']
  7. })
  8. export class BooksComponent implements OnInit {
  9. private bookname: string;
  10. constructor(private activatedRout: ActivatedRoute) {
  11. }
  12. ngOnInit() {
  13. // 普通方式传参的快照获取方式
  14. this.bookname = this.activatedRout.snapshot.queryParams['bookname'];
  15. }
  16. }

其中snapshot表示快照方式,还可以使用订阅subscribe方式,下面再说。

普通方式传参

在模板文件app.component.html中为/books所在的<a>标签中添加属性[queryParams]:

  1. <a [routerLink]="['/books']" [queryParams]="{bookname:'《活着》'}">书籍</a>

此时点击显示的路由信息则为:

为了更明确起见,在books组件的模板文件中添加绑定信息:

最终呈现效果:

rest方式传参

首先需要在路由配置文件中预定义参数的名称:

然后在<a>中直接传递值:

  1. <a [routerLink]="['/books','《活着》']">书籍</a>

此时点击链接时所呈现的路径变为:

最后,通过params方法来获取参数的值:

  1. // rest方式传参的快照获取方式
  2. this.bookname = this.activatedRout.snapshot.params['bookname'];

这样就可以呈现同上面一样的效果的内容了。那么,如何通过方法来传递rest形式的参数呢?其实同<a>的格式一样:

  1. toBookDetails() {
  2. this.router.navigate(['/books', '《简爱》']);
  3. }

此时我传入的参数是《简爱》,在链接主页和链接书籍或者在链接主页和按钮书籍之间切换点击时,内容的显示是没有问题的,但是在链接书籍和按钮书籍之间切换点击时,底下展示的内容不发生变化,这时就要用到参数订阅的方式来实时呈现了:

  1. // rest方式传参的订阅获取方式
  2. this.activatedRout.params.subscribe((params: Params) => this.bookname = params['bookname']);

路由配置传参

这种传参是静态的,假设此时需要添加一个参数isChineseVersion来表示是否是中文版,其配置形式如下:

此时在book组件控制器中新增一个boolean类型参数isChineseVersion,并在初始化方法ngOnInit中为其赋值:

this.isChineseVersion = this.activatedRout.snapshot.data[0]['isChineseVersion'];,最后在模板文件中绑定即可:

  1. <p>
  2. is Chinese Version: {{isChineseVersion}}
  3. </p>

重定向路由

在用户访问一个特定地址时,将其重定向到另一个指定的地址。

此处先将所有指向HomeComponent的根路径全部改为/home,包括路由配置文件和<a>

此时虽然指定了当路径为/home时才指向HomeComponent组件,但如果我希望访问根路径时直接展示HomeComponent的内容是怎么办,重定向路由可以帮助我们实现,语法如下:

  1. const routes: Routes = [
  2. {path: '', redirectTo: '/home', pathMatch: 'full'},
  3. {path: 'home', component: HomeComponent},
  4. {path: 'books/:bookname', component: BooksComponent, data: [{isChineseVersion: true}]},
  5. {path: '**', component: Err404Component}
  6. ];

需要注意的是,pathMatch属性的值,当为full时,表示只有当路径为根路径时才指向HomeComponent组件,它还有另一个值:prefix,意思是匹配前缀,试了之后发现没有用,比如

{path: 'hh', redirectTo: '/home', pathMatch: 'prefix'},当访问的路径为http://localhost:4200/h或者http://localhost:4200/hhh时都不会呈现主页内容而是直接跳到Err404的内容,此处存疑

子路由

子路由定义的格式如下:

上图中子路由的意思是,当访问/home/时,展示HomeComponentXxxComponent的内容,当访问/home/yyy时,展示HomeComponentYyyComponent的内容。

明确需求

在书籍模块的模板下方,增加两个链接,《简爱》的中文简介和英文简介,点击时分别显示对应语言的简介,默认显示中文简介。

实现

  1. 生成组件introduction_cn=>ng g component introduction_cn

    introduction_en=>ng g component introduction_en
  2. 分别在对应中英文模板中添加显示的内容,代码略;
  3. 在路由配置文件中定义子路由:
  1. {
  2. path: 'books/:bookname', component: BooksComponent, children: [
  3. {path: '', component: IntroductionCnComponent},
  4. {path: 'en', component: IntroductionEnComponent}
  5. ]
  6. }
  1. 在书籍模板中定义链接和定位显示位置:
  1. <p>
  2. books works!
  3. </p>
  4. <p>
  5. query book is name of {{bookname}}
  6. </p>
  7. <a [routerLink]="['./']">中文简介</a>
  8. <a [routerLink]="['./en']">英文简介</a>
  9. <router-outlet></router-outlet>

效果

多个路由

以上的例子中都是一个路由来操控页面的显示内容,但是实际上页面总是有多个模块,如何来分配各自区域的显示控制呢?这时就要用到多路由了,比如在上面的例子中,在主界面上只定义了一个<router-outlet></router-outlet>,当我们定义多个路由时就需要加以区分,实现方式很简单,在<router-outlet>上加一个name属性并赋值即可,比如:<router-outlet name = "a"></router-outlet>,此时a就是此路由定位器的标识,当不定义name属性时,其名称默认为primary

下面通过实际例子做个简单演示

  • 需求

    在原有基础上添加两个链接开始咨询结束咨询,当点击开始咨询时页面右侧显示文本域,同时左侧显示HomeComponent组件内容,当点击结束咨询时文本域消失,页面左侧仍然显示原有内容。
  • 为原有组件添加样式,使其宽度变为70%,并且靠页面左边,此例中涉及的组件有HomeComponent/BooksComponent,以主页组件为例演示:

使用div元素将原有内容包裹,并定义class值为home

  1. <div class="home">
  2. <p>
  3. home works!
  4. </p>
  5. </div>

定义样式:

  1. .home {
  2. background-color: yellowgreen;
  3. height: 750px;
  4. width: 70%;
  5. float: left;
  6. box-sizing: border-box;
  7. }

同理定义BooksComponent模板及样式文件。

  • 新增咨询组件

    ng g component advise
  • 定义模板
  1. <textarea placeholder="请输入内容" class="advise"></textarea>
  • 定义样式
  1. .advise {
  2. background-color: green;
  3. height: 750px;
  4. width: 30%;
  5. float: left;
  6. box-sizing: border-box;
  7. }
  • 新增路由advise

在路由配置文件中新增路由并制定定位器:

  • 新增开始咨询链接并指定定位器

    指定定位器仍然通过routerLink,格式:<a [routerLink]="[{outlets:{primary:'home', advise:'advise'}}]">开始咨询</a>

    表示点击链接时,没有名字(前面说了,它的默认名字叫primary)的定位器处显示路径为/home即组件是HomeComponent的内容,而名叫advise定位器处显示路径为/advise即组件是AdviseComponent的内容。

    同理,定义结束咨询的:<a [routerLink]="[{outlets:{advise:null}}]">书籍</a>

  • 添加名叫advise的定位器

  • 效果

路由守卫

路由守卫一般涉及三个类:CanActivate,CanDeactivate,Resolve.

CanActivate:决定是否有权限进入某路由;

CanDeactivate:决定是否可以离开某路由;

Resolve:初始化某个组件中的被Resolve绑定的类型参数;

CanActivate用法

新建ts文件:PermissionGuard.ts

  1. import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
  2. import {Observable} from 'rxjs/Observable';
  3. import * as _ from 'lodash';
  4. export class PermissionGuard implements CanActivate {
  5. canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean> | Promise<boolean> {
  6. const rand = _.random(0, 5);
  7. console.log(rand);
  8. return rand % 2 === 0;
  9. }
  10. }

在路由配置文件中增加canActivate参数:

  1. const routes: Routes = [
  2. {path: '', redirectTo: '/home', pathMatch: 'full'},
  3. {path: 'home', component: HomeComponent},
  4. {path: 'advise', component: AdviseComponent, outlet: 'advise'},
  5. /*{path: 'books/:bookname', component: BooksComponent, data: [{isChineseVersion: true}]},*/
  6. {
  7. path: 'books/:bookname',
  8. component: BooksComponent,
  9. children: [
  10. {path: '', component: IntroductionCnComponent},
  11. {path: 'en', component: IntroductionEnComponent}
  12. ],
  13. canActivate: [PermissionGuard]
  14. },
  15. {path: '**', component: Err404Component}
  16. ];

在模块配置文件app.moudle.ts中的providers参数中增加实现了CanActivate接口的值:

  1. providers: [PermissionGuard]

目的是实例化PermissionGuard

此时,再次点击链接书籍时会先判断生成的随机数除2时的余数,余0则可进入,否则被阻止。

CanDeactivate用法

需求:

当咨询窗口已被打开,并且已经输入了内容,此时突然点击结束,会被阻止,类似博客编辑到一半突然访问其他网站时会给出提示一样。

新建ts文件LeaveGuard.ts

  1. import {CanDeactivate} from '@angular/router';
  2. import {AdviseComponent} from '../advise/advise.component';
  3. import {Observable} from 'rxjs/Observable';
  4. export class LeaveGuard implements CanDeactivate<AdviseComponent> {
  5. /**
  6. * 下面这段代码是自带实现,暂时注掉不用它。
  7. * canDeactivate(component: AdviseComponent,
  8. * currentRoute: ActivatedRouteSnapshot,
  9. * currentState: RouterStateSnapshot,
  10. * nextState?: RouterStateSnapshot)
  11. * : boolean | Observable<boolean> | Promise<boolean> {
  12. * throw new Error("Method not implemented.");
  13. * }
  14. * @param advise
  15. * @returns {boolean}
  16. */
  17. canDeactivate(advise: AdviseComponent): boolean | Observable<boolean> | Promise<boolean> {
  18. return advise.isEmpty();
  19. }
  20. }

可以看到CanDeactivate不同于CanActivate,使用时需要输入泛型类,用以绑定数据。代码中使用了isEmpty(),它是咨询组件AdviseComponent中的一个方法,表示当输入的咨询内容为空时返回真,代码如下:

  1. import {Component, OnInit} from '@angular/core';
  2. import * as $ from 'jquery';
  3. @Component({
  4. selector: 'app-advise',
  5. templateUrl: './advise.component.html',
  6. styleUrls: ['./advise.component.css']
  7. })
  8. export class AdviseComponent implements OnInit {
  9. constructor() {
  10. }
  11. ngOnInit() {
  12. }
  13. isEmpty(): boolean {
  14. const adviseVal = $('textarea').val();
  15. console.log(adviseVal);
  16. return adviseVal === '';
  17. }
  18. }

在路由配置文件中为组件AdviseComponent添加canDeactivate属性并赋值:

  1. {
  2. path: 'advise',
  3. component: AdviseComponent,
  4. outlet: 'advise',
  5. canDeactivate: [LeaveGuard]
  6. }

在模块文件app.module.ts参数providers新增LeaveGuard用以实例化:

  1. providers: [PermissionGuard, LeaveGuard]

Resolve

为方便演示,先将路由配置文件中关于BookComponentcanActivate参数注掉,防止干扰。

books.componnet.ts中新建类Book,将属性bookname替换名为book类型为Book的成员变量,添加对对此成员变量的订阅方法:

  1. import {Component, OnInit} from '@angular/core';
  2. import {ActivatedRoute} from '@angular/router';
  3. @Component({
  4. selector: 'app-books',
  5. templateUrl: './books.component.html',
  6. styleUrls: ['./books.component.css']
  7. })
  8. export class BooksComponent implements OnInit {
  9. public book: Book;
  10. private isChineseVersion: boolean;
  11. constructor(private activatedRout: ActivatedRoute) {
  12. }
  13. ngOnInit() {
  14. // 普通方式传参的快照获取方式
  15. // this.bookname = this.activatedRout.snapshot.queryParams['bookname'];
  16. // rest方式传参的快照获取方式
  17. // this.bookname = this.activatedRout.snapshot.params['bookname'];
  18. // rest方式传参的订阅获取方式
  19. // this.activatedRout.params.subscribe((params: Params) => this.book.bookname = params['bookname']);
  20. this.activatedRout.data.subscribe((data: { book: Book }) => this.book = data.book);
  21. // 路由配置方式传参的获取方式
  22. // this.isChineseVersion = this.activatedRout.snapshot.data[0]['isChineseVersion'];
  23. }
  24. }
  25. export class Book {
  26. public bookname: string;
  27. public author?: string;
  28. public private?: number;
  29. constructor(bookname: string) {
  30. this.bookname = bookname;
  31. }
  32. }

修改books.component.htmlquery book is name of {{bookname}} => query book is name of {{book?.bookname}}

新建BookResolve.ts:

  1. import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router';
  2. import {Book} from '../books/books.component';
  3. import {Observable} from 'rxjs/Observable';
  4. import {Injectable} from '@angular/core';
  5. @Injectable()
  6. export class BookResolve implements Resolve<Book> {
  7. constructor(private router: Router) {
  8. }
  9. resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Book | Observable<Book> | Promise<Book> {
  10. const bookname = route.params['bookname'];
  11. if (bookname === '《活着》' || bookname === '《简爱》') {
  12. console.log('当前输入书名为:' + bookname);
  13. return new Book('《简爱》');
  14. } else {
  15. this.router.navigate(['/home']);
  16. return null;
  17. }
  18. }
  19. }

在路由控制文件中为组件BooksComponent添加resolve参数并赋值:resolve: {book: BookResolve}

在模块文件app.module.ts参数providers新增BookResolve用以实例化:

  1. providers: [PermissionGuard, LeaveGuard, BookResolve]

总结

源码

http://pan.baidu.com/s/1bpNdgFt

使用前先运行ci.bat~

Angular4学习笔记(三)- 路由的更多相关文章

  1. 微信小程序学习笔记三 路由的基本使用

    小程序中路由的使用 1.1 页面路由 在小程序中, 所有页面的路由全部由框架进行管理 1.2 页面栈 框架以栈的形式维护了当前的所有页面, 当发生路由切换的时候, 页面栈的表现如下: 1.3 获取当前 ...

  2. Angular4学习笔记-目录汇总

    Angular4学习笔记(一)-环境搭建 Angular4学习笔记(二)-在WebStorm中启动项目 Angular4学习笔记(三)- 路由 Angular4学习笔记(四)- 依赖注入 Angula ...

  3. Angular4学习笔记(十)- 组件间通信

    分类 父子组件通信 非父子组件通信 实现 父子 父子组件通信一般使用@Input和@Output即可实现,参考Angular4学习笔记(六)- Input和Output 通过Subject 代码如下: ...

  4. angular学习笔记(三十一)-$location(2)

    之前已经介绍了$location服务的基本用法:angular学习笔记(三十一)-$location(1). 这篇是上一篇的进阶,介绍$location的配置,兼容各版本浏览器,等. *注意,这里介绍 ...

  5. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  6. [Firefly引擎][学习笔记三][已完结]所需模块封装

    原地址:http://www.9miao.com/question-15-54671.html 学习笔记一传送门学习笔记二传送门 学习笔记三导读:        笔记三主要就是各个模块的封装了,这里贴 ...

  7. JSP学习笔记(三):简单的Tomcat Web服务器

    注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...

  8. java之jvm学习笔记三(Class文件检验器)

    java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...

  9. VSTO学习笔记(三) 开发Office 2010 64位COM加载项

    原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(A ...

  10. Java IO学习笔记三

    Java IO学习笔记三 在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在了一组字节流-字符流的转换类. OutputStreamWriter:是Writer的子类,将输出的 ...

随机推荐

  1. [COGS2554][SYZOJ247][福利]可持久化线段树

    思路: 主席树模板. 注意内存的分配,原始的线段树有$2n$个结点,每次更新时最多增加$log(n)$个结点,总共有$q$次询问,所以存储结点的数组大小为$2N+q log(n)$. #include ...

  2. java生成一次性验证码

    1.编写生成验证码的工具类: import java.awt.BasicStroke;import java.awt.Color;import java.awt.Font;import java.aw ...

  3. IIS远程发布(Web Deploy)

    作为开发人员,我们之前发布应用很可能是拷贝开发环境上发布好的代码文件到应用服务器硬盘中,然后在IIS中部署网站. 但是今天我们讲的是如果直接在我们的开发环境通过VS远程发布网站到应用服务器上,这将极大 ...

  4. 对请求并发数做限制的通用RequestDecorator

    使用场景 在开发中,我们可能会遇到一些对异步请求数做并发量限制的场景,比如说微信小程序的request并发最多为5个,又或者我们需要做一些批量处理的工作,可是我们又不想同时对服务器发出太多请求(可能会 ...

  5. 微信小程序 —— 动态决定页面元素显示或隐藏的技巧

    在微信小程序开发中,经常遇到一些由后台控制显示(is_open : 1)或者隐藏(is_open : 0),有俩种办法: 复杂办法 1.先在元素的class中 class=’{{show?’true’ ...

  6. Asp.Net JsonResult重写

    在Json序列化工具中,Newtonsoft.Json 的工具包相对比较好用. 对于循环引用.序列化格式.时间格式等都提供了可配置. 如果想重写Mvc 自带 JsonResult 返回结果,提供了2中 ...

  7. 深入理解 Java try-with-resource 语法糖

    背景 众所周知,所有被打开的系统资源,比如流.文件或者Socket连接等,都需要被开发者手动关闭,否则随着程序的不断运行,资源泄露将会累积成重大的生产事故. 在Java的江湖中,存在着一种名为fina ...

  8. jquery操作radio,checkbox

    1. 获取radio选中的value. $('input:radio[name=sex]:checked').val(); 2. 选择 radio 按钮 (Male). $('input:radio[ ...

  9. Connection is read-only. Queries leading to data modification are not allowed

    看了下mysql-connector-5.1.40版本中,如果设置failoverReadOnly=true (即默认值,参考链接),当mysql连接failover时,会根据jdbc连接串将当前连接 ...

  10. 内存优化总结:ptmalloc、tcmalloc和jemalloc(转)

    转载于:http://www.cnhalo.net/2016/06/13/memory-optimize/ 概述 需求 系统的物理内存是有限的,而对内存的需求是变化的, 程序的动态性越强,内存管理就越 ...