I have a parent component which displays a list of validation errors and renders a list of child components with ngFor
. Each child component performs its validation during ngOnInit
and outputs the result to the parent component. The parent component listens for this output and updates the list of validation errors, the result of which throws an error:
Expression has changed after it was checked
Now, I understand why this error is being thrown - at the start of the change detection cycle the validation errors were in one state, and at the end it was in another state, and this is not allowed.
What I do not understand is how to get around this issue. The parent component has to display a list of errors at the top of the page, and each child component will add their validation results to this list. If this is a breach of the uni-directional dataflow then please tell me how to get around this in a clean manner (i.e, not wrapping the validation in a setTimeout
, not changing from an immutable list to a mutable one, and not explicitly invoking the change detector again after validation).
Plunker reproducing the issue: https://plnkr.co/edit/q52A1DraNOnxZa0qGFDo?p=preview
EDIT
I've "solved" the issue by constructing the EventEmitter
with the isAsync
flag:
new EventEmitter(true)
This means that values will be emitted asynchronously, so emitted values will be picked up on the next change detection cycle. I guess the result is the same as wrapping the logic in a setTimeout
but this way at least we don't have to wrap our code in a timeout everywhere we may emit a value.
Angular doesn't like when change detection itself causes changes and ngOnInit
is called by change detection.
In devMode change detection does an additional change detection run after each regular change detection run, to check if the model is stable. If the model changed during change detection, it throws this error.
You can delay the change until after change detection is completed using
ngOnInit() {
setTimeout(() => {
this.output.emit({value: true});
}
}
Plunker example
Update (see Ghetolay's comment below)
ngOnInit
is not called by change detection and you can do bind changes inside it. What you cannot do is making change on a parent from a child'sngOnInit
cause this breaks unidirectional data flow.
See also https://github.com/angular/angular/issues/10131#issuecomment-233369491
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With