Change detection at face value is the act of determining if something is the same as it was at a previous time. In the case of this article, the thing that has changed is a web browser's Document Object Model (DOM) and the detection is done by Angular 2. This is an important feature of any web application's view layer and Angular 2 provides developers with many performant and useful tools for detecting change.
Angular 2 works on the axiom that all changes happen asynchronously. This makes sense given that if the JavaScript never yielded to the browser the user's DOM use would be "blocked". This axiom is universal, it is not limited to Angular. In fact, the mechanism that lets Angular 2 monitor asynchronous browser actions is a standalone library called "zone.js".
About zone.js
Zone.js was inspired by Dart. The library effectively "monkey patches" all of the browser's native asynchronous calls. This provides consumers of zone.js the ability to detect, measure, and otherwise reason about code executed with zone.
Angular 2's change detection system is built on top of zone.js hooks. Once an asynchronous action completes, Angular 2 starts its change detection routine. This means traversing all of the nodes in the "component tree" always starting with the root node.
Readers experienced with Angular 1.x might start to panic. This mechanism seems a lot like $digest cycles in Angular 1.x. The systems might be similar but Angular 2 provides a number of tools that make this process significantly more performant. Angular 1.x digest cycles could handle roughly two thousand members before causing noticeable delays. Angular 2's system allows for hundreds of thousands of components to be checked in seconds or less, depending on CPU.
Change Detector Classes
To keep this process proficient Angular 2 keeps specials copies of each component's data properties. These copies are structured to be optimally compiled by the underlying just in time compiler (JIT). These copies not only include the current state but they also include the previous state. This provides Angular 2 all the information it needs to detect changes.
Developers do not have to do anything special for these change detector classes to be created. Angular 2 takes care of all of this behind the scenes resulting in lookup times that are 3-10x faster than Angular 1.x
Recap
Recapping the basic change detection flow:
- Asynchronous actions are the source of all changes
- Zone.js provides Angular 2 with information on when asynchronous actions have
completed - Angular 2 extends zone.js's system and turns it into a more formal framework
- Angular 2 augments all components with special changed detection objects
- When Angular 2 detects the completion of an asynchronous action it triggers
a change detection cycle - The change detection cycle walks the component tree from the root down to each
leaf checking the special change detection components
How Changes Are Detected
All of this has built up to when Angular 2 does change detection. There is still the question of how Angular 2 determines if something has changed. By default Angular 2 will check everything that could change, including object properties and array elements. Despite optimized change detection objects and a one-way data flow, large applications can potentially have a lot of values to check. Fortunately, Angular 2 provides developers with the power to choose how Angular 2 determines if something has changed.
In Angular 2 every component is decorated with metadata provided by @Component({ /* meta data */ }). There is an optional property in the meta data called changeDetection that lets developers specify how change detection will function. This is done by importing ChangeDetectionStrategy from "angular2/core". The ChangeDetectionStrategy object contains a number of different strategies for detecting changes.
Change Detection Strategies
There are several different strategies listed in the Angular 2 source but the two most documented are Default and OnPush. Default as stated earlier checks everything and does not need to be specified. OnPush works by comparing references.
In the Default strategy all object properties and array elements get checked during each change detection cycle. With OnPush object properties and array elements are only checked if their reference has changed. For example:
let data = {
value1: 'a',
value2: {
nestedValue1: 'A',
nestedValue2: 'B'
},
value3: 'b'
}
If the above data were passed as inputs to a component with the OnPush strategy and some JavaScript code changed data.value1 to something else, like data.value1 = 7, Angular 2 would not change the DOM. This is because the reference to data1 from the previous check would still be the same.
On the other hand, if the reference changed to something else, like data = 'hello world' Angular 2 would change the DOM. This is because the reference that data was holding has been changed.
Sometimes when using the OnPush strategy we might only want to change "part" of an object. Fortunately, the Angular 2 team has provided a mechanism for this any component can arbitrarily call this.cd.markForCheck() to force Angular 2 to check it. Beyond that, change detection can even be triggered manually with this.cd.detectChanges().
To Conclude
We've taken a deep dive into when Angular 2 performs change detection and some of the options as to how Angular 2 performs change detection. We've also looked at how the developer can exercise control over how Angular 2 detects change.