Angular中无关组件间接收变更的声明式实现方案问询
问题描述
- 父组件Dashboard包含Sidebar和Navbar两个子组件
- Sidebar的链接上使用了自定义权限指令,用于校验当前scope的访问权限
- Navbar提供Admin、Manager、Staff三种角色切换菜单,希望切换角色后,Sidebar的链接能根据更新后的scope权限自动调整
尝试过的方案无效:
- 在父组件中声明Observable:
scope$: Observable<Roles> = this.scopeService.scope$; - 通过async管道传递给Sidebar:
<app-sidebar [scope]="scope$ | async"></app-sidebar> - 在父组件ngOnInit中手动订阅
scope$
希望采用声明式实现,不想用EventEmitter。
解决方案
方案1:让权限指令直接监听scope变化(推荐)
修改自定义权限指令,直接注入ScopeService并订阅scope的变化,每次scope更新时自动重新校验权限,无需通过组件传递数据,更符合声明式模式。
import { Directive, Input, OnInit, OnDestroy, ElementRef, Renderer2 } from '@angular/core'; import { ScopeService } from '../services/scope.service'; import { Subscription } from 'rxjs'; @Directive({ selector: '[hasPermission]' }) export class HasPermissionDirective implements OnInit, OnDestroy { @Input() hasPermission: string[]; private scopeSub: Subscription; constructor( private el: ElementRef, private renderer: Renderer2, private scopeService: ScopeService ) {} ngOnInit(): void { // 订阅scope变化,每次更新都重新校验权限 this.scopeSub = this.scopeService.scope$.subscribe(currentScope => { this.toggleElementVisibility(currentScope); }); } private toggleElementVisibility(scope: any): void { // 校验当前角色是否拥有指定权限 const hasAccess = this.hasPermission.some(perm => scope?.permissions?.includes(perm)); // 控制元素显示/隐藏 this.renderer.setStyle(this.el.nativeElement, 'display', hasAccess ? 'block' : 'none'); } ngOnDestroy(): void { // 销毁时取消订阅,避免内存泄漏 this.scopeSub?.unsubscribe(); } }
方案2:优化组件间的scope传递(如果需要保留组件输入的方式)
如果坚持通过父组件向Sidebar传递scope,需要确保Sidebar组件能响应输入的变化,同时指令也能监听输入的scope更新:
父组件简化代码:
移除ngOnInit中的手动订阅,async管道会自动处理订阅和取消订阅:scope$: Observable<Roles> = this.scopeService.scope$;模板保持不变:
<app-sidebar [scope]="scope$ | async"></app-sidebar>Sidebar组件监听输入变化:
在Sidebar组件中使用ngOnChanges钩子,当输入的scope更新时,触发权限重新校验:import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { Roles } from '../../models/roles.model'; @Component({ selector: 'app-sidebar', templateUrl: './sidebar.component.html' }) export class SidebarComponent implements OnChanges { @Input() scope: Roles; ngOnChanges(changes: SimpleChanges): void { if (changes['scope'] && changes['scope'].currentValue) { // 此处可触发权限指令的重新校验逻辑 } } }指令监听输入的scope变化:
修改权限指令,添加@Input() scope,并通过ngOnChanges监听变化:import { Directive, Input, OnChanges, SimpleChanges, ElementRef, Renderer2 } from '@angular/core'; @Directive({ selector: '[hasPermission]' }) export class HasPermissionDirective implements OnChanges { @Input() hasPermission: string[]; @Input() scope: any; constructor(private el: ElementRef, private renderer: Renderer2) {} ngOnChanges(changes: SimpleChanges): void { if (changes['scope'] || changes['hasPermission']) { this.toggleElementVisibility(this.scope); } } private toggleElementVisibility(scope: any): void { const hasAccess = this.hasPermission.some(perm => scope?.permissions?.includes(perm)); this.renderer.setStyle(this.el.nativeElement, 'display', hasAccess ? 'block' : 'none'); } }同时在Sidebar模板中给指令绑定scope:
<a *ngFor="let item of menuItems" [hasPermission]="item.permissions" [scope]="scope">{{ item.label }}</a>
关键前提:确保ScopeService的Observable是热Observable
必须使用BehaviorSubject或ReplaySubject来存储scope,这样新的订阅者能立即获取当前最新值:
import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { Roles } from '../models/roles.model'; @Injectable({ providedIn: 'root' }) export class ScopeService { private readonly _scope$ = new BehaviorSubject<Roles>(null); // 对外暴露只读的Observable scope$: Observable<Roles> = this._scope$.asObservable(); updateScope(newScope: Roles): void { this._scope$.next(newScope); } }
内容的提问来源于stack exchange,提问作者Drake Mohit
相关产品推荐
相关产品推荐

