Angular 从入坑到挖坑 - Router 路由使用入门指北
一、Overview
Angular 入坑记录的笔记第五篇,因为一直在加班的缘故拖了有一个多月,主要是介绍在 Angular 中如何配置路由,完成重定向以及参数传递。至于路由守卫、路由懒加载等“高级”特性,并不会在本篇文章中呈现
对应官方文档地址:
配套代码地址:angular-practice/src/router-tutorial
二、Contents
- Angular 从入坑到弃坑 - Angular 使用入门
- Angular 从入坑到挖坑 - 组件食用指南
- Angular 从入坑到挖坑 - 表单控件概览
- Angular 从入坑到挖坑 - HTTP 请求概览
- Angular 从入坑到挖坑 - Router 路由使用入门指北
三、Knowledge Graph

四、Step by Step
4.1、基础概念
4.1.1、base url
在 Angular 应用中,框架会自动将 index.html 文件中的 base url 配置作为组件、模板和模块文件的基础路径地址。默认的情况下 app 文件夹是整个应用的根目录,所以我们直接使用 index.html 中使用默认的 <base href='/'> 即可
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>RouterTutorial</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
4.1.2、路由的配置
在 Angular 项目中,系统的路由需要我们将一个 url 地址映射到一个展示的组件,因此需要手动的去设置 url 与组件之间的映射关系
因为我们在使用 Angular CLI 创建项目时,选择了添加路由模组,因此我们可以直接在 app-routing.module.ts 文件中完成路由的定义。最终我们定义的路由信息,都会在根模块中被引入到整个项目
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './components/home/home.component';
import { PagenotfoundComponent } from './components/pagenotfound/pagenotfound.component';
import { NewsComponent } from './components/news/news.component';
import { ProductComponent } from './components/product/product.component';
// 配置路由信息
const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'news', component: NewsComponent },
{ path: 'product', component: ProductComponent },
{ path: '**', component: PagenotfoundComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule // 引入路由配置信息
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

当定义好路由信息后,我们需要在页面上使用 <router-outlet> 标签来告诉 Angular 在何处渲染出页面。对于路由之间的跳转,我们可以在 a 标签上通过使用 RouterLink 指令来绑定具体的路由来完成地址的跳转
<div class="card-container">
<a class="card" [routerLink]="[ '/news' ]" routerLinkActive="active">
<span>News</span>
</a>
<a class="card" [routerLink]="[ '/product' ]" routerLinkActive="active">
<span>Product</span>
</a>
</div>
<div class="card-container">
<div class="form-card">
<!-- 组件渲染的出口 -->
<router-outlet></router-outlet>
</div>
</div>
</div>
当然,如果你非要自己给自己找事,就是要用 a 标签的 href 属性进行跳转,当然也是可以的,不过在后面涉及到相关框架的功能时就会显得有点不辣么聪明的样子了
4.1.3、重定向与通配地址
在普遍情况下,对于进入系统后的默认路径,我们会选择重定向到一个具体的地址上,这里我们在定义路由信息时,定义了一个空路径用来表示系统的默认地址,当用户请求时,重定向到 /home 路径上,因为只有完整的 url 地址匹配空字符串时才应该进行重定向操作,所以这里需要指定匹配模式是全部匹配

const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: '', redirectTo: 'home', pathMatch: 'full' }
];
Angular 在解析路由时,是按照我们定义路由时的顺序依次进行的,一旦匹配就会立即终止。因此,类似于 404 错误的这种通配的路由配置,因为可以匹配上每个 url 地址,所以应该在定义时放到最后
const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'news', component: NewsComponent },
{ path: 'product', component: ProductComponent },
{ path: '**', component: PagenotfoundComponent },
];

从截图中可以看到,当我们打开系统时,会自动跳转到我们指定的 home 路径,点击菜单按钮后,则会加载对应的组件页面
4.1.4、激活的路由
很多情况下,对于被选中的路由,我们可能会添加一个特定的样式来进行提示用户,因此,在我们定义 router-link 时,可以使用 routerLinkActive 属性绑定一个 css 的样式类,当该链接对应的路由处于激活状态时,则自动添加上指定的样式类

4.2、路由间的参数传递
在进行路由跳转时,很常见的一种使用情况是我们需要将某些数据作为参数传递到下一个页面中,例如从列表中选择点击某一行数据,跳转到对应的详情页面
常见的参数传递有如下的两种方式
4.2.1、query 查询参数传递
最常见的一种参数传递的方式,在需要跳转的路由地址后面加上参数和对应的值,在跳转后的页面通过获取参数 key 从而获取到对应的参数值
<a href="www.yoursite.com/product?productId=xxxx">跳转</a>
对于直接通过 a 标签进行的路由跳转,我们可以在 a 标签上通过绑定 queryParams 属性来添加查询参数信息
这里通过 queryParams 属性绑定的是一个对象,Angular 会自动的帮我们将这个参数对象与 url 进行拼接。对于参数对象中的属性(key)对应的属性值(value),我们可以绑定一个组件中的属性进行动态的赋值,也可以通过添加单引号将参数值作为一个固定的数值,例如在下面代码中的两个查询参数就是固定的值
<a class="card" [routerLink]="[ '/news' ]" routerLinkActive="active" [queryParams]="{category:'social',date:'2020-05-02'}">News</a>

