在分层路由的每个级别上,你都可以设置多个守卫。 路由器会先按照从最深的子路由由下往上检查的顺序来检查 [CanDeactivate](https://angular.cn/api/router/CanDeactivate)()[CanActivateChild](https://angular.cn/api/router/CanActivateChild)() 守卫。 然后它会按照从上到下的顺序检查 [CanActivate](https://angular.cn/api/router/CanActivate)() 守卫。 如果特性模块是异步加载的,在加载它之前还会检查 [CanLoad](https://angular.cn/api/router/CanLoad)() 守卫。 如果任何一个守卫返回 false,其它尚未完成的守卫会被取消。

Resolve预先获取数据

该方法可以返回一个 Promise、一个 Observable 来支持异步方式,或者直接返回一个值来支持同步方式。
你希望的是只有当所有必要数据都已经拿到之后,才渲染这个路由组件。

  1. 路由器的这个 [Resolve](https://angular.cn/api/router/Resolve) 接口是可选的。CrisisDetailResolverService 没有继承自某个基类。路由器只要找到了这个方法,就会调用它。
  2. 要依赖路由器调用此守卫。不必关心用户用哪种方式导航离开,这是路由器的工作。你只要写出这个类,等路由器从那里取出它就可以了。

    Resolve服务

    1. export class CrisisDetailResolverService implements Resolve<Crisis> {
    2. constructor(private cs: CrisisService, private router: Router) {}
    3. // 该方法可以返回一个 Promise、一个 Observable 来支持异步方式,或者直接返回一个值来支持同步方式。
    4. resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Crisis> | Observable<never> {
    5. let id = route.paramMap.get('id');
    6. return this.cs.getCrisis(id).pipe(
    7. take(1),
    8. mergeMap(crisis => {
    9. if (crisis) {
    10. return of(crisis);
    11. } else { // id not found
    12. this.router.navigate(['/crisis-center']);
    13. return EMPTY; // 注意 EMPTY: Observable<never>
    14. }
    15. })
    16. );
    17. }
    18. }

    RoutingModule中resolve选项

    1. const crisisCenterRoutes: Routes = [
    2. {
    3. path: 'crisis-center',
    4. component: CrisisCenterComponent,
    5. children: [
    6. {
    7. path: '',
    8. component: CrisisListComponent,
    9. children: [
    10. {
    11. path: ':id',
    12. component: CrisisDetailComponent,
    13. canDeactivate: [CanDeactivateGuard],
    14. resolve: {
    15. crisis: CrisisDetailResolverService
    16. }
    17. }
    18. ]
    19. }
    20. ]
    21. }
    22. ];

    组件中订阅ActivateRoute.data

    1. ngOnInit() {
    2. this.route.data // activateRoute.data: 解析守卫(resolve guard)解析而来的值。
    3. .subscribe((data: { crisis: Crisis }) => {
    4. this.editName = data.crisis.name;
    5. this.crisis = data.crisis;
    6. });
    7. }

    CanDeactivate

    创建了一个 Guard,它将检查这个(任意)组件中是否有 [canDeactivate](https://angular.cn/api/router/Route#canDeactivate)() 函数

  3. 通用guard,它将检查任意组件中是否有 [canDeactivate](https://angular.cn/api/router/Route#canDeactivate)() 函数

  4. 只想为这个组件使用该守卫,并且需要获取该组件属性或确认路由器是否允许从该组件导航出去时

应用:表单录入

  1. export interface CanComponentDeactivate {
  2. canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
  3. }
  4. export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  5. canDeactivate(component: CanComponentDeactivate) {
  6. return component.canDeactivate ? component.canDeactivate() : true;
  7. }
  8. }

CanActive

无组件路由

一个无组件的路由能让守卫子路由变得更容易。
这里的目标是对 admin 路径下的 危机中心 管理类路由进行分组,但并不需要另一个仅用来分组路由的组件。
同样把这个 AuthGuard 添加到“无组件的”管理路由,来同时保护它的所有子路由,而不是为每个路由单独添加这个 AuthGuard

  1. const adminRoutes: Routes = [
  2. {
  3. path: 'admin',
  4. component: AdminComponent,
  5. canActivate: [AuthGuard],
  6. children: [
  7. {
  8. path: '',
  9. canActivateChild: [AuthGuard],
  10. children: [
  11. { path: 'crises', component: ManageCrisesComponent },
  12. { path: 'heroes', component: ManageHeroesComponent },
  13. { path: '', component: AdminDashboardComponent }
  14. ]
  15. }
  16. ]
  17. }
  18. ];
  1. export class AuthGuard implements CanActivate {
  2. constructor(private authService: AuthService, private router: Router) {}
  3. canActivate(
  4. next: ActivatedRouteSnapshot,
  5. state: RouterStateSnapshot): boolean {
  6. let url: string = state.url;
  7. return this.checkLogin(url);
  8. }
  9. checkLogin(url: string): boolean {
  10. if (this.authService.isLoggedIn) { return true; }
  11. // Store the attempted URL for redirecting
  12. this.authService.redirectUrl = url;
  13. // Navigate to the login page with extras
  14. this.router.navigate(['/login']);
  15. return false;
  16. }
  17. }

router.parseUrl

  1. // Get the redirect URL from our auth service
  2. // If no redirect has been set, use the default
  3. let redirect = this.authService.redirectUrl ? this.router.parseUrl(this.authService.redirectUrl) : '/admin';

多个守卫

无法实现前一个路由返回false后,不执行后面的路由
github issue 已关闭
hack 方式,包装为一个路由:

  1. import {Injectable, Injector} from "@angular/core";
  2. import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree} from "@angular/router";
  3. @Injectable()
  4. export class AllGuard implements CanActivate {
  5. constructor(private injector: Injector) {
  6. }
  7. async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean | UrlTree> {
  8. let guards = route.data.guards || [];
  9. for (let guard of guards) {
  10. let instance: CanActivate = this.injector.get(guard);
  11. let result = await instance.canActivate(route, state);
  12. if (result === false || result instanceof UrlTree) {
  13. return result;
  14. }
  15. }
  16. return true;
  17. }
  18. }
  1. @NgModule({
  2. imports: [
  3. SomeOtherModule,
  4. RouterModule.forRoot([
  5. { path: '', data: {guards: [SomeGuard]}, canActivate: [AllGuard]
  6. ])
  7. ],
  8. providers: [AllGuard]
  9. })
  10. export class SomeModule {}

CanActivateChild:保护子路由

[CanActivateChild](https://angular.cn/api/router/CanActivateChild) 会在任何子路由被激活之前运行。
扩展 AuthGuard 以便在 admin 路由之间导航时提供保护。