If you think `ngDoCheck` means your component is being checked — read this article
In this article I'll explain in great detail when is ngDoCheck triggered which will make it clear why ngDoCheck lifecycle hook is still triggered for a component that implements OnPush change detection strategy.

There’s one question that comes up again and again on stackoverflow. The question is about ngDoCheck
lifecycle hook that is triggered for a component that implements OnPush
change detection strategy. It’s usually formulated something like:
I have usedOnPush
strategy for my component and no bindings have changed, but thengDoCheck
lifecycle hook is still triggered. Is the strategy not working?
It’s an interesting question which stems from misunderstanding of when the the ngDoCheck
lifecycle hook is triggered and why it’s provided by the framework. This article gives an answer to this common question by showing when the hook in question is triggered and what’s its purpose.
When is ngDoCheck triggered
The official docs don’t tell much about this lifecycle hook:
Detect and act upon changes that Angular can’t or won’t detect on its own.
Called during every change detection run, immediately afterngOnChanges()
andngOnInit()
.
So we know that it’s triggered after ngOnChanges
and ngOnInit
but is the component being checked or not when it’s triggered? To answer that question, we need to first define “component check”. The article Everything you need to know about change detection in Angular states that there are three core operations related to component change detection:
- update child component input bindings
- update DOM interpolations
- update query list
Besides these core operations Angular triggers lifecycle hooks as part of change detection. What is interesting is that the hooks for the child component are triggered when the parent component is being checked. Here is the small example to demonstrate that. Suppose we have the following components tree:
ComponentA
ComponentB
ComponentC
So when Angular runs change detection the order of operations is the following:
Checking A component:
- update B input bindings
- call NgDoCheck on the B component
- update DOM interpolations for component A
Checking B component:
- update C input bindings
- call NgDoCheck on the C component
- update DOM interpolations for component B
Checking C component:
- update DOM interpolations for component C
If you read the article on change detection I referenced above you will notice that it’s a bit abridged list of operations, but it will do for the purpose of demonstrating when ngDoCheck
is triggered.
You can see that ngDoCheck
is called on the child component when the parent component is being checked. Now suppose we implement onPush
strategy for the B
component. How does the flow change? Let’s see:
Checking A component:
- update B input bindings
- call NgDoCheck on the B component
- update DOM interpolations for component A
if (bindings changed) -> checking B component:
- update C input bindings
- call NgDoCheck on the C component
- update DOM interpolations for component B
Checking C component:
- update DOM interpolations for component C
So with the introduction of the OnPush
strategy we see the small condition if (bindings changed) -> checking B component
is added before B
component is checked. If this condition doesn’t hold, you can see that Angular won’t execute the operations under checking B component
. However, the NgDoCheck
on the B
component is still triggered even though the B
component will not be checked. It’s important to understand that the hook is triggered only for the top level B
component with OnPush
strategy, and not triggered for its children — C
component in our case.
So, the answer to the question:
I have usedOnPush
strategy for my component, but thengDoCheck
lifecycle hook is still triggered. Is the strategy not working?
is — the strategy is working. The hook is triggered by design and the next chapter shows why.
Why do we need ngDoCheck?
You probably know that Angular tracks binding inputs by object reference. It means that if an object reference hasn’t changed the binding change is not detected and change detection is not executed for a component that uses OnPush
strategy. In AngularJS this is the standard approach as well and in the following example the changes to o
object won’t be detected:
const o = {some: 3};
$scope.$watch(
() => { return o;},
() => { console.log('changed'); } // nothing is logged
);
$timeout(() => { o.some = 4; }, 2000);
But AngularJS has a few variations of standard $watch
function to allow tracking object and array mutations — deep watch and collection watch.To enabled the deep watch you have to pass the third parameter true
to the $watch
function:
$scope.$watch(
() => { return o;},
() => { console.log('changed'); }, // logs `changed`
true
);
And to watch collections you have to use $watchCollection
method:
const o = [3];
$scope.$watchCollection(
() => { return o;},
() => { console.log('changed'); } // logs `changed`
);
$timeout(() => { o.push(4) }, 2000);
But there’s no equivalent in Angular. If we want to track an object or an array mutations we need to manually do that. And if discover the change we need to let Angular know so that it will run change detection for a component even though the object reference hasn't changed.
Let’s use the example from AngularJS in the Angular application. We have A
component that uses OnPush
change detection strategy and takes o
object through input binding. Inside the component template it references the name
property:
@Component({
selector: 'a-comp',
template: `<h2>The name is: {{o.name}}</h2>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AComponent {
@Input() o;
}
And we also have the parent App
component that passes the o
object down to the child a-comp
. In 2 seconds it mutates the object by updating the name
and id
properties:
@Component({
selector: 'my-app',
template: `
<h1>Hello {{name}}</h1>
<a-comp [o]="o"></a-comp>
`,
})
export class App {
name = `Angular! v${VERSION.full}`;
o = {id: 1, name: 'John'};
ngOnInit() {
setTimeout(() => {
this.o.id = 2;
this.o.name = 'Jane';
}, 2000);
}
}
Since Angular tracks object reference and we mutate the object without changing the reference Angular won’t pick up the changes and it will not run change detection for the A
component. Thus the new name
property value will not be re-rendered in DOM.
Luckily, we can use the ngDoCheck
lifecycle hook to check for object mutation and notify Angular using markForCheck method. In the implementation above we will track only id
property mutation but if required you can implement full-blown change tracking similar to deep watch in AngularJS.
Let’s do just it:
export class AComponent {
@Input() o;
// store previous value of `id`
id;
constructor(private cd: ChangeDetectorRef) {}
ngOnChanges() {
// every time the object changes
// store the new `id`
this.id = this.o.id;
}
ngDoCheck() {
// check for object mutation
if (this.id !== this.o.id) {
this.cd.markForCheck();
}
}
}
Here is the plunker that shows that approach. To practice on your own you can try to implement the full-blown deep watch and watch collection approaches similar to AngularJS.
One thing to bear in mind is that Angular team recommends using immutable objects instead of object mutations so that you can rely on default Angular bindings change tracking mechanism. But since it’s not always possible as you’ve just learnt Angular provides a fallback in the form of ngDoCheck
lifecycle hook.