【问题标题】:Hide a routerLink if its associated route cannot be activated?如果无法激活关联的路由,隐藏 routerLink?
【发布时间】:2016-08-16 13:20:14
【问题描述】:

我有:

  • 与 routerLinks 的链接
  • 指向带有 canActivate 逻辑的路由
  • canActivateLogic根据目标组件和用户权限授权与否

我想隐藏无法激活的带有 routerLinks 的链接。

有什么好方法可以隐藏无法激活的 routerLink 吗?

现在你会说做类似“分享 canActivate 逻辑”之类的事情:

<a [hidden]="callThatCanActivateLogic()" [routerLink]="['/brand']"

问题是这个 canActivateLogic 依赖于组件和权限,这将被错误地设计为以下内容,因为组件解析应该委托给路由,而不是嵌入到链接中:

<a [hidden]="callThatCanActivateLogic(TheComponentAtTheEndTargettedByTheRoute, IAmInThatContext)" [routerLink]="['/brand']"

如果有一个解决方案可以在 routerLink 和它的路由 canActivate 之间提供链接,那将是幸运的 :)

【问题讨论】:

    标签: angular


    【解决方案1】:

    今天我遇到了同样的问题。不幸的是,我没有找到任何好的解决方案,所以我不得不自己创建一个。

    这个想法是创建一个指令,它将路由器路径作为参数,如果不允许转换,则隐藏链接。例如:

    <li [allowTransition]="'list'">
          <a routerLink="list">List</a>
    </li>
    

    指令的逻辑如下:

    1) 导入所有路由器的配置并从属性中找到输入路径的配置。我的配置如下:

      { path: 'login', component: LoginComponent },
      { path: 'list', component: ListComponent, canActivate: [UserGuardService]},
    

    2) 然后该指令读取配置的 canActivate 属性并通过注入器获取 UserGuardService。通过这种方式,指令采用 UserGuardService 的实例。 UserGuardService 是实现 CanActivate 接口的类。

    3)UserGuardService 知道是否允许转换。所以我们应该通过更改 css 显示属性来询问它并处理答案。

    但是有两个特性对实现有很大影响:

    A) CanActivate 接口不舒服,因为要调用它,我们必须创建 ActivatedRouteSnapshot 和 RouterStateSnapshot 对象。真正的过渡可能是必要的,但万一我们只想知道过渡是否可能。

    B) 我们应该在某些事件之后提供链接可见性刷新(在我的例子中是登录事件)

    解决问题 A 我已经创建了接口 Guard。

    export interface Guard {
        allowTransition():boolean;
    }
    

    所以,我希望我的所有 CanActivate 实现也实现 Guard 接口。现在我的典型 CanActivate 实现如下所示:

    canActivate(route:ActivatedRouteSnapshot,
              state:RouterStateSnapshot):Observable<boolean>|boolean {
      //this method performs only "naigation" logic. It calls another method for business logic.
      var allow = this.allowTransition();
      if (!allow) {
          this.router.navigate(['login']);
      }
      return allow;
    }
    
    allowTransition():boolean {
      // this method contains business logic of transition possibility 
      return this.userService.hasCurrentUserRole("USER");
    }
    

    在这样的“接口划分”之后,我可以从我的指令中调用简单的方法 allowTransition()。代码如下。

    import {routes} from "../../../app.routes";
    ... other imports
    @Directive({selector: '[allowTransition]'})
      export class AllowTransition implements OnDestroy, LoginListener, OnInit {
    
      private el: HTMLElement;
    
      private visibleDisplay:string;
    
      @Input('allowTransition') destUrl: string;
    
      constructor(el: ElementRef, private injector: Injector,
              @Inject('appUserService') private userService : UserService ) {
        this.el = el.nativeElement;
        this.visibleDisplay = this.el.style.display;
      }
    
      ngOnDestroy() {
        this.userService.removeLoginListener(this)
      }
    
    
      ngOnInit() {
        this.userService.addLoginListener(this);
        this.onLogin();
      }
    
      onLogin() {
        let allow = true;
    
        for (let i = 0; i < routes.length; i++) {
          let path = routes[i].path;
          if (path == this.destUrl) {
            let canActivate = routes[i].canActivate;
            if (canActivate != null) {
              //todo do the same for canActivate[i]
              try {
                let canActivateInstance = this.injector.get(canActivate[0]) as Guard;
                allow = canActivateInstance.allowTransition();
              } catch (error) {
                //did you forget to implement Guard?
                console.error('Error');
              }
            }
          }
        }
        this.el.style.display = allow ? this.visibleDisplay : 'none';
      }
    }
    

    我已经提到了上面的问题 B,这就是为什么我的指令使用一种方法 onLogin() 实现 LoginListener 接口的原因。虽然指令存在,但它应该在事件生产者处注册并在发生变化时进行处理。在我的情况下,登录或注销后 UserService 调用所有指令的方法 onLogin() 并且每个链接都会刷新。

    初始显示属性也可以有多个值,这就是指令必须将其保留为 'visibleDisplay' 属性的原因。

    作为结论,现在可以在没有任何与路由对应的组件信息的情况下使用该指令。这种方法的主要缺点是它不适用于像 'view/:id' 这样的动态路由。在 CanActivate 方法中也不允许使用 Observale 代替布尔值。当然,可以修复它,但在我看来,我这样做的方式对于这么简单的事情来说已经太复杂了。我刚刚完成了所有我需要的工作,并等待具有类似功能的 angular2 更新。

    更多信息:https://kosbr.github.io/2016/10/18/angular2-guard-router.html

    【讨论】:

    • 第一。 'view/:id' 问题,我认为解决方案是使用npmjs.com/package/route-matcher 处理一些路由匹配,而不是简单的“等于”,您可以检查 url 是否与模板匹配。
    • 第二。 &lt;li [allowTransition]="'list'"&gt;&lt;a [routerLink]="list"&gt;List&lt;/a&gt; 在我的场景中,通过的双 list 似乎是多余的。我现在要做的是使用您的解决方案与此设置&lt;li&gt;&lt;a [allowTransition] [routerLink]="list"&gt;List&lt;/a&gt;(allowTransition 使用传递给 routerLink 的值)
    猜你喜欢
    • 2020-08-12
    • 1970-01-01
    • 1970-01-01
    • 2023-01-11
    • 2022-08-17
    • 1970-01-01
    • 2019-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多