动态嵌套的反应形式:ExpressionChangedAfterItHasBeenCheckedError [英] Dynamic nested reactive form: 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函数用于更新DOM的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屋!