动态嵌套的反应形式:ExpressionChangedAfterItHasBeenCheckedError [英] Dynamic nested reactive form: ExpressionChangedAfterItHasBeenCheckedError

查看:131
本文介绍了动态嵌套的反应形式:ExpressionChangedAfterItHasBeenCheckedError的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的反应形式深达三个组成部分.父组件将创建一个没有任何字段的新表单,并将其传递给子组件.

My reactive form is three component levels deep. The parent component creates a new form without any fields and passes it down to child components.

首先,外部格式有效.稍后在子组件上添加带有验证器(失败)的新表单元素,使外部表单无效.

At first the outer form is valid. Later on a child component adds new form elements with validators (that fail) making the outer form invalid.

我在控制台中收到一个 ExpressionChangedAfterItHaHasBeenCheckedError 错误.我想修复该错误.

I am getting an ExpressionChangedAfterItHasBeenCheckedError error in the console. I want to fix that error.

不知何故,只有在添加第三级嵌套时才会发生这种情况.相同的方法似乎适用于两个级别的嵌套.

Somehow this only happens when I add the third level of nesting. The same approach seemed to work for two levels of nesting.

柱塞:: https://plnkr.co/edit/GymI5CqSACFEvhhz55l1 ?p =预览

父组件

Parent component

@Component({
  selector: 'app-root',
  template: `
    myForm.valid: <b>{{myForm.valid}}</b>
    <form>
      <app-subform [myForm]="myForm"></app-subform>
    </form>
  `
})
export class AppComponent implements OnInit {
  ...

  ngOnInit() {
    this.myForm = this.formBuilder.group({});
  }
}

子组件

Sub component

@Component({
  selector: 'app-subform',
  template: `
    <app-address-form *ngFor="let addressData of addressesData;"
      [addressesForm]="addressesForm">
    </app-address-form>
  `
})
export class SubformComponent implements OnInit {
  ...

  addressesData = [...];

  constructor() { }

  ngOnInit() {
    this.addressesForm = new FormArray([]);
    this.myForm.addControl('addresses', this.addressesForm);
  }

子组件

Child component

@Component({
  selector: 'app-address-form',
  template: `
    <input [formControl]="addressForm.controls.addressLine1">
    <input [formControl]="addressForm.controls.city">
  `
})
export class AddressFormComponent implements OnInit {
  ...

  ngOnInit() {
    this.addressForm = this.formBuilder.group({
      addressLine1: [
        this.addressData.addressLine1,
        [ Validators.required ]
      ],
      city: [
        this.addressData.city
      ]
    });

    this.addressesForm.push(this.addressForm);
  }
}

推荐答案

要了解此问题,您需要阅读

To understand the problem you need to read Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error article.

对于您的特殊情况,问题在于您正在AppComponent中创建表单,并在DOM中使用{{myForm.valid}}插值.这意味着Angular 将运行创建并运行updateRenderer函数AppComponent的a>.然后,使用子组件的ngOnInit生命周期钩子将带有控件的子组添加到此表单中:

For your particular case the problem is that you're creating a form in the AppComponent and use a {{myForm.valid}} interpolation in the DOM. It means that Angular will run create and run updateRenderer function for the AppComponent that updates DOM. Then you use the ngOnInit lifecycle hook of subcomponent to add subgroup with control to this form:

export class AddressFormComponent implements OnInit {
  @Input() addressesForm;
  @Input() addressData;

  ngOnInit() {
    this.addressForm = this.formBuilder.group({
      addressLine1: [
        this.addressData.addressLine1,
        [ Validators.required ]   <-----------
      ]

    this.addressesForm.push(this.addressForm); <--------

该控件无效,因为您没有提供初始值,而是指定了必需的验证器.因此,整个表格将变得无效,并且表达式{{myForm.valid}}的结果为false.但是,当对AppComponent进行Angular运行变化检测时,其评估结果为true.这就是错误的意思.

The control becomes invalid because you don't supply initial value and you specify a required validator. Hence the entire form becomes invalid and the expression {{myForm.valid}} evaluates to false. But when Angular ran change detection for the AppComponent it evaluated to true. And that's what the error says.

一个可能的解决方法是在一开始就将表单标记为无效,因为您打算添加必需的验证器,但是Angular似乎没有提供这种方法.您最好的选择可能是异步添加控件.实际上,这是Angular在源代码中所做的事情:

One possible fix could be to mark the form as invalid in the start since you're planning to add required validator, but it seems Angular doesn't provide such method. Your best choice is probably to add controls asynchronously. In fact, this is what Angular does itself in the sources:

const resolvedPromise = Promise.resolve(null);

export class NgForm extends ControlContainer implements Form {
  ...

  addControl(dir: NgModel): void {
    // adds controls asynchronously using Promise
    resolvedPromise.then(() => {
      const container = this._findContainer(dir.path);
      dir._control = <FormControl>container.registerControl(dir.name, dir.control);
      setUpControl(dir.control, dir);
      dir.control.updateValueAndValidity({emitEvent: false});
    });
  }

对于您而言,它将是:

const resolvedPromise = Promise.resolve(null);

@Component({
   ...
export class AddressFormComponent implements OnInit {
  @Input() addressesForm;
  @Input() addressData;

  addressForm;

  ngOnInit() {
    this.addressForm = this.formBuilder.group({
      addressLine1: [
        this.addressData.addressLine1,
        [ Validators.required ]
      ],
      city: [
        this.addressData.city
      ]
    });

    resolvedPromise.then(() => {
       this.addressesForm.push(this.addressForm); <-------
    })
  }
}

或者在AppComponent中使用一些变量来保持表单状态并在模板中使用它:

Or use some variable in the AppComponent to hold form state and use it in the template:

{{formIsValid}}

export class AppComponent implements OnInit {
  myForm: FormGroup;
  formIsValid = false;

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.myForm = this.formBuilder.group({});
    this.myForm.statusChanges((status)=>{
       formIsValid = status;
    })
  }
}

这篇关于动态嵌套的反应形式:ExpressionChangedAfterItHasBeenCheckedError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