同样的,我们也可以在 js 中完成路由的跳转,对于这种使用场景,我们需要在进行 js 跳转的组件类中通过构造函数依赖注入 Router 类,之后通过 Router 类的 navigate 方法完成路由的跳转;对于可能存在的查询参数,我们需要定义一个 NavigationExtras 类型的变量来进行设置
import { Component, OnInit } from '@angular/core';
// 引入路由模块
import { Router, NavigationExtras } from '@angular/router';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
constructor(private router: Router) {}
ngOnInit(): void {}
/**
* 使用 js 的方式通过 query 查询字符串的形式传递参数
*/
queryNavigate() {
// 查询参数
let query: NavigationExtras = {
queryParams: {
category: 'social',
date: '2020-05-04'
}
};
this.router.navigate(['/news' ], query);
}
}
既然在进行跳转时附加了参数信息,在跳转后的页面我们肯定需要获取到传递的参数值。在 Angular 中,需要在组件类中依赖注入 ActivatedRoute 来获取传递的参数信息
这里的 queryParamMap 是一个 Observable 对象,所以这里需要使用 subscribe 方法来获取传递的参数值
import { Component, OnInit } from '@angular/core';
// 引入路由模块
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-news',
templateUrl: './news.component.html',
styleUrls: ['./news.component.scss']
})
export class NewsComponent implements OnInit {
constructor(private route: ActivatedRoute) { }
ngOnInit(): void {
this.route.queryParamMap.subscribe((data: any) => {
console.log(data.params);
});
}
}

4.2.2、动态路由传递
与使用查询参数不同,使用动态路由进行参数传值时,需要我们在定义路由时就提供参数的占位符信息,例如在下面定义路由的代码里,对于组件所需的参数 newsId,我们需要在定义路由时就指明
const routes: Routes = [
{ path: 'news/detail/:newsId', component: NewsDetailComponent },
];
对于采用动态路由进行的路由跳转,在 a 标签绑定的 routerLink 属性数组的第二个数据中,需要指定我们传递的参数值。例如这里的 item.newsId 变量就是我们需要传递的参数值
<ul>
<li *ngFor="let item of newsList; let i = index">
<a [routerLink]="['/news/detail', item.newsId]" routerLinkActive="active" >
{{item.title}}
</a>
</li>
</ul>
而采用 js 的方式进行跳转时,我们同样需要使用依赖注入的方式注入 Router 类,然后调用 navigate 方法进行跳转。与使用 query 查询参数传递数据不同,此时需要将跳转的链接与对应的参数值组合成为一个数组参数进行传递
import { Component, OnInit } from '@angular/core';
// 引入路由模块
import { Router, ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-news',
templateUrl: './news.component.html',
styleUrls: ['./news.component.scss']
})
export class NewsComponent implements OnInit {
newsList: any;
constructor(private route: ActivatedRoute, private router: Router) {
this.newsList = [{
newsId: 1111,
title: 'lalalalalallaaa'
}, {
newsId: 2222,
title: 'lalalalalallaaa'
}, {
newsId: 3333,
title: 'lalalalalallaaa'
}];
}
ngOnInit(): void {
this.route.queryParamMap.subscribe((data: any) => {
console.log(data.params);
});
}
routerNavigate() {
this.router.navigate(['/news/detail', 11111]);
}
}
在获取参数数据的组件类中,需要依赖注入 ActivatedRoute 类,因为是采用的动态路由的方式进行的参数传递,这里需要通过 paramMap 属性获取到对应的参数值
import { Component, OnInit } from '@angular/core';
// 引入路由模块
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-news-detail',
templateUrl: './news-detail.component.html',
styleUrls: ['./news-detail.component.scss']
})
export class NewsDetailComponent implements OnInit {
constructor(private route: ActivatedRoute) { }
ngOnInit(): void {
this.route.paramMap.subscribe((data: any) => {
console.log(data.params);
});
}
}

4.3、嵌套路由
在一些情况下,路由是存在嵌套关系的,例如下面这个页面,只有当我们点击资源这个顶部的菜单后,它才会显示出左侧的这些菜单,也就是说这个页面左侧的菜单的父级菜单是顶部的资源菜单

针对这种具有嵌套关系的路由,在定义路由时,我们需要通过配置 children 属性来指定路由之间的嵌套关系,例如这里我定义 ProductDetailComponent 这个组件和 ProductComponent 组件形成的路由之间具有嵌套关系
// 配置路由信息
const routes: Routes = [
{
path: 'product', component: ProductComponent, children: [{
path: 'detail', component: ProductDetailComponent
}, {
path: '', redirectTo: 'detail', pathMatch: 'full'
}]
}
];
因为子路由的渲染出口是在父路由的页面上,因此当嵌套路由配置完成之后,在嵌套的父级页面上,我们需要定义一个 <router-outlet> 标签用来指定子路由的渲染出口,最终的效果如下图所示
<h3>我是父路由页面显示的内容</h3>
<p>product works!</p>
<!-- 加载子路由的数据 -->
<h3>子路由组件渲染的出口</h3>
<router-outlet></router-outlet>

