Angular动态表单行复选框单独绑定及多类控制台报错问题咨询
问题修复方案
1 问题原因梳理
Error: Cannot find control/ERROR TypeError: Cannot read property 'valueChanges' of null:你在ngOnInit中直接从根rowForm获取checkbox2、input3控件,但这两个控件实际存在于rowsFormArray下的每一行子FormGroup中,根Form不存在对应控件,因此取值为null,调用方法时自然报错。Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError:你通过setTimeout异步给rowForm添加rows控件,异步操作在Angular首轮变更检测完成后触发了视图更新,导致检测前后表达式值不一致触发报错。- 复选框作用于所有行:你使用了全局的
showInput变量控制所有行的input3显示,没有和行索引绑定,只要值变化所有行的判断条件都会同步变更。
2 修复后代码
TS文件代码
import { Component, Input, OnInit, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, FormArray, FormControl, Validators, ControlContainer } from '@angular/forms'; import { Subscription } from 'rxjs'; export class RowComponent implements OnInit, OnDestroy { @Input() rowForm: FormGroup; constructor(private formBuilder: FormBuilder,private parentControl: ControlContainer) {} ngOnInit() { // 去掉setTimeout异步操作,同步添加rows控件避免变更检测报错 this.rowForm.addControl("rows",this.formBuilder.array([this.createRow(true)])) } ngOnDestroy(): void { } createRow(disabled:boolean=false): FormGroup { const rowGroup = this.formBuilder.group({ input1: [{value:'',disabled:disabled}, Validators.required], input2: [{value:'',disabled:disabled}, Validators.required], input3: [{value:'',disabled:disabled}], checkbox2: [{value:false,disabled:disabled}], // 初始值设为布尔值,适配复选框取值逻辑 }); // 订阅当前行内部的复选框状态,每行单独管理自己的校验规则,互不干扰 const checkboxControl = rowGroup.get('checkbox2') as FormControl; const input3Control = rowGroup.get('input3') as FormControl; const sub = checkboxControl.valueChanges.subscribe(value => { if (value) { input3Control.setValidators([Validators.required]) } else { input3Control.clearValidators(); } input3Control.updateValueAndValidity({emitEvent: false}); }); // 行删除时自动销毁订阅避免内存泄漏 rowGroup.valueChanges.subscribe({ complete: () => sub.unsubscribe() }) return rowGroup; } get formArray() { return this.rowForm.get('rows') as FormArray; } addNewRow() { this.formArray.push(this.createRow()); } deleteRow(index: number) { this.formArray.removeAt(index); } getControls() { return this.formArray.controls as FormGroup[]; } }
HTML文件代码
<form [formGroup]="rowForm"> <div formArrayName="rows"> <div *ngFor="let rowGroup of getControls(); let i=index" [formGroup]="rowGroup"> <mat-checkbox class="example-margin" formControlName="checkbox2">Show hidden option!</mat-checkbox> <mat-form-field class="example-full-width"> <input matInput placeholder="Input1" formControlName="input1" required> </mat-form-field> <mat-form-field class="example-full-width"> <input matInput placeholder="Input2" formControlName="input2" required> </mat-form-field> <!-- 直接读取当前行的复选框状态控制显示,天然和行绑定,不会影响其他行 --> <mat-form-field *ngIf="rowGroup.get('checkbox2').value" class="example-full-width"> <input matInput placeholder="Input3" formControlName="input3" required> <mat-error>Title is required</mat-error> </mat-form-field> <button mat-button color="primary" *ngIf="getControls().length > 1" (click)="deleteRow(i)" class="btn btn-danger">Delete Button</button> </div> </div> <button mat-button color="primary" (click)="addNewRow()" class="btn btn-primary">Add new Row</button><br> </form>
3 修改说明
- 移除了全局的
showInput变量,直接读取每行FormGroup内的checkbox2值控制对应行input3的显示,天然和行索引绑定,不会互相干扰 - 把复选框状态订阅移到
createRow方法内,每个新创建的行单独管理自己的控件状态和校验规则,互不影响 - 去掉了异步添加
rows控件的setTimeout,同步操作避免变更检测报错 - 修复了控件获取路径错误的问题,不会再出现找不到控件、调用
valueChanges为null的报错 - 调整了复选框的初始值为布尔值
false,更符合复选框的取值逻辑
内容的提问来源于stack exchange,提问作者user14910244
相关产品推荐
相关产品推荐

