问题
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?
回答1:
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)
}
回答2:
Just import ChangeDetectionStrategy and then add this to your component
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-bla-bla-bla',
templateUrl: './xxxxx'
})
回答3:
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);
}
回答4:
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.
来源:https://stackoverflow.com/questions/44070732/angular-4-expressionchangedafterithasbeencheckederror-expression-has-changed