Angular 从入坑到挖坑 - Router 路由使用入门指北的更多相关文章
- Angular 从入坑到挖坑 - 路由守卫连连看
一.Overview Angular 入坑记录的笔记第六篇,介绍 Angular 路由模块中关于路由守卫的相关知识点,了解常用到的路由守卫接口,知道如何通过实现路由守卫接口来实现特定的功能需求,以及实 ...
- Angular 从入坑到挖坑 - 模块简介
一.Overview Angular 入坑记录的笔记第七篇,介绍 Angular 中的模块的相关概念,了解相关的使用场景,以及知晓如何通过特性模块来组织我们的 Angular 应用 对应官方文档地址: ...
- Angular 从入坑到挖坑 - 组件食用指南
一.Overview angular 入坑记录的笔记第二篇,介绍组件中的相关概念,以及如何在 angular 中通过使用组件来完成系统功能的实现 对应官方文档地址: 显示数据 模板语法 用户输入 组件 ...
- Angular 从入坑到挖坑 - 表单控件概览
一.Overview angular 入坑记录的笔记第三篇,介绍 angular 中表单控件的相关概念,了解如何在 angular 中创建一个表单,以及如何针对表单控件进行数据校验. 对应官方文档地址 ...
- Angular 从入坑到挖坑 - HTTP 请求概览
一.Overview angular 入坑记录的笔记第四篇,介绍在 angular 中如何通过 HttpClient 类发起 http 请求,从而完成与后端的数据交互. 对应官方文档地址: Angul ...
- Angular 从入坑到挖坑 - Angular 使用入门
一.Overview angular 入坑记录的笔记第一篇,完成开发环境的搭建,以及如何通过 angular cli 来创建第一个 angular 应用.入坑一个多星期,通过学习官方文档以及手摸手的按 ...
- 【转】angular指令入坑
独立作用域和函数参数 通过使用本地作用域属性,你可以传递一个外部的函数参数(如定义在控制器$scope中的函数)到指令.这些使用&就可以完成.下面是一个例子,定义一个叫做add的本地作用域属性 ...
- webpack入坑之旅(一)入门安装
学习一个新的东西,首先第一步就是安装,有时候会遇到各种奇葩的问题 至于什么是webpack我这里就不介绍了,请参考官网:https://github.com/webpack/webpack 安装 前提 ...
- web前端入坑第五篇:秒懂Vuejs、Angular、React原理和前端发展历史
秒懂Vuejs.Angular.React原理和前端发展历史 2017-04-07 小北哥哥 前端你别闹 今天来说说 "前端发展历史和框架" 「前端程序发展的历史」 「 不学自知, ...
随机推荐
- 百度AI开发平台简介
AIstudio https://aistudio.baidu.com/aistudio/index 关于AI Studio AI Studio是基于百度深度学习平台飞桨的一站式AI开发平台,提供在线 ...
- [linux] 权限问题
权限问题一直蒙蒙的,下面就是总结一下!(原文链接:http://www.cnblogs.com/chengJAVA/p/4319420.html) 指令名称:chmod 使用权限 : 所有使用者 使用 ...
- 开源软件SoftEther使用
最近在寻找比较好用的开源VPN,感觉SoftEther很符合我的需求.一方面是SoftEther属于开源软件并且一直在更新,另一方面是功能强大,好用. VPN支持路由功能和NAT功能,还支持多种类型的 ...
- shiro:集成Spring(四)
基于[加密及密码比对器(三)]项目改造 引入相关依赖环境 shiro-spring已经包含 shiro-core和shiro-web 所以这两个依赖可以删掉 <!--shiro继承spring依 ...
- 图数据库的内部结构 (NEO4j)
What “Graph First” Means for Native Graph Technology Neo4j是一个具有原生处理(native processing)功能和原生图存储(nativ ...
- ES[7.6.x]学习笔记(三)新建索引
与ES的交互方式 与es的交互方式采用http的请求方式,请求的格式如下: curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT& ...
- 初学者的Pygame安装教程
最近在自学python,在看完了些基础知识之后,准备写个小项目[外星人入侵],这个项目需要安装pygame. 所以就在网上找到了两个下载地址https://bitbucket.org/pygame/p ...
- 详解PHP中instanceof关键字及instanceof关键字有什么作用
来源:https://www.jb51.net/article/74409.htm PHP5的另一个新成员是instdnceof关键字.使用这个关键字可以确定一个对象是类的实例.类的子类,还是实现了某 ...
- 2019-2020-1 20199308《Linux内核原理与分析》第六周作业
<Linux内核分析> 第五章 系统调用的三层机制(下) 5.1 给MenuOS增加命令 强制删除当前menu目录,用get clone重新克隆一个新版本的menu,运行make root ...
- opencv-0-项目启程
opencv-0-项目启程 opencvC++QT 开始 无数次说要开始 opencv 的系列, 但是都由于各种原因没有坚持下去, 这次我做最后一次尝试, 也做最后一次坚持, 如果不做下去, 我就再也 ...