定义:在进入或离开路由时进行一定条件的判断和处理。
应用场景:
用户可能无权导航到目标组件
用户得先登录(认证)
在显示目标组件前,你可能得先获取某些数据
在离开组件前,你可能要先保存修改
你可能要询问用户:你是否要放弃本次更改,而不用保存它们?
路由守卫返回一个布尔值,来控制路由器的跳转行为
true
,导航过程会继续false
,导航过程就会终止,且用户留在原地。UrlTree
,则取消当前的导航,并且开始导航到返回的这个 UrlTree
.路由守卫异步返回一个observable
或Promise
,路由器会等待这个可观察对象被解析为true或false。
路由器支持的几种守卫形式:
CanActivate
来处理导航到某路由的情况。(要求认证)CanActivateChild
来处理导航到某子路由的情况。(保护子路由)CanDeactivate
来处理从当前路由离开的情况.(离开时处理未保存的更改)CanDeactivate
:处理未保存的更改)
- 生成一个通用的守卫(guard),以检查组件(任意组件均可)中是否存在
canDeactivate()
方法。该守卫并不需要知道某个组件确认退出激活状态的详情。 它只需要检查该组件是否有一个canDeactivate()
方法,并调用它。 这就让该守卫可以复用。
// 新建 leava-guard.service.ts
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';
export interface CanComponentDeactivate {
canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}
@Injectable()
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
canDeactivate(component: CanComponentDeactivate) {
return component.canDeactivate ? component.canDeactivate() : true;
}
}
// video.ts 写入方法判断
import { Observable } from 'rxjs';
// 它返回observable,决定放弃更改直接导航离开(true),或者保留未完成的修改,留在危机编辑器中(false)。
canDeactivate(): Observable<boolean> | boolean {
if (this.isRecover === true) {
return true;
}
const obs = new Observable<boolean>(observer => {
this.modalService.confirm({
nzTitle: '您正在离开页面',
nzContent: '请问确认是否离开页面,您可能会丢失尚未保存的数据。',
nzOnOk: () => {
observer.next(true);
},
nzOnCancel: () => {
observer.next(false);
},
});
});
return obs;
}
// project-routing.module.ts
//在路由中用 canDeactivate 数组添加一个 Guard(守卫)。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { VideoComponent } from './video/video.component';
import { CanDeactivateGuard } from 'app/service/leave-guard.service';
import { CanDeactivateGuardVideo } from 'app/service/can-leave-video.service'; // 针对vuideo 创建的特定守卫
const routes: Routes = [
{ path: 'authenticate', component: AuthenticateComponent },
{
path: 'home',
component: HomeComponent,
children: [
{
path: 'video',
component: VideoComponent,
canDeactivate: [CanDeactivateGuard]
// canDeactivate: [CanDeactivateGuardVideo] // 特定守卫
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProjectRoutingModule { }
// project.module.ts
// 在 @NgModule providers 里面注册 守卫 !否则会报错误:Error: StaticInjectorError(AppModule)[CanDeactivateGuardVideo]
import { CanDeactivateGuard } from 'app/service/leave-guard.service';
import { CanDeactivateGuardVideo } from 'app/service/can-leave-video.service'; // 特定守卫
@NgModule({
imports: [],
declarations: [],
providers: [
CanDeactivateGuard,
// CanDeactivateGuardVideo // 特定守卫
],
entryComponents: COMPONENT_NOROUNT
})
- 检查特定组件离开拦截判断,创建特定的守卫。在需要访问外部信息时,
canDeactivate()
方法为你提供了组件、ActivatedRoute
和RouterStateSnapshot
的当前实例。
// 创建 can-leave-video.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { CanDeactivate,ActivatedRouteSnapshot,RouterStateSnapshot} from '@angular/router';
import { VideoComponent } from '../routes/project/video/video.component'; // 针对video.component 组件创建特定 守卫
@Injectable()
export class CanDeactivateGuardVideo implements CanDeactivate<VideoComponent> {
canDeactivate(
component: VideoComponent,
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
nextState: RouterStateSnapshot,
): Observable<boolean> | boolean {
const nextRoute = nextState.url;
const nextRouteArray = nextRoute.split('/');
// 允许进入home子集路由
if (nextRouteArray.indexOf('project') !== -1 && nextRouteArray.indexOf('home') !== -1) {
return true;
}
return component.canDeactivate();
}
}
// video.ts / project-routing.module.ts / project.module.ts 代码同上
CanActivate
// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean {
let url: string = state.url;
...
return true;
}
}
CanActivateChild
CanActivateChild
:保护子路由。CanActivateChild
守卫和 CanActivate
守卫很像。 它们的区别在于,CanActivateChild
会在任何子路由被激活之前运行。保护管理特性模块,防止它被非授权访问,还要保护这个特性模块内部的那些子路由。import { Injectable } from '@angular/core';
import {
CanActivate, Router,
ActivatedRouteSnapshot,
RouterStateSnapshot,
CanActivateChild
} from '@angular/router';
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate, CanActivateChild {
constructor() {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean {
let url: string = state.url;
return true;
}
canActivateChild(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean {
return this.canActivate(route, state);
}
}
// admin-routing.module.ts
import { AuthGuard } from '../auth/auth.guard';
const adminRoutes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard],
children: [
{
path: '',
canActivateChild: [AuthGuard],
children: [
{ path: 'crises', component: ManageCrisesComponent },
]
}
]
}
];