代码之家  ›  专栏  ›  技术社区  ›  mwilson

角路标/动态导航

  •  3
  • mwilson  · 技术社区  · 7 年前

    我有一个应用程序,我的导航栏会根据产品的“区域”进行更改。我正在使用安格拉斯路线警卫,以确保他们的访问被检查,以便他们只能击中他们有权访问的路线。这很管用!

    在我的 app-routing-module.ts 我正在努力变得聪明,利用ActivatedRouteSnapshot获取所有子链接,然后为它构建一个导航。我想做的是,还可以使用Route Guard来决定是否应该显示子链接。

    //守卫

    import { Injectable } from '@angular/core';
    import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
    import { environment } from '../../environments/environment';
    import { MeService } from '../shared/services/me.service';
    
    @Injectable()
    export class AdminGuard implements CanActivate {
      constructor(private _meService: MeService) {}
      async canActivate(
        next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
        const user = await this._meService.getCurrentUser();
        if (user && user.isUserAdminForCompany) {
          return true;
        } else {
          return false;
        }
      }
    }
    

    //路线

    export const routes: Routes = [
      { path: '', redirectTo: 'route1', pathMatch: 'full' },
      { path: 'route1', component: MyComponent,
        children: [
          { path: '', redirectTo: 'overview', pathMatch: 'full' },
          { path: 'overview', component: Overview },
          { path: 'specs', component: Specs, canActivate: [ AdminGuard ] }
        ]
      }
    ];
    

    所以,一旦有人击中 MyComponent ,我取了孩子们的路线,并用它做了一个导航栏。有没有可能有某种指令或某种方式来利用 AdminGuard /spec 如果 管理员 返回false?因为我的一些/更多的守卫需要某种对服务器或其他服务依赖性的异步调用,所以我不能简单地调用 guard.canActivate *ngIf 或者别的什么。

    我很确定它不存在,但是看起来需要这样的设置:

    <a [routerLink]="child.path" [canActivate]="child.guards">{{child.name}}</a>
    

    更新 最后我在angular repo上打开了一个GitHub特性请求。此功能似乎不存在(以开箱即用的方式)。在找到更好的解决方案之前,我将创建一个自定义指令,该指令将在警卫中运行逻辑,以评估是否应该公开某些内容。

    https://github.com/angular/angular/issues/25342

    2 回复  |  直到 7 年前
        1
  •  0
  •   Krishna    7 年前

    const user = await this._meService.getCurrentUser();
    if (user && user.isUserAdminForCompany) {
      return true;
    } else {
      return false;
    }
    

    那会解决你的问题。只是在方法上稍有改变这将隐藏路由器链接本身

        2
  •  0
  •   mwilson    7 年前

    这就是我最终的选择。由于没有任何“开箱即用”的方法来利用警卫做我想做的事情,我只是做了一个自定义指令。

    关于这个解决方案,我要注意的一点是,我讨厌以下两件事(我最终会改变)。

    1. 如果你的守卫有任何做重定向的东西,你必须改变它,这样守卫只返回true/false。如果它重定向了Guard failure上的页面,那么该指令最终将重定向您,而不是隐藏元素

    2. this._elementRef.nativeElement.style.display = hasAccess ? 'block' : 'none'; 有一个更好的解决办法,而不是仅仅做一个简单的隐藏。它应该表现得像 *ngIf 它甚至根本不呈现元素,除非它的计算结果为true。

    实施:

    <div appGuard [guards]="myGuardsArray">Something you want to hide .... </div>
    

    指令:

    import { Directive, ElementRef, Injector, Input, OnInit } from '@angular/core';
    import { ActivatedRoute, Router } from '@angular/router';
    
    @Directive({
      selector: '[appGuard]'
    })
    export class GuardDirective implements OnInit {
      @Input() guards: any[];
      private readonly _elementRef: ElementRef;
      private readonly _activatedRoute: ActivatedRoute;
      private readonly _router: Router;
      private readonly _injector: Injector;
      constructor(_elementRef: ElementRef, _activatedRoute: ActivatedRoute, _router: Router,
                  _injector: Injector) {
        this._elementRef = _elementRef;
        this._activatedRoute = _activatedRoute;
        this._router = _router;
        this._injector = _injector;
      }
      async ngOnInit(): Promise<void> {
        const canActivateInstances = this.guards.map( g => this._injector.get(g));
        const results = await Promise.all(canActivateInstances.map( ca => ca.canActivate(this._activatedRoute.snapshot, this._router.routerState.snapshot)));
        const hasAccess = results.find( r => !r) === false ? false : true;
        this._elementRef.nativeElement.style.display = hasAccess ? 'block' : 'none';
      }
    }
    

    更新

    一个简单的解决方案来确定如何处理重定向:

    async canActivate(
        next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
        const user = await this._meService.getCurrentUser();
        const result = user && user.isUserAdminForCompany;
        if (next.routeConfig && next.routeConfig.canActivate.find( r => r.name === 'NameOfGuard') && !result) {
      window.location.href = `${environment.webRoot}/sign-in`;
    }
        return result;
      }
    
    推荐文章