Angular 14:动态创建依赖NgControl的组件并设置formControlName
问题
尝试通过ViewContainerRef的createComponent方法向DOM中添加依赖NgControl的组件,最终目标是用FormBuilder向表单中动态添加组件,但遇到NG0201: No provider for NgControl found in NodeInjector错误,想知道能否在动态创建组件时设置formControlName。
相关代码:
createNgControlComponent() { // Add control to form group this.controlName++; this.formGroup.addControl( this.controlName.toString(), this.formBuilder.control('', []) ); // Create component // ERROR Error: NG0201: No provider for NgControl found in NodeInjector. const componentRef = this.form.createComponent( CustomControlValueAccessorComponent ); // componentRef.instance.controlDir = new FormControlName(); }
解决方案
动态创建依赖NgControl的组件时,不能直接实例化FormControlName,需要通过Angular的注入器手动提供NgControl的提供者,具体步骤如下:
创建并配置
FormControlName实例
先为动态组件创建对应的FormControlName指令实例,注入必要依赖并绑定控件名:import { FormControlName, NgControlStatus, FormGroupDirective } from '@angular/forms'; // 在组件类中获取表单组指令(需模板中存在<form [formGroup]="formGroup">) @ViewChild(FormGroupDirective) formGroupDir!: FormGroupDirective; createNgControlComponent() { this.controlName++; const controlKey = this.controlName.toString(); // 向表单组添加控件 this.formGroup.addControl(controlKey, this.formBuilder.control('', [])); // 创建FormControlName实例并配置 const controlName = new FormControlName( this.injector.get(NgControlStatus), this.formGroupDir, this.injector ); controlName.name = controlKey; controlName.ngOnChanges({}); // 触发初始化变更检测 }为动态组件提供
NgControl注入器
调用createComponent时,传入包含NgControl的自定义注入器,解决依赖缺失问题:const componentFactory = this.componentFactoryResolver.resolveComponentFactory(CustomControlValueAccessorComponent); // 创建自定义注入器,将FormControlName作为NgControl提供 const injector = Injector.create({ providers: [{ provide: NgControl, useValue: controlName }], parent: this.form.injector }); // 创建动态组件并传入注入器 const componentRef = this.form.createComponent(componentFactory, { injector });关联表单与动态组件并处理销毁
将FormControlName注册到表单组指令中,并在组件销毁时清理资源:// 注册控件到表单组指令 this.formGroupDir.addControl(controlName); // 组件销毁时移除控件并清理表单组 componentRef.onDestroy(() => { this.formGroupDir.removeControl(controlName); this.formGroup.removeControl(controlKey); });完整整合代码
最终可运行的完整方法示例:import { Component, ViewChild, Injector, ComponentFactoryResolver, ViewContainerRef } from '@angular/core'; import { FormBuilder, FormGroup, FormControlName, NgControlStatus, FormGroupDirective, NgControl } from '@angular/forms'; import { CustomControlValueAccessorComponent } from './custom-control-value-accessor.component'; @Component({ selector: 'app-root', template: ` <form [formGroup]="formGroup"> <div #form></div> <button (click)="createNgControlComponent()">添加组件</button> </form> ` }) export class AppComponent { formGroup: FormGroup; controlName = 0; @ViewChild('form', { read: ViewContainerRef }) form!: ViewContainerRef; @ViewChild(FormGroupDirective) formGroupDir!: FormGroupDirective; constructor( private formBuilder: FormBuilder, private injector: Injector, private componentFactoryResolver: ComponentFactoryResolver ) { this.formGroup = this.formBuilder.group({}); } createNgControlComponent() { this.controlName++; const controlKey = this.controlName.toString(); // 添加控件到表单组 this.formGroup.addControl(controlKey, this.formBuilder.control('', [])); // 创建FormControlName实例 const controlName = new FormControlName( this.injector.get(NgControlStatus), this.formGroupDir, this.injector ); controlName.name = controlKey; controlName.ngOnChanges({}); // 创建自定义注入器 const injector = Injector.create({ providers: [{ provide: NgControl, useValue: controlName }], parent: this.form.injector }); // 创建动态组件 const componentFactory = this.componentFactoryResolver.resolveComponentFactory(CustomControlValueAccessorComponent); const componentRef = this.form.createComponent(componentFactory, { injector }); // 注册与销毁逻辑 this.formGroupDir.addControl(controlName); componentRef.onDestroy(() => { this.formGroupDir.removeControl(controlName); this.formGroup.removeControl(controlKey); }); } }
内容的提问来源于stack exchange,提问作者bunchOfModelos
相关产品推荐
相关产品推荐

