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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 03:50:25