Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FormBuilder Control causing "Expression has changed after it was checked" exception

I have a form that is being instantiated programatically via DynamicComponentLoader::loadIntoLocation. The form code is below:

constructor (
    private _builder: FormBuilder
) {
    this.editForm = _builder.group({
        name: ['', Validators.required],
        email: ['', Validators.compose([Validators.required, Helpers.emailValidator])],
        phone: [''],
        phoneAlt: [''],
        location: [''],
        dob: [''],
        bio: [''],
    });
}

You'll notice that some of the forms don't have validators (as far as I can tell, this is the same as using Validators.nullValidator, I've tested with both).

In my template I have the following code (for each control):

<label for="phone">Contact Number <span *ngIf="!phone.valid">- {{e(phone)}}</span></label>
<input type="text" name="phone" id="phone" ngControl="phone" #phone="ngForm">

The first control that doesn't have a validator throws the following exception twice when it hits the !phone.valid part of the template:

EXCEPTION: Expression '!phone.valid in e@15:43' has changed after it was checked. Previous value: 'true'. Current value: 'false' in [!phone.valid in e@15:43]

At no point am I touching the controls or this.editForm after the initial creation, so, as far as my code is concerned, nothing should be changing.

I'm aware that I can suppress the errors by calling enableProdMode() but I'd rather fix the problem than hide it.

Edit (8th Feb): I have since tried moving the contents of the modal to a separate page, but the errors persist. This would suggest the issue is not related to the way I am creating and loading the modals, but rather the ControlGroup or FormBuilder.

Plunker of the issue | Plunker without modal

like image 470
Tam Avatar asked Jan 26 '16 17:01

Tam


3 Answers

Thanks to qdouble for solving this for me on the Angular Gitter chat.

The issue seemed to be caused by the order in which angular parsed the page. By going from top to bottom, ngIf="!phone.valid" was being parsed before phone.valid had been initialised. This was easily fixed by adding a catch in the if statement to make sure that it was not null *ngIf="phone.valid === null ? false : !phone.valid" (or by moving the label after the input).

like image 174
Tam Avatar answered Oct 21 '22 14:10

Tam


using ngAfterContentChecked fixed the error in my side

ngAfterContentChecked(): void {
    this.cd.detectChanges();
}
like image 45
wessam yaacob Avatar answered Oct 21 '22 14:10

wessam yaacob


This was the problem I ran into.

Angular 2 introduced a feature to better handle change detection. Angular 2 drops the digest cycles in favor of one-way flow which is about 3-10 times faster and handles asynchronous logic better.

@Component({
    ...
    changeDetection: ChangeDetectionStrategy.OnPush
})...

Links: Angular Reference: https://angular.io/docs/ts/latest/api/core/index/ChangeDetectionStrategy-enum.html

Understanding change detection: https://auth0.com/blog/understanding-angular-2-change-detection/

How Angular 2 Change Detection Really Works: http://blog.angular-university.io/how-does-angular-2-change-detection-really-work/

like image 9
Tetrapike Avatar answered Oct 21 '22 12:10

Tetrapike