Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4: `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked

Every EventEmiiter in my child module gives this error and I can't find a fix for this.

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

This is what triggers my EventEmitter:

ngOnChanges(changes: any) {
    if (changes.groupingTabValid) {
        if (changes.groupingTabValid.currentValue !== changes.groupingTabValid.previousValue) {
            this.groupingTabValidChange.emit(this.groupingTabValid);
        }
    }
}

Here is my "main" componenent's HTML

<year-overview-grouping [definitionDetails]="definitionDetails"
                        [fixedData]="fixedData"
                        [showValidation]="showValidation"
                        [groupingTabValid]="groupingTabValid"
                        (groupingTabValidChange)="setValidators('groupingTab', $event)">

</year-overview-grouping>

Which calls this function

public setValidators(validator: string, e: boolean) {
    switch (validator) {
        case "groupingTab":             
            this.groupingTabValid = e;
            break;

        case "selectionTab":
            this.selectionTabValid = e;
            break;
    }

    if (this.groupingTabValid && this.selectionTabValid) {
        this.valid = true;
    } else {
        this.valid = false;
    }
}

1) In a a simple explanation, what's causing this error?

2) What steps can I take to solve this?

like image 341
Nicolas Avatar asked May 19 '17 12:05

Nicolas


People also ask

What is expression changed after it was checked in angular?

The expression has changed after it was checked (Expressionchangedafterithasbeencheckederror) is one of the common errors, that we encounter in Angular applications. The error arises when the binding expression changes after angular checked it during the change detection cycle. What is Change Detection ?

What is expression changed after it was checked?

Angular Examples & Projects The expression has changed after it was checked (Expressionchangedafterithasbeencheckederror) is one of the common errors, that we encounter in Angular applications. The error arises when the binding expression changes after angular checked it during the change detection cycle.

What is change detection error in angular?

The error arises when the binding expression changes after angular checked it during the change detection cycle. What is Change Detection ? To understand the error, let us create an app and try to produce the error.

What is expressionchangedafterithasbeencheckederror trying to warn me about?

What Is ExpressionChangedAfterItHasBeenCheckedError Trying to Warn Me About? ExpressionChangedAfterItHasBeenCheckedError is thrown when an expression in your HTML has changed after Angular has checked it (it is a very expressive error).


4 Answers

Abide by the unidirectional data flow rule

Try to postpone the call to emit for one tick with a setTimeout

if (changes.groupingTabValid.currentValue !== changes.groupingTabValid.previousValue) {
    setTimeout(() => this.groupingTabValidChange.emit(this.groupingTabValid), 0)
}
like image 129
Draeken Avatar answered Oct 17 '22 23:10

Draeken


Just import ChangeDetectionStrategy and then add this to your component

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'app-bla-bla-bla',
  templateUrl: './xxxxx'
})
like image 42
Cengkuru Michael Avatar answered Oct 17 '22 22:10

Cengkuru Michael


You can avoid using setTimeout and tell Angular that changes are about to happen after initial check.

You can inject ChangeDetectorRef into your component and use its markForCheck method.

/* ... */

constructor(private cdr: ChangeDetectorRef) {}

/* ... */

if (changes.groupingTabValid.currentValue !== changes.groupingTabValid.previousValue {
  this.cdr.markForCheck();
  this.groupingTabValidChange.emit(this.groupingTabValid);
}
like image 2
Michał Pietraszko Avatar answered Oct 17 '22 22:10

Michał Pietraszko


I'm just starting to look at this exception type myself, so no expert, but presume that it's there to stop feedback loops.

I can't see any obvious reason why you want to send the value down to the child and back up to the parent, but presume the child applies some additional logic. To me, seems better to apply any additional logic in a service rather than a child component.

I realize the plunker code doesn't have a loop, but the Angular mechanism seems rather simple - just re-checks values at the end of the cycle. Note, groupingTabValid in your question code does feedback directly.

Looking at the plunker code as given, structurally the change to age can be handled on the parent. The click event would be (click)=changeAge(age.value) and the method (on parent) to handle it

changeAge(inputAge) {
  this.newAge = inputAge;
  this.person.age = inputAge;
}

Here's a fork of the plunker. Modified plunker
The child component still updates person.age, but no longer causes an error as the value is the same as set on the parent.

like image 1
Richard Matsen Avatar answered Oct 17 '22 22:10

Richard Matsen