2017-08-13 179 views
4

我的反应形式是三个组件级别深。父组件创建一个没有任何字段的新表单,并将其传递给子组件。动态嵌套反应形式:ExpressionChangedAfterItHasBeenCheckedError

首先,外形是有效的。稍后,子组件添加带有验证器的新表单元素(失败),从而使外部表单无效。

我收到的控制台ExpressionChangedAfterItHasBeenCheckedError错误。我想修复这个错误。

不知何故,这只发生在我添加第三层嵌套。同样的方法似乎适用于两层嵌套。

Plunker:https://plnkr.co/edit/GymI5CqSACFEvhhz55l1?p=preview

父组件

@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({}); 
    } 
} 

子组件

@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); 
    } 

子组件

@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); 
    } 
} 

enter image description here

+0

检查[你需要了解的'ExpressionChangedAfterItHasBeenCheckedError'错误的一切(https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasb eencheckederror-error-e3fd9ce7dbb4)文章 –

+0

我在过去24小时内访问了该页面5次;)。仍然不知道如何解决它。 –

+0

你参考 –

回答

5

要了解你需要阅读Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error文章的问题。

对于您的特定情况,问题在于您要在AppComponent中创建表单并在DOM中使用插值。这意味着更新DOM的的角度will run create and run updateRenderer function。然后,可以使用子的ngOnInit生命周期挂钩与控件添加分组这种形式:因为你不提供初始值,指定所需的验证

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。但是当Angular运行AppComponent的变化检测时,它评估为true。这就是错误所说的。

由于您打算添加必需的验证器,因此一种可能的解决方法是将表单标记为无效,但似乎Angular不提供此类方法。您的最佳选择可能是异步添加控件。事实上,这就是角做自己在来源:

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持有形式的国家,并用它在模板:

{{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; 
    }) 
    } 
} 
+0

感谢您的非常详细的答案!欣赏它。 –

+0

@ErikNijland,不客气)祝你好运 –

+0

对于档案:第一种方法完美无缺地工作。第二个没有工作。它只是将相同的错误移动到另一个变量。还需要一个.subscribe来检查statusChanges。 –