Angular结构指令多输入:首加载时第二个参数值无法在第一个Input中获取
Angular结构指令多参数读取问题
问题详情
我创建了一个支持多参数的Angular结构指令,目前遇到问题:第二个参数的值无法在accessControlRole这个@Input中读取,但该值在自身的@Input(accessControlRolePermission)中是存在的。请问这是什么原因,以及如何在accessControlRole中获取该参数值?
指令代码
@Directive({ selector: '[accessControlRole]' }) export class AccessControlRoleDirective implements OnInit { userProfile!: UserProfile; private _accessControlRolePermission!:string; constructor( private templateRef: TemplateRef<any>, private viewContainerRef: ViewContainerRef, private jwtAuthService: JwtAuthService, ) { } ngOnInit(): void { } @Input() set accessControlRole(roleAllowed: Permission[]) { this.jwtAuthService.user.subscribe(user => { if (user&&this._accessControlRolePermission) { this.userProfile = user; if(this._accessControlRolePermission === 'Approve'){ console.log('yesssssssssssssssssssssss'); this.viewContainerRef.createEmbeddedView(this.templateRef); } } else{ console.log("_accessControlRolePermission value is empty"); } }); } @Input() set accessControlRolePermission(permission:string) { this._accessControlRolePermission = permission; console.log('accessControlRolePermission value::::',this._accessControlRolePermission); } }
组件调用方式
<ng-container *accessControlRole="this.userPermissionRoles;permission:'Approve'"> <a class="btn btn-lg btn-success btn-block">Approve</a> </ng-container>
控制台输出
_accessControlRolePermission value is emptyaccessControlRolePermission value:::: Approve
原因分析
Angular对结构指令多参数输入属性的赋值顺序没有固定保证,你的代码中accessControlRole的setter可能先于accessControlRolePermission的setter执行,导致此时_accessControlRolePermission还未被赋值,所以会输出空值提示。
另外,accessControlRole的setter中直接订阅user流却未处理订阅清理,会导致重复订阅和内存泄漏问题。
解决方案
方案1:合并参数为对象传递
将多个参数合并成一个对象,确保所有参数同时被接收:
修改指令输入:
@Input() set accessControlRole(options: { roles: Permission[], permission: string }) { this.jwtAuthService.user.subscribe(user => { if (user && options.permission) { this.userProfile = user; if(options.permission === 'Approve'){ console.log('yesssssssssssssssssssssss'); this.viewContainerRef.createEmbeddedView(this.templateRef); } else { this.viewContainerRef.clear(); } } else { console.log("_accessControlRolePermission value is empty"); this.viewContainerRef.clear(); } }); }
组件调用修改为:
<ng-container *accessControlRole="{ roles: this.userPermissionRoles, permission: 'Approve' }"> <a class="btn btn-lg btn-success btn-block">Approve</a> </ng-container>
方案2:通过ngOnChanges等待所有参数就绪
利用ngOnChanges生命周期钩子,监听所有输入属性变化,待必要参数都赋值完成后再执行逻辑,同时处理订阅清理:
修改指令代码:
import { Directive, TemplateRef, ViewContainerRef, Input, OnChanges, SimpleChanges, OnDestroy } from '@angular/core'; import { Subscription } from 'rxjs'; @Directive({ selector: '[accessControlRole]' }) export class AccessControlRoleDirective implements OnChanges, OnDestroy { userProfile!: UserProfile; @Input() accessControlRole!: Permission[]; @Input() accessControlRolePermission!: string; private userSubscription?: Subscription; constructor( private templateRef: TemplateRef<any>, private viewContainerRef: ViewContainerRef, private jwtAuthService: JwtAuthService, ) { } ngOnChanges(changes: SimpleChanges): void { // 检查所有必要参数是否已赋值 if (this.accessControlRole && this.accessControlRolePermission) { this.checkPermission(); } } ngOnDestroy(): void { // 清理订阅,防止内存泄漏 this.userSubscription?.unsubscribe(); } private checkPermission(): void { // 先清理旧订阅 this.userSubscription?.unsubscribe(); this.userSubscription = this.jwtAuthService.user.subscribe(user => { if (user && this.accessControlRolePermission) { this.userProfile = user; if(this.accessControlRolePermission === 'Approve'){ console.log('yesssssssssssssssssssssss'); this.viewContainerRef.createEmbeddedView(this.templateRef); } else { this.viewContainerRef.clear(); } } else { console.log("_accessControlRolePermission value is empty"); this.viewContainerRef.clear(); } }); } }
组件调用保持原有方式即可:
<ng-container *accessControlRole="this.userPermissionRoles;permission:'Approve'"> <a class="btn btn-lg btn-success btn-block">Approve</a> </ng-container>
方案说明
- 方案1简洁直观,适合参数较少的场景;
- 方案2更灵活,支持单独监听每个参数的变化,同时解决了RxJS订阅泄漏的问题;
- 无论选择哪种方案,都必须在组件销毁时清理RxJS订阅,避免内存泄漏。
内容的提问来源于stack exchange,提问作者ZAJ
相关产品推荐
相关产品推荐

