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

Angular动态增删表单组件报错:无法找到control2控件

问题:动态加载Angular Material下拉组件时提示“Cannot find control with name: 'control2'”

使用Angular Material创建了一个下拉选择框组件,静态放置时可正常工作,但通过ViewContainerRef.createComponent动态加载并传递FormGroup参数后,出现错误:ERROR Error: Cannot find control with name: 'control2'。

父组件代码

import { STEPPER_GLOBAL_OPTIONS } from "@angular/cdk/stepper";
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { FormBuilder, FormControl, FormGroup } from "@angular/forms";

@Component({
  selector: "gcr-create",
  template: `<gcr-select-form
                [formGroup]="form"
                formControlName="control1"
                otherParam="OK"
              ></gcr-select-form>
            </div>
            <ng-template #containerMoreSelect></ng-template>
              <button mat-stroked-button (click)="click()">Add More</button>`,
  styles: [``],
  providers: [
    {
      provide: STEPPER_GLOBAL_OPTIONS,
      useValue: { showError: true },
    },
  ],
})
export class ParentComponent implements OnInit {
  @ViewChild("containerMoreSelect", {
    read: ViewContainerRef,
  })
  containerMoreSelect!: ViewContainerRef;
  
  public form!: FormGroup;

  constructor() {}
  ngOnInit(): void {
    this.formInit();
  }

  formInit() {
    this.form = new FormGroup({
      control1: new FormControl(),
    });
  }

  click() {
    this.form.addControl("control2", new FormControl());
    const component = this.containerMoreSelect.createComponent(SelectReteComponent);
    component.setInput("formGroup", this.form);
    component.setInput("formControlName", "control2");
    component.setInput("otherParam", "OK");
  }
}

子组件代码

import {
  Component,
  EventEmitter,
  Input,
  OnChanges,
  Output,
  SimpleChanges,
} from "@angular/core";
import {
  AbstractControl,
  ControlContainer,
  FormGroup,
  FormGroupDirective,
} from "@angular/forms";
import { MatSelectChange } from "@angular/material/select";
import { ValueText } from "../models/value-text.model";

@Component({
  selector: "gcr-select-form",
  template: `<mat-form-field>
    <mat-label>Select what you prefer</mat-label>
    <mat-select
      [formControlName]="formControlName"
      (selectionChange)="getSelection($event)">
      <mat-option></mat-option>
      <mat-option *ngFor="let value of values" [value]="value.id">
        {{ value.text}}
      </mat-option>
    </mat-select>
  </mat-form-field>`,
  styles: ``,
  viewProviders: [
    {
      provide: ControlContainer,
      useExisting: FormGroupDirective,
    },
  ],
})
export class SelectReteComponent implements OnChanges {
  @Input() formGroup!: FormGroup;
  @Input() formControlName!: string;
  @Input() otherParam!: string;
  @Output() selected= new EventEmitter<ValueText>();
  constructor(private ValuesService: ValuesRepositoryService) {}

  /* Data are loaded from DB */

  ngOnChanges(changes: SimpleChanges) {
    /* Other stuff */
    console.log("formGroup" + this.formGroup.controls); // I can see all control, control2 included
    console.log("formControlName " + this.formControlName); // It shows control2 correctly
    console.log("otherParam" + this.otherParam); // It shows "OK"
  }

  getSelection(event: MatSelectChange) {
    console.log("event.value " + event.value);
    this.selected.emit({
      value: event.value,
      text: event.source.triggerValue,
    });
  }
}

问题原因

静态组件工作正常是因为它处于父组件的FormGroupDirective上下文(比如父组件模板中有<form [formGroup]="form">包裹),子组件通过viewProviders配置的ControlContainer能获取到该上下文。但动态组件挂载到ng-template中,没有继承这个上下文,导致formControlName无法找到对应的FormGroup,即使已经传入了formGroup输入参数。

解决方案

步骤1:移除子组件的viewProviders配置

删掉子组件元数据中的viewProviders部分,因为动态组件不需要依赖父级的FormGroupDirective:

@Component({
  selector: "gcr-select-form",
  template: `...`,
  styles: ``,
  // 移除以下配置
  // viewProviders: [
  //   {
  //     provide: ControlContainer,
  //     useExisting: FormGroupDirective,
  //   },
  // ],
})

步骤2:显式绑定FormGroup到子组件模板

修改子组件的模板,将mat-form-field用[formGroup]="formGroup"包裹,确保formControlName能关联到传入的FormGroup:

<mat-form-field [formGroup]="formGroup">
  <mat-label>Select what you prefer</mat-label>
  <mat-select
    formControlName="{{formControlName}}"
    (selectionChange)="getSelection($event)">
    <mat-option></mat-option>
    <mat-option *ngFor="let value of values" [value]="value.id">
      {{ value.text}}
    </mat-option>
  </mat-select>
</mat-form-field>

或者直接绑定formControl:

<mat-form-field>
  <mat-label>Select what you prefer</mat-label>
  <mat-select
    [formControl]="formGroup.get(formControlName)!"
    (selectionChange)="getSelection($event)">
    <mat-option></mat-option>
    <mat-option *ngFor="let value of values" [value]="value.id">
      {{ value.text}}
    </mat-option>
  </mat-select>
</mat-form-field>

修改后,动态加载的组件就能正确关联到传入的FormGroup和对应的FormControl,不会再出现找不到控件的错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 06:13:21