I can't seem to figure out why I need ngDoCheck lifecycle hook other than for simple notification, particularly how writing code inside of it makes a difference as regard to change detection. Most of the examples I've found show useless examples, like this one, with a bunch of logging functionality.
Also, in the generated classes I don't see it being used for something else other than simple notification:
conmponent/wrapper.ngfactory.js
Wrapper_AppComponent.prototype.ngDoCheck = function(view,el,throwOnChange) {
var self = this;
var changed = self._changed;
self._changed = false;
if (!throwOnChange) {
if (changed) {
jit_setBindingDebugInfoForChanges1(view.renderer,el,self._changes);
self._changes = {};
}
self.context.ngDoCheck(); <----------- this calls ngDoCheck on the component
but the result is not used
anywhere and no params are passed
}
return changed;
};
This great article If you think
ngDoCheckmeans your component is being checked — read this article explains the error in depth.The contents of this answer is based on the angular version 2.x.x. For the most recent version 4.x.x see this post.
There is nothing on the internet on the inner workings of change detection, so I had to spend about a week debugging sources, so this answer will be pretty technical on details.
An angular application is a tree of views (
AppViewclass that is extended by the Component specific class generated by the compiler). Each view has a change detection mode that lives incdModeproperty. The default value forcdModeisChangeDetectorStatus.CheckAlways, which iscdMode = 2.When a change detection cycle runs, each parent view checks whether it should perform change detection on the child view here:
where
thispoints to thechildview. So ifcdModeisChangeDetectorStatus.Checked=1, the change detection is skipped for the immediate child and all its descendants because of this line.What
changeDetection: ChangeDetectionStrategy.OnPushdoes is simply setscdModetoChangeDetectorStatus.CheckOnce = 0, so after the first run of change detection the child view will have itscdModeset toChangeDetectorStatus.Checked = 1because of this code:Which means that the next time a change detection cycle starts there will be no change detection performed for the child view.
There are few options how to run change detection for such view. First is to change child view's
cdModetoChangeDetectorStatus.CheckOnce, which can be done usingthis._changeRef.markForCheck()inngDoChecklifecycle hook:This simply changes
cdModeof the current view and its parents toChangeDetectorStatus.CheckOnce, so next time the change detection is performed the current view is checked.Check a full example here in the sources, but here is the gist of it:
The second option is call
detectChangeson the view itself which will run change detection on the current view ifcdModeis notChangeDetectorStatus.CheckedorChangeDetectorStatus.Errored. Since withonPushangular setscdModetoChangeDetectorStatus.CheckOnce, angular will run the change detection.So
ngDoCheckdoesn't override the changed detection, it's simply called on every changed detection cycle and it's only job is to set current viewcdModeascheckOnce, so that during next change detection cycle it's checked for the changes. See this answer for details. If the current view's change detection mode ischeckAlways(set by default if onPush strategy is not used),ngDoCheckseem to be of no use.