You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Angular中获取验证主题的组件引用?

问题描述

我需要编写一个指令绑定到组件上,以此订阅组件的验证状态变更事件,指令作为连接器使用。现有实现了IEntryValidationSubject接口的组件代码如下:

@Component({
    selector: 'app-authors',
    templateUrl: './authors.component.html',
    styleUrls: ['./authors.component.scss']
})
export class AuthorsComponent
    implements OnInit, OnDestroy, IEntryValidationSubject
{
    authorsForm: UntypedFormGroup;
    authorsFormArray: UntypedFormArray;
    subscriptions: Subscription = new Subscription();
    authorsDataSource: FirstLastNameDataSource<AuthorViewModel>;
     
    private _isValid: BehaviorSubject<boolean> = new BehaviorSubject(true);

    public $validationStatusChange: Observable<boolean> =
        this._isValid.asObservable();

    @Input() entryId: number;

    constructor(
        private fb: UntypedFormBuilder,
        public editorValidationService: EditorValidationService,
        public elementRef: ElementRef
        // ... 其他依赖
    ) {
        this.editorValidationService.register(this, 'another');
        // ... 其他初始化逻辑
    }

    public isValid(): boolean {
        const controls =
            this.authorsFormArray && this.authorsFormArray.controls;
        let isValid = true;
        if (controls) {
            // 仅验证已填写的字段
            isValid = controls
                .filter(control => this.isFilled(control.value))
                .every(author => author.valid);
        }
        this._isValid.next(isValid);
        return isValid;
    }

    // ... 其他方法
}

为判断表单有效性编写的指令代码如下,尝试通过@Host()获取宿主组件引用但未成功:

@Directive({
    selector: '[validateSubject]'
})
export class ValidationDirective implements OnInit, OnDestroy {
    @Input() validateSubject: IEntryValidationSubject;

    constructor(
        @Host() @Optional() private host: IEntryValidationSubject,
        private validationService: EditorValidationService
    ) {
    }

    // ... 生命周期方法
}
解决方案

核心原因

Angular依赖注入系统无法直接识别接口类型(编译后接口元数据会被擦除),且组件默认不会自动注册为自身实现接口的提供者,导致@Host()无法找到对应的实例。

具体步骤

1. 在宿主组件中显式提供接口映射

修改AuthorsComponent的@Component装饰器,添加providers配置,将IEntryValidationSubject接口指向组件自身:

@Component({
    selector: 'app-authors',
    templateUrl: './authors.component.html',
    styleUrls: ['./authors.component.scss'],
    providers: [
        { provide: IEntryValidationSubject, useExisting: AuthorsComponent }
    ]
})
export class AuthorsComponent
    implements OnInit, OnDestroy, IEntryValidationSubject
{
    // ... 原有代码保持不变
}

2. 修正指令中的注入逻辑

移除冗余的@Input()(若不需要手动传入实例),确保@Host()能正确获取宿主组件实例:

@Directive({
    selector: '[validateSubject]'
})
export class ValidationDirective implements OnInit, OnDestroy {
    private validationSubscription?: Subscription;

    constructor(
        @Host() private host: IEntryValidationSubject,
        private validationService: EditorValidationService
    ) {}

    ngOnInit(): void {
        // 订阅宿主组件的验证状态变更事件
        this.validationSubscription = this.host.$validationStatusChange.subscribe(isValid => {
            // 在此处理验证状态变更的业务逻辑
            console.log('组件验证状态变更:', isValid);
        });
    }

    ngOnDestroy(): void {
        // 取消订阅避免内存泄漏
        this.validationSubscription?.unsubscribe();
    }
}

备选方案:通过@Input()手动传入实例

若不想修改组件的提供者配置,可在模板中直接将组件实例传入指令的输入属性:

<app-authors [validateSubject]="this"></app-authors>

此时指令可保留@Input(),无需依赖注入获取实例:

@Directive({
    selector: '[validateSubject]'
})
export class ValidationDirective implements OnInit, OnDestroy {
    @Input() validateSubject!: IEntryValidationSubject;
    private validationSubscription?: Subscription;

    constructor(private validationService: EditorValidationService) {}

    ngOnInit(): void {
        this.validationSubscription = this.validateSubject.$validationStatusChange.subscribe(isValid => {
            // 处理逻辑
        });
    }

    ngOnDestroy(): void {
        this.validationSubscription?.unsubscribe();
    }
}

内容的提问来源于stack exchange,提问作者trimmm0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 00:55:56