In the last 8 months I’ve spent most of my free time reverse-engineering Angular. The topic that fascinated me the most was change detection. I’d argue that it’s the most important part of the framework since it’s responsible for the “visible” job like DOM updates, input bindings and query list updates. My exploration resulted in a bunch of in-depth articles that highlight main ideas of change detection mechanism and dive deep into the implementation details. In this article I put them all together with a short description of what you can find in each one. Once you finish reading them you will achieve change detection enlightenment 😎.
Understanding change detection
Here is the list of 5 in-depth articles (and one new one) that will significantly expand the boundaries of what you know about change detection process in Angular. Each article builds upon the information explained in the preceding one so I recommend that you read them in the order they are listed here.
This article draws a comparison between $digest process in AngularJS and change detection process in Angular. It explains the need for both of them and shows how they are built using the same concept of dirty checking. It then provides some examples that demonstrate how lifecycle hooks in Angular can be used as an equivalent mechanism to
$watch in AngularJS. It also shows how Angular is different from AngularJS as it now enforces so-called unidirectional data flow from top to bottom. The article explains the reasoning behind the enforcement, its benefits and restrictions it puts on the architecture. This article will be especially useful for the experienced AngularJS developers who look to migrate to Angular.
This article provides a “light” explanation of the change detection mechanism. It’ll give you a high-level overview of its constituent parts and mechanics: internal data structures used to represent a component, the role of bindings and the operations performed as part of the process. I’ll also touch on zones and show you exactly how this functionality enables automatic change detection in Angular.
This articles describes how NgZone is implemented on top of the zone.js library and explains the role that NgZone plays in the framework. Contrary to the common belief it’s not part of the change detection process but is rather used to trigger it. The article starts by demonstrating how Angular can detect changes and perform rendering without both NgZone and zone.js. Then it proceeds to show what value NgZone brings and how it’s implemented. A big part of the article is devoted to explaining the commonly used public API like
onMicrotaskEmpty. The article is concluded with the explanation of the common pitfall with changes not being detected when using third-party libraries like GoogleAPI.
This article is a must-read if you want to have a solid grasp of the change detection mechanism. It provides a high-level overview of how the engine is implemented with relevant links for further exploration. The write-up starts by introducing an internal component presentation called View and explains that change detection process runs on views. It then presents a list of all operations performed during change detection in the order of execution. The operations include updating view state, rendering, processing input bindings and calling lifecycle hooks. Finally it explains the ChangeDetectorRef public API like
markForCheck and provides short examples of the methods usage.
This article dives deep into implementation details of the process of synchronizing application models with the DOM, a.k.a. one-way data binding or DOM rendering. This operation takes a central place in the change detection process as it is exactly what makes component changes rendered in the DOM. The article begins by disclosing additional details about the concept of View, particularly, View Factory and several essential types of View Nodes. It then shows how change detection mechanism performs DOM updates setup by interpolation or input binding for these nodes.
Similarly to the previous article on DOM updates, this one explores implementation details of the process responsible for updating input bindings for child components and directives. It introduces a notion of a Binding Definition and its role in the change detection process. It then proceeds to demonstrate how these binding definitions are generated by a compiler when it processes template syntax for property bindings. Finally, it outlines a step-by-step process of running change detection on View Nodes and updating input properties on child components and directives.
Avoiding common confusions
Here is an additional list of valuable articles that clear up some confusions related to change detection that I often see on stackoverflow.
This article gives an answer to an interesting question whether Angular first checks siblings of the current component (breadth-first order) or its children (depth-first). It shows how Angular triggers lifecycle hooks on sibling components before it actually starts checking them and explains how this behavior may lead you to the wrong answer.
This article explains the difference between two-way data-binding and unidirectional data flow. It demonstrates how the process of updating input bindings is different in Angular from AngularJS and how where this difference is important.
This article explains the reasoning and mechanics behind the frequent and often misunderstood error in the Angular community. While some developers view it as a bug, in reality this is a design decision put in place to boost performance by limiting change detection runs to a single run as opposed to numerous runs (
$digest runs) in AngularJS. This article explains how throwing the error helps prevent inconsistencies between model data and UI so that erroneous or old data are not shown to a user on the page. The article mainly consists of two parts with first exploring the causes of the error and the second suggesting possible fixes. It also explains why the error is not thrown in the production mode.
This article provides an elaborate answer to the question why
ngDoCheck lifecycle hook is triggered for the components that use
OnPush strategy even if the input bindings on these components have not changed. It explains the usually unexpected fact that hooks are triggered for the child components when the parent component is being checked and shows how that mechanism is responsible for triggering
ngDoCheck even if there are seemingly no reasons to do that. The second part of the article answers the question why we need
ngDoCheck by demonstrating a few use-cases.
This article provides an elaborate answer to the one of the most popular Angular questions on stackoverflow with over 100k views which is Difference between Constructor and ngOnInit. The article gives a comprehensive comparison that highlights the difference in the usage and also taps into components initialization process.