Angular 12 - Nest-Form 列表中的组件触发 NG0100 错误

Angular 12 - Nest-Form component in list trigger NG0100 Error

版本:Angular12.2 堆栈闪电战:https://stackblitz.com/edit/angular-ivy-r8cpbh?file=src/app/app.component.html

你好, 我有一个包含 3 个组件类型的项目:

在 Parent 级别我们可以添加 Child

在Child级别我们可以添加SubChild

一切正常,但当我添加新行时,有时(当有效字段更改时)我在 formGroup.invalid 上收到此消息:

NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value for 'disabled': 'false'. Current value: 'true'

我知道这是一个常见的错误,但是这次我没有找到pb来自哪里...

请帮忙。

Parent
   |____Child
           |______SubChild
           |______SubChild
           |______SubChild
   |____Child
           |______SubChild
   |____Child
           |______SubChild
           |______SubChild

Parent ts

 public data!: ParentData;
  public formGroup!: FormGroup;

  get children() {
    return <FormArray>this.formGroup.controls.children;
  }

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.data = {
      parentField1: 'string',
      parentField2: 'string',
      parentHiddenField1: 'string',
      children: []
    };
    this.formGroup = this.toFormGroup(this.data);

    this.formGroup.valueChanges.subscribe(value => console.log(value));
  }

  add() {
    this.children.push(new FormGroup({}));
    this.data.children.push({
      id: 1,
      childField1: 'string',
      subchildren: []
    });
  }

  private toFormGroup(data: ParentData): FormGroup {
    const formGroup = this.fb.group({
      parentField1: [data.parentField1, Validators.required],
      parentField2: [data.parentField2, Validators.required],
      parentHiddenField1: [data.parentHiddenField1],
      children: new FormArray([])
    });

    return formGroup;
  }

  submit(finalData: ParentData) {
    console.log(finalData);
  }
}

Parent Html

<form [formGroup]="formGroup" (ngSubmit)="submit(formGroup.value)">
  <label for="parentField1">Parent Field 1</label>
  <input formControlName="parentField1" />

  <br />

  <label for="parentField2">Parent Field 2</label>
  <input formControlName="parentField2" />



  <div formArrayName="children">
    <button type="button" (click)="add()">+</button>
    <div *ngFor="let child of data.children; let i=index">
      <div>Parent</div>
      <app-test-list-child [formGroup]="children.controls[i] | formGroup" [data]="child">
      </app-test-list-child>

    </div>
  </div>

  <button type="submit" [disabled]="formGroup.invalid">Send</button>
</form>

Child ts

export class TestListChildComponent implements OnInit, AfterViewInit {
  @Input('formGroup')
  public formGroup!: FormGroup;

  @Input('data')
  public data!: ChildData;

  get subchildren() {
    return <FormArray>this.formGroup.get('subchildren');
  }

  constructor(private cd: ChangeDetectorRef) {}

  ngOnInit() {
    this.formGroup.addControl(
      'childField1',
      new FormControl(this.data.childField1 || '', Validators.required)
    );
    this.formGroup.addControl('subchildren', new FormArray([]));
  }
  ngAfterViewInit() {}

  add() {
    this.subchildren.push(new FormGroup({}));
    this.data.subchildren.push({
      childField1: 'test',
      childHiddenField1: '',
      childField2: '',
      id: 1
    });
  }
}

Child html

<div [formGroup]="formGroup">
  <input formControlName="childField1" />
  <button type="button" (click)="add()">++</button>

  <div formArrayName="subchildren">

    <div *ngFor="let child of subchildren.controls; let i = index">
      <div>Child</div>

      <app-test-child [formGroup]="child | formGroup" [data]="data.subchildren[i]">
      </app-test-child>
    </div>
  </div>
</div>

子Child ts

export class TestChildComponent implements OnInit, AfterViewInit {

  @Input('formGroup')
  public formGroup!: FormGroup;

  @Input('data')
  public data!: SubChildData;

  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    this.toFormGroup(this.data);
  }

  ngAfterViewInit() {

  }

  private toFormGroup(data: SubChildData) {

    this.formGroup.addControl("childField1", new FormControl(data?.childField1 || '', Validators.required));
    this.formGroup.addControl("childField2", new FormControl(data?.childField2 || '', Validators.required));

  }
}

亚Child html

<!-- child-form.component.html -->
SubChild
<div [formGroup]="formGroup">
  <label for="childField1">Child Field 1</label>
  <input formControlName="childField1" />
  <br/>
  <label for="childField1">Child Field 2</label>
  <input formControlName="childField2" />
</div>

型号

export interface ParentData {
  parentField1: string;
  parentField2: string;
  parentHiddenField1: string;
  children: ChildData[];
}

export interface ChildData {
  id: number;
  childField1: string;
  subchildren: SubChildData[];
}

export interface SubChildData {
  id: number;
  childField1: string;
  childField2: string;
  childHiddenField1: string;
}

管道


@Pipe({
  name: 'formGroup'
})
export class FormGroupPipe implements PipeTransform {
  transform(value: AbstractControl, ...args: unknown[]): FormGroup {
    return value as FormGroup;
  }
}

错误发生是因为在 test-child-component.ts 中你有一个必需的验证器:

  private toFormGroup(data: SubChildData) {

    this.formGroup.addControl("childField1", new FormControl(data?.childField1 || '', Validators.required));
    this.formGroup.addControl("childField2", new FormControl(data?.childField2 || '', Validators.required));

  }

...但在 test-list-component.ts 中,您没有为 childField2 控件分配值,并且表单无效。

最简单的解决方法是为第二个控件分配一个默认值:

  add() {
    this.subchildren.push(new FormGroup({}));
    this.data.subchildren.push({
      childField1: 'test',
      childHiddenField1: '',
      childField2: 'default value',
      id: 1
    });
  }

我最终发现 pb 是某种异步模式...

我在创建表单时添加了 setTimeout,现在一切正常。

Child

setTimeout(() => {
 this.formGroup.addControl(
      'childField1',
      new FormControl(this.data.childField1 || '', Validators.required)
    );
    this.formGroup.addControl('subchildren', new FormArray([]));
});

亚Child

setTimeout(() => {
    this.formGroup.addControl("childField1", new FormControl(data?.childField1 || '', Validators.required));
    this.formGroup.addControl("childField2", new FormControl(data?.childField2 || '', Validators.required));
});

add 子“++”方法的末尾添加 this.cdr.detectChanges() 将修复它。