angular6路由守卫详解

一. 什么是路由守卫?什么情况下使用路由守卫?

定义:在进入或离开路由时进行一定条件的判断和处理。

应用场景

  • 用户可能无权导航到目标组件

  • 用户得先登录(认证)

  • 在显示目标组件前,你可能得先获取某些数据

  • 在离开组件前,你可能要先保存修改

  • 你可能要询问用户:你是否要放弃本次更改,而不用保存它们?

路由守卫返回一个布尔值,来控制路由器的跳转行为

  • 返回 true,导航过程会继续
  • 返回 false,导航过程就会终止,且用户留在原地。
  • 返回 UrlTree,则取消当前的导航,并且开始导航到返回的这个 UrlTree.

路由守卫异步返回一个observablePromise,路由器会等待这个可观察对象被解析为true或false

路由器支持的几种守卫形式

  • CanActivate来处理导航某路由的情况。(要求认证)
  • CanActivateChild来处理导航某子路由的情况。(保护子路由)
  • CanDeactivate来处理从当前路由离开的情况.(离开时处理未保存的更改)
二. Router 离开守卫拦截 – (CanDeactivate:处理未保存的更改)
  • 决定放弃更改直接导航离开 -> 返回true
  • 保留未完成的更改,还留在当前页面 -> false
  1. 生成一个通用的守卫(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
})
  1. 检查特定组件离开拦截判断,创建特定的守卫。在需要访问外部信息时,canDeactivate() 方法为你提供了组件ActivatedRouteRouterStateSnapshot 的当前实例。
// 创建 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  代码同上
三. Router 路由进入认证守卫: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;
  }
}
四. Router 路由保护子路由守卫: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 },
        ]
      }
    ]
  }
];

你可能感兴趣的:(angular6系列)