学习路由基本知识
学习子路由、保护路由、辅助路由等
一、创建路由项目
使用 ng new xxx --routing
来创建一个带有angular官方路由的项目
加routing参数,会新增一个app-routing.module.ts 文件
二、路由基础知识
名称 |
简介 |
Routes |
路由的配置,保存着哪一个URL对应展示哪一个组件和在哪一个RouterOutlet展示 |
RouterOutler |
在HTML中标记路由内容呈现的占位符指令 |
Router |
运行时执行的路由对象,可以通过navigate()和navigateByUrl()方法来导航到指定路由 |
RouterLink |
在HTML中声明导航的指令 |
ActivatedRoute |
当前激活的路由对象,保存着当前路由信息,路由地址,路由参数等。 |
路由配置文件
app-routing.module.ts中配置路由
const routes: Routes = [
{ path: 'product', component: ProductComponent },
{ path: '', component: HomeComponent }
]
// 注意路径中不能加 / 号,如path:'/product'是会报错的
在html中写入标签<router-outlet></router-outlet>来展示相关组件
路由通用配置
{ path: '**', component:Code404Component }
// 路由路径的通用配置,注意不可以写在最前面,否则路由会优先匹配
routerLink的使用
<a [routerLink]="['/]">home</a>
<a [routerLink]="['/product']">产品</a>
<router-outlet></router-outlet>
// 点击a标签,导航到相关路径
Router路由对象导航到指定路由
<input type="button" value="产品" (click)="toProduct"/>
// tips: 这里的click是angular的事件绑定
export class Appcomponent = {
constructor(private router: Router){
// 这里可以拿到router对象
}
toProduct(){
// 导航到指定路由
this.router.navigate(['/product'])
}
}
在路由中传递数据(ActivatedRoute)
1.在查询参数中传递数据
/product?id=1&name=2 => ActivatedRoute.queryParams[id]
2.在路由路径中传递数据
{ path: /product/:id } => /product/1 => ActivatedRoute.params[id]
// 注意这里不是queryParams,而是params
3.在路由配置中传递数据
{ path: /product, component:ProductComponent, data:[{isProd}]} => ActivatedRoute.data[0][isProd]
在查询参数中传递数据
<a [routerLink]="['/product']" [queryParams="{id: 1}"]>产品</a>
// 在url中可以看到product?id=1
export class ProductComponent implements OnInit {
private productId: number;
constructor(private routeInfo: ActivatedRoute){
// 这里可以拿到routeInfo对象
}
ngOnInit(){
this.productId = this.routeInfo.snapshot.queryParams["id"];
}
}
// 这样就可以在product.component.html中展示拿到的productId 的值
<p>{{productId}}</p>
在路由路径中传递数据
// 在路由配置中修改path属性,使其可以携带参数
const routes: Routes = [
{ path: 'product/:id', component: ProductComponent },
]
// 在html中修改如下
<a [routerLink]="['/product',666]">产品</a>
// 这样路径就变为product/666
// 稍微改动ts文件,使html中可以得到路径传递数据方式的productId 的值
export class ProductComponent implements OnInit {
private productId: number;
constructor(private routeInfo: ActivatedRoute){
// 这里可以拿到routeInfo对象
}
ngOnInit(){
this.productId = this.routeInfo.snapshot.params["id"]; // 这里修改为params就行了
}
}
// 然后在html中可以显示出productId 的值了
<p>{{productId}}</p>
tips:关于参数快照snapshot和参数订阅
// 你可以看到上面的代码中,routeInfo对象有一个snapshot的属性,
这就是参数快照,但是只能被调用一次(组件被创建时,构造函数只能初始化一次),
为了解决因此导致的productId值不会被改变,我们可以使用参数订阅来搞定它
// 我们来修改这段代码
export class ProductComponent implements OnInit {
private productId: number;
constructor(private routeInfo: ActivatedRoute){
// 这里可以拿到routeInfo对象
}
ngOnInit(){
// 修改这里
this.routeInfo.params.subscribe((params: Params) => this.productId = params["id"])
//subscribe就是参数订阅,实际上是RxJs中的语法,在后面我们会讲到。
}
}
重定向路由
// 当用户访问一个特定的地址时,将其重定向到另一个指定的地址
// 我们修改路由配置
const routes: Routes = [
{ path: '', redirectTo:'/home', pathMatch: 'full' },
{ path: '/home', component: HomeComponent }
]
// 你可以看到在第一行中我加了一条新的路径,它告诉angular当我访问空路径时,把当前路由重定向到home的路径上,是不是很简单
子路由
{ path: '/home', component: HomeComponent } // 这是一个普通的路由配置
// 当我想配置子路由时
{ path: '/home', component: HomeComponent,
children: [
{
path: '',component: XxxComponent
},
{
path: '/yyy',component: YyyComponent
}
]
}
// html中
在相关模板加入<router-outlet></router-outlet>就能展示了
// 但是有点小问题需要注意,在html中,如果你想用a链接跳转到相关子路由,如''这个路径
<a [routerLink]="['./']"></a>
// 这样才能访问子路由中空路径的组件,如果是'/',则会跳转到主路由中的空路径而不是当前路由的子路由
辅助路由
// 辅助主路由,可以保持主路由变化时自身路由不变
// html中
<router-outlet></router-outlet>
<router-outlet name="aux"></router-outlet>
// 路由配置中
{ path: 'xxx', component: XxxComponent, outlet: 'aux' }
{ path: 'yyy', component: YyyComponent, outlet: 'aux' }
// 导航时
<a [routerLink]="['/home', {outlets: {aux: 'xxx'}}]">Xxx</a> // 主插座,导航到home组件,辅助路由显示xxx组件,下同
<a [routerLink]="['/product', {outlets: {aux: 'yyy'}}]">Yyy</a>
路由守卫(保护路由)
// 只有当用户已经登录并拥有某些权限时才能进入某些路由
// 一个由多个表单组件组成的向导,例如注册流程,用户只有在当前路由的组件中填写了满足要求的信息才能导航到下一个路由
// 当用户未执行保存操作而试图离开当前导航时提醒用户
// 三种路由守卫
1.CanActivate 处理导航到某路由的情况
2.CanDeactivate 处理从当前路由离开的情况
3.Resolve 在路由激活之前获取路由数据
1.CanActivate
// 我们在src/app文件夹下新建一个guard文件夹,在guard文件夹下新建login.guard.ts文件,用来模拟用户未登录或者登录后路由守卫可以做的事
// 在login.guard.ts文件中写
import {CanActivate} from "@angular/router";
export class LoginGuard implements CanActivate {
canActivate() {
let loggedIn: boolean = Math.random() < 0.5; // 我们写个随机数来返回布尔值
if(!loggedIn){
console.log("用户未登录")
}else {
console.log("用户已登录")
}
return loggedIn;
}
}
// 路由配置中
const routes: Routes = [
{path: 'product',component: ProductComponent, canActivate: [LoginGuard]}, // 比如我们要访问产品组件,需要判断登录状态
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers:[LoginGuard]
})
// 这样,在组件中点击你写好的按钮(导航到'product',你可以看到随机打印的登录状态,canActivate根据不同状态来处理不同逻辑)
2.CanDeactivate
// 我们在guard文件夹下新建unsaved.guard.ts文件
// 在unsaved.guard.ts中写
import {CanDeactivate} from "@angular/router";
import {ProductComponent} from "../product/product.component";
export class UnsavedGuard implements CanDeactivate<ProductComponent> {
canDeactivate(component: ProductComponent){
return window.confirm("你还没有保存信息,确定离开当前页面么?")
}
}
// 路由配置中
const routes: Routes = [
{path: 'product',component: ProductComponent, canActivate: [LoginGuard],canDeactivate: [UnsavedGuard]},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers:[LoginGuard,UnsavedGuard]
})
// 这样,当我们离开product路径时,CanDeactivate路由守卫会弹出框提示你是否离开当前页面
3.Resolve
// 我们在guard文件夹下新建product.resolve.ts文件
// 在product.resolve.ts中写
import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from "@angular/router";
import {Product} from "../product/product.component";
import {Observable} from "rxjs/Observable";
import {Injectable} from "@angular/core";
@Injectable()
export class ProductResolve implements Resolve<Product> {
constructor(private router: Router){
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Product> | Promise<Product> | Product {
let productId:number = route.params["id"];
if(productId == 1){
return new Product(1,"iphone7"); // 如果productId 为1,则商品ID为1,商品名称为7
}else {
this.router.navigate(['/home']); // 如果productId 不是1,则跳转到‘’/home'
return undefined;
}
}
}
// 路由配置中
const routes: Routes = [
{path: 'product',component: ProductComponent, canActivate: [LoginGuard],canDeactivate: [UnsavedGuard],resolve: {
product: ProductResolve
}},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers:[LoginGuard,UnsavedGuard,ProductResolve]
})
// 这样,我们在访问/product时,可以让Resolve带着路由信息访问该路径