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
相关产品推荐
相关产品推荐

