visit
There are other operations that are performed during change detection and I’ve presented them all in the Everything you need to know about change detection in Angular.
After each operation Angular remembers what values it used to perform an operation. They are stored in the oldValues property of the component view. After the checks have been done for all components Angular than starts the next digest cycle but instead of performing the operations listed above it compares the current values with the ones it remembers from the previous digest cycle:
template: '<span>{{name}}</span>'
And it also has B component in its template and passes the text property to this component through input property binding:
@Component({
selector: 'a-comp',
template: `
<span>{{name}}</span>
<b-comp [text]="text"></b-comp>
`
})
export class BComponent {
name = 'I am A component';
text = 'A message for the child component`;
So here is what happens when Angular runs change detection. It starts by checking A component. The first operation in the list is to update bindings so it evaluates text expression to A message for the child component and passes it down to the B component. It also stores this value on the view:
view.oldValues[0] = 'A message for the child component';
Then it calls the lifecycle hooks mentioned in the list.
Now, it performs the third operation and evaluates the expression {{name}} to the text I am A component. It updates the DOM with this value and puts the evaluated value to the oldValues:
view.oldValues[1] = 'I am A component';
Then Angular performs the next operation and runs the same check for the child B component. Once the B component is checked the current digest loop is finished.
If Angular is running in the development mode it then runs the second digest performing verification operations I listed above. Now imagine that somehow the property text was updated on the A component to the updated text after Angular passed the value A message for the child component to the B component and stored it. So it now runs the verification digest and the first operation is to check that the property name is not changed:
AComponentView.instance.text === view.oldValues[0]; // false
'A message for the child component' === 'updated text'; // false
Yet it has and so Angular throws the error ExpressionChangedAfterItHasBeenCheckedError.
The same holds for the third operation. If the name property was updated after it was rendered in the DOM and stored we’ll get the same error:
AComponentView.instance.name === view.oldValues[1]; // false
'I am A component' === 'updated name'; // false
You probably have a question in your mind now how is it possible that these values changed. Let’s see that.
export class BComponent {
@Input() text;
constructor(private parent: AppComponent) {}
ngOnInit() {
this.parent.text = 'updated text';
}
}
And as expected we get the error:
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'A message for the child component'. Current value: 'updated text'.
Now, let’s do the same for the property name that is used in the template expression of the parent A component:
ngOnInit() {
this.parent.name = 'updated name';
}
And now everything works OK. How come?
Well if you take an attentive look at the operations order you will see that the ngOnInit lifecycle hook is triggered before the DOM update operation. That’s why there’s no error. We need a hook that is called after the DOM update operations and the ngAfterViewInit is a good candidate:
export class BComponent {
@Input() text;
constructor(private parent: AppComponent) {}
ngAfterViewInit() {
this.parent.name = 'updated name';
}
}
And this time we get the expected error:
AppComponent.ngfactory.js:8 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'I am A component'. Current value: 'updated name'.
Of course, real world examples are much more intricate and complex. The parent component properties update or operations that cause DOM rendering are usually done indirectly by using services or observables. But the root cause is always the same.
Now let’s see some real-world common patterns that lead to the error.
Often, the fix is to use the right change detection hook to create a dynamic component. For example, the last example in the previous section with dynamic components can be fixed by moving the component creation to the ngOnInit hook. Although the documentation states the ViewChildren are available only after ngAfterViewInit, it populates the children when creating a view and so they are available earlier. If you google around you will probably find two most common fixes for this error — asynchronous property update and forcing additional change detection cycle. Although I show them here and explain why they work I don’t recommend using them but rather redesign your application. I explain why in the last chapter.Expression has changed after it was checked. Previous value:… Has it been created in a change detection hook ?
export class BComponent {
name = 'I am B component';
@Input() text;
constructor(private parent: AppComponent) {}
ngOnInit() {
setTimeout(() => {
this.parent.text = 'updated text';
});
}
ngAfterViewInit() {
setTimeout(() => {
this.parent.name = 'updated name';
});
}
}
Indeed, no error is thrown. The setTimeout function schedules a macrotask then will be executed in the following VM turn. It is also possible to executed the update in the current VM turn but after the current synchronous code has finished executing by using then callback of a promise:
Promise.resolve(null).then(() => this.parent.name = 'updated name');
Instead of a macrotask Promise.then creates a micrtotask. The microtask queue is processed after the current synchronous code has finished executing hence the update to the property will happen after the verification step.
export class AppComponent {
name = 'I am A component';
text = 'A message for the child component';
constructor(private cd: ChangeDetectorRef) {
}
ngAfterViewInit() {
this.cd.detectChanges();
}
Well, no error. So it seems to be working but there is a problem with this solution. When we trigger change detection for the parent A component, Angular will run change detection for all child components as well and so there’s a possibility of parent property update anew.
Angular enforces so-called unidirectional data flow from top to bottom. No component lower in hierarchy is allowed to update properties of a parent component after parent changes have been processed. This ensures that after the first digest loop the entire tree of components is stable. A tree is unstable if there are changes in the properties that need to be synchronized with the consumers that depend on those properties. In our case a child B component depends on the parent text property. Whenever these properties change the component tree becomes unstable until this change is delivered to the child B component. The same holds for the DOM. It is a consumer of some properties on the component and it renders them on UI. If some properties are not synchronized a user will see incorrect information on the page.
This data synchronization process is what happens during change detection — particularly those two operations I listed in the beginning. So what happens if you update the parent properties from the child component properties after the synchronization operation has been performed? Right, you’re left with the unstable tree and the consequences of such state is not possible to predict. Most of the times you will end up with an incorrect information shown on the page to the user. And this will be very difficult to debug.
So why not run the change detection until the components tree stabilizes? The answer is simple — because it may never stabilize and run forever. If a child component updates a property on the parent component as a reaction to this property change you will get an infinite loop. Of course, as I said earlier it’s trivial to spot such a pattern with the direct update or dependency but in the real application both update and dependency are usually indirect.Interestingly, AngularJS didn’t have a unidirectional data flow so it tried to stabilize the tree. But it often resulted in the infamous 10 $digest() iterations reached. Aborting! error. Go ahead and google this error and you’ll be surprised by the amount of questions that this error produced.
The last question you may have is why run this only during development mode? I guess this is because an unstable model is not as dramatic problem as a runtime error produced by the framework. After all it may stabilize in the next digest run. However, it’s better to be notified of the possible error when developing an application than debug a running application on the client side.