Here's an example:
@Component({
selector: 'my-app',
template: `
<div>
<h1>{{ foo }}</h1>
<bpp [(foo)]="foo"></bpp>
</div>
`,
})
export class App {
foo;
}
@Component({
selector: 'bpp',
template: `
<div>
<h2>{{ foo }}</h2>
</div>
`,
})
export class Bpp {
@Input('foo') foo;
@Output('fooChange') fooChange = new EventEmitter();
ngAfterViewInit() {
const potentiallyButNotNecessarilyAsyncObservable = Observable.of(null);
potentiallyButNotNecessarilyAsyncObservable.subscribe(() => {
this.fooChange.emit('foo');
})
}
}
where error occasionally appears:
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'foo'
It results from the fact that two-way binding is updated by an observable that can get a value on same tick. I would prefer to not wrap the logic above with setTimeout
because it looks like a hack and complicates control flow
What can be done to avoid this error here?
Does ExpressionChangedAfterItHasBeenCheckedError
error have ill effects or can it be ignored? If it can, can change detector be silent on it and not pollute the console?
Let's first unwrap the two-way data binding to simplify explanation:
<div>
<h1>{{ foo }}</h1>
<bpp [foo]="foo" (fooChange)="foo=$event"></bpp>
</div>
It still has the same effect and occasionally produces the error. The error will only be produced if the potentiallyButNotNecessarilyAsyncObservable
is synchronous. So we can also replace this:
ngAfterViewInit() {
const potentiallyButNotNecessarilyAsyncObservable = Observable.of(null);
potentiallyButNotNecessarilyAsyncObservable.subscribe(() => {
this.fooChange.emit('foo');
})
with this:
ngAfterViewInit() {
this.fooChange.emit('foo');
This case falls into the Synchronous event broadcasting
category of errors that is explained in the article Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError
error.
The ngAfterViewInit
lifecycle hook is triggered after the parent component changes have been processed. The order of hooks related to child components is explained in the Everything you need to know about change detection in Angular. Now Angular remembers that when it ran change detection for the App
component the value of foo
was undefined
, but during validation phase the value is foo
that is updated by the child Bpp
component. Hence it produces the error.
What can be done to avoid this error here?
The fixes and the problems are described in the article I linked. The only safe option here if you don't want to redesign you logic is asynchronous update. You could also run change detection for the parent component but they may lead to an infinite loop since change detection on a component triggers change detection for component's children.
Does ExpressionChangedAfterItHasBeenCheckedError error have ill effects or can it be ignored?
The ill affect is that you will have incosistent state in the application App.foo==='foo'
and the view {{foo}}===undefined
until the next digest cycle iteration. The error cannot be turned off in the development mode but it will not appear in the production mode.
Two Phases of Angular Applications is also pretty good in explaining the mental model for this error.
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