You need to enable JavaScript to run this app. Angular动态表单行复选框单独绑定及多类控制台报错问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

Angular动态表单行复选框单独绑定及多类控制台报错问题咨询

问题修复方案

1 问题原因梳理

  • Error: Cannot find control / ERROR TypeError: Cannot read property 'valueChanges' of null:你在ngOnInit中直接从根rowForm获取checkbox2、input3控件,但这两个控件实际存在于rows FormArray下的每一行子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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 15:30:00