如何为动态创建的Angular组件实例添加formControlName指令?
解决方案说明
首先明确前提:Angular的指令是编译阶段静态解析的,不存在运行时调用addDirective这类API给已经实例化的组件动态挂载指令的能力,你之前的思路走不通,换以下两种可落地的方案即可实现需求。
方案1:通过Input传递FormControl实例手动绑定(最简单,推荐)
你自定义组件已经实现了ControlValueAccessor,本身就支持直接绑定FormControl,完全不用依赖formControlName指令。
第一步:修改指令新增输入参数
import { Directive, Input, OnInit, Type, ViewContainerRef, ComponentFactoryResolver } from '@angular/core'; import { FormGroup, ControlValueAccessor, setUpControl } from '@angular/forms'; Directive({ selector: '[customComponentDirective]', }) export class ComponentDirective implements OnInit { @Input() customComponentDirective: Type<any>; // 新增两个输入参数,接收父表单和控件名 @Input() controlName: string; @Input() parentForm: FormGroup; constructor(private _viewContainerRef: ViewContainerRef, private _componentFactoryResolver: ComponentFactoryResolver){} ngOnInit() { const componentFactory = this._componentFactoryResolver.resolveComponentFactory(this.customComponentDirective); const componentRef = this._viewContainerRef.createComponent(componentFactory); // 拿到对应FormControl实例 const targetControl = this.parentForm.get(this.controlName); // 用Angular表单内置方法关联控件和组件,等效于formControlName的内部实现逻辑 setUpControl(targetControl, componentRef.instance as ControlValueAccessor); } }
第二步:修改模板传参
<form [formGroup]="formGroup"> <ng-container *ngFor="let singleForm of formConfigs;"> <ng-container [ngSwitch]="singleForm?.type"> <ng-container *ngSwitchCase="'input'"> <input formControlName="singleForm.controlName" > </ng-container> <ng-container *ngSwitchCase="'custom'"> <div myCustomComponentDirective [parentForm]="formGroup" [controlName]="singleForm.controlName" ></div> </ng-container> </ng-container> </ng-container> </form>
这个方案不需要修改现有自定义组件的逻辑,只要确保组件已经正确实现ControlValueAccessor接口即可正常运行。
方案2:把绑定逻辑移到自定义组件内部
如果你不想修改指令逻辑,也可以把formControlName声明在自定义组件的内部模板里,外部只需要把控件名作为Input传给动态创建的组件即可,关联逻辑由组件内部完成,实现效果和方案1一致。
注意事项
- Angular从设计上不支持运行时给组件动态挂载指令,所有指令必须在模板编译前显式声明,不要浪费时间找相关黑盒API
- 你代码里的
conrolName为拼写错误,正确写法是controlName,线上运行时会导致表单匹配失败
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

