Angular 5组件调用授权服务冗余问题求最优解决方案
解决Angular 5中权限校验代码冗余的最优方案
这个重复代码的问题太常见了,在Angular 5里我们有几个非常优雅的解决方案,我来给你拆解一下,你可以根据自己的业务场景选最合适的:
方案1:抽象出基础组件(Base Component)
这是最直接的代码复用方式,把所有组件都需要的权限校验逻辑抽离到一个基类里,其他业务组件直接继承它即可。
首先创建基础组件类:
import { Component, OnInit } from '@angular/core'; import { AuthService } from './auth.service'; import { Grants } from './grants.model'; // 定义抽象基类,封装通用权限逻辑 export abstract class BaseAuthComponent implements OnInit { protected userAccess: Grants; constructor(protected authService: AuthService) {} ngOnInit() { this.authService.grants().subscribe(grants => { this.userAccess = grants; // 调用子类实现的具体业务逻辑 this.showComponents(grants); }); } // 抽象方法,强制子类实现自己的组件显示逻辑 protected abstract showComponents(grants: Grants): void; }
然后业务组件只需要继承这个基类,实现自己的showComponents方法:
import { Component } from '@angular/core'; import { BaseAuthComponent } from './base-auth.component'; import { AuthService } from './auth.service'; import { Grants } from './grants.model'; @Component({ selector: 'app-dashboard', templateUrl: './dashboard.component.html' }) export class DashboardComponent extends BaseAuthComponent { // 只需要传递AuthService给父类构造函数 constructor(authService: AuthService) { super(authService); } // 实现当前组件特有的权限渲染逻辑 protected showComponents(grants: Grants): void { if (grants.dashboardAccess) { // 这里写仪表盘组件的显示逻辑 } } }
优点:一次性抽离所有重复逻辑,业务组件只需要关注自身业务;
注意:Angular不支持多继承,如果你的组件已经需要继承其他类,这个方案就不太适用了。
方案2:使用路由守卫(Route Guard)
如果你的权限校验是页面级别的(进入页面之前就要判断权限),路由守卫是更合适的选择,它能把权限控制统一放到路由层面,彻底消除组件里的重复代码。
首先创建权限守卫:
import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; import { AuthService } from './auth.service'; import { Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; @Injectable() export class AuthGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) {} canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean> { return this.authService.grants().pipe( map(grants => { // 把权限信息存在AuthService里,方便后续组件获取 this.authService.setCurrentGrants(grants); // 从路由配置的data中获取当前页面需要的权限标识 const requiredGrant = next.data.requiredGrant; return grants[requiredGrant]; }), tap(hasAccess => { // 无权限时统一跳转到未授权页面 if (!hasAccess) { this.router.navigate(['/unauthorized']); } }) ); } }
然后在路由配置中添加守卫:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { DashboardComponent } from './dashboard.component'; import { AuthGuard } from './auth.guard'; const routes: Routes = [ { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard], // 配置当前页面需要的权限标识 data: { requiredGrant: 'dashboardAccess' } } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}
最后组件里直接从AuthService获取已缓存的权限信息:
import { Component, OnInit } from '@angular/core'; import { AuthService } from './auth.service'; import { Grants } from './grants.model'; @Component({ selector: 'app-dashboard', templateUrl: './dashboard.component.html' }) export class DashboardComponent implements OnInit { userAccess: Grants; constructor(private authService: AuthService) {} ngOnInit() { // 直接获取路由守卫中已经缓存的权限数据 this.userAccess = this.authService.getCurrentGrants(); this.showComponents(this.userAccess); } private showComponents(grants: Grants): void { // 组件特有的显示逻辑 } }
优点:权限校验统一在路由层面处理,组件无需重复发起请求;可以统一处理无权限跳转,适合页面级权限控制。
方案3:自定义权限指令(Custom Directive)
如果你的需求是细粒度控制组件内的元素显示/隐藏,自定义指令会让模板代码更简洁,彻底摆脱组件里的showComponents方法。
创建权限控制指令:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; import { AuthService } from './auth.service'; import { Grants } from './grants.model'; @Directive({ selector: '[appShowIfGranted]' }) export class ShowIfGrantedDirective { private currentGrant: string; private grants: Grants; constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef, private authService: AuthService ) { // 订阅权限数据,保存到指令内部 this.authService.grants().subscribe(grants => { this.grants = grants; this.updateElementVisibility(); }); } // 通过输入属性接收需要校验的权限标识 @Input() set appShowIfGranted(grant: string) { this.currentGrant = grant; this.updateElementVisibility(); } // 根据权限控制元素的显示/隐藏 private updateElementVisibility(): void { if (this.grants && this.grants[this.currentGrant]) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } } }
然后在组件模板中直接使用:
<!-- 只有当用户拥有dashboardAccess权限时,这个div才会显示 --> <div *appShowIfGranted="'dashboardAccess'"> 仪表盘专属内容 </div> <button *appShowIfGranted="'canEdit'">编辑按钮</button>
优点:把权限控制逻辑下沉到模板层面,组件无需编写任何权限相关的业务代码,非常适合细粒度的元素权限控制。
内容的提问来源于stack exchange,提问作者Imad El Hitti
相关产品推荐
相关产品推荐

