【问题标题】:Best way to show/hide a link in angular 5以 Angular 5 显示/隐藏链接的最佳方式
【发布时间】:2018-04-19 00:27:56
【问题描述】:

我目前有一个主页,可以路由到我公司运行的所有不同工作流程。我们有大约 15 个不同的工作流程,每个工作流程都由一个用户角色保护。如果您在数据库中没有正确的用户角色,您将看不到指向该页面的相应链接。我们保护服务器端点,但我担心的是向人们展示链接的最佳方式是什么,或者不向他们展示我不想重复代码。

这是一种方法:

我有一个这样的html页面

<ul>
  <li *ngIf="authService.hasRequiredRole('users.user-admin')" routerLink="/user">Users</li>
  <li *ngIf="authService.hasRequiredRole('users.role-admin')" routerLink="/role">Roles</li>
</ul>

我有这样的身份验证服务:

hasRequiredRole(roles: string | [string]) {
    if (typeof roles === 'string') {
      roles = [roles];
    }
    for (const roleSlug of roles) {
      if (this.user.roles.find((role: any) => {
          return role.slug === roleSlug;
      })) {
        return true;
      }
    }
    return false;
  }

我有一个路由如下的路由器:

const routes: Routes = [{

  path: 'user',
  data: {
    allowedRoles: 'users.user-admin'
  },
  loadChildren: 'app/user/user.module#UserModule',
  canActivate: [AuthGuard]
},
{ path: 'home', component: HomeComponent }]

AuthGuard 只是检查用户是否登录,然后使用路由中的数据检查用户是否具有正确的角色。

如您所见,我们在两个不同的位置使用字符串“users.user-admin”。


我认为我们应该有一个带有两个保护装置的路由器。一名守卫将检查用户是否已登录,另一名守卫将检查用户是否具有正确的角色。该角色将在此处进行硬编码,如下所示:

export class UserAdminGuard implements CanActivate {
  constructor(private router: Router, private authService: AuthService) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable < boolean > | Promise < boolean > | boolean {

    return this.authService.hasRequiredRole('users.user-admin');
  }
}

然后 html 看起来像这样:

<ul>
  <li *ngIf="userAdminGuard.canActivate()" routerLink="/user">Users</li>
  <li *ngIf="roleAdminGuard.canActivate()" routerLink="/role">Roles</li>
</ul>

像我的方法这样的方法会起作用吗,还是有更好的更角度的方法来做到这一点?我在文档中找不到任何关于此类的内容。如果您还可以提供文档加分。

【问题讨论】:

  • 顺便说一句,你真的是指 tuple [string](一个长度正好为 1 的 Array,包含一个 string)或者更确切地说是 string[]Array&lt;string&gt;?我问是因为,虽然完全允许,但我从未在生产 Angular 中看到它。
  • 我的意思是它可以是字符串数组或字符串,因此您可以提供 ['role1','role2'] 或 'role'
  • 好的,TypeScript 中的字符串数组被声明为string[]Array&lt;string&gt;。另一方面,[string] 是元组类型,这意味着您声明的唯一有效类型是与您定义的格式完全相同 的数组。在这种情况下,长度为 1 的数组带有 1 个字符串(请参阅原始评论中的链接)。同样,您可以声明 x: [string, number, number] 之类的内容,其中 ["a", 1, 2] 有效,但 ["a", "b", "c"] 无效。

标签: angular angular-template angular-router angular-router-guards


【解决方案1】:

我认为这样做的一种方法是创建一个结构指令。您可以创建一个 RolesDirective,它根据您传递的角色处理显示/隐藏内容的逻辑。

例如:

你的指令看起来像这样

@Directive({
  selector: '[roles]',
  inputs: ['roles']
})
export class RolesDirective {

  constructor(private _templateRef: TemplateRef<any>,
              private _viewContainer: ViewContainerRef,
              private userService: UserService) {

  }

  @Input() set roles(allowedRoles: Array<string>) {
    let shouldShow: boolean = false;
    let userRoles:Array<string> = this.userService.getUserRoles();
    for(let role of userRoles){
      if(role.toUpperCase() == "ADMIN"){
        shouldShow = true;
        break;
      }
      for(let allowedRole of allowedRoles){
        allowedRole = allowedRole.toUpperCase();
        if(allowedRole.toUpperCase() == role.toUpperCase()){
          shouldShow = true;
          break;
        }
      }
    }
    if (shouldShow) {
      this._viewContainer.createEmbeddedView(this._templateRef);
    } else {
      this._viewContainer.clear();
    }
  }

}

在您的 html 中,只需传递该组件的允许角色

<ul>
  <li *roles="['admin']" routerLink="/user">Users</li>
  <li *roles="['other','roles']" routerLink="/role">Roles</li>
</ul>

这是结构指令的文档:https://angular.io/guide/structural-directives

【讨论】:

  • 非常好的解决方案!
  • 如何处理路由保护?您会在主要身份验证守卫中使用该指令吗?您将如何将角色传递给路线守卫?你会一起摆脱路线守卫吗?
  • @SamuelThompson 我有一个和你一样的 AdminAuthGuard,它只是检查用户是否是管理员。我将它用于管理员专用的路由以及我想要隐藏页面的某些内容的其他页面,我使用该指令。如果您想让页面对管理员独占,请使用 auth 保护;如果您想控制页面中组件的可见性,请使用角色指令。
  • @msanford 很高兴你喜欢它,我也很好奇是否有更好或优化的方法来做到这一点。不过,指令非常糟糕。
  • @SamuelThompson 将角色作为数据属性附加到路由并在 Guard 中检索该角色并应用相同的逻辑似乎是目前的方法。此答案中的示例:stackoverflow.com/a/42721468/3778953
猜你喜欢
  • 2015-10-21
  • 2011-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-02
  • 2012-07-30
  • 1970-01-01
  • 2015-07-27
相关资源
最近更新 更多