The change detection in Angular framework is unidirectional and top-down (unlike the digest cycles in AngularJS). Whenever an async event like a DOM event, timer callback, XHR handler gets triggered, application code normally updates the state(or model) in these callbacks. Angular with the help of zone.js detects these async events and automatically kicks in the change detection(*). It checks for the state changes traversing the UI tree starting from the root component and makes the necessary DOM updates to reflect the updated state. State can be of any data structure (arrays, sets, maps, objects, primitives) that is wired up as property bindings in the component templates.
Angular supports two types of change detection strategies:
In the default change detection mode, Angular checks for changes for every component in the UI. These checks are much faster than digest cycle checks in AngularJS because of the way Angular generates these VM friendly change detector classes (when the code is pre-compiled with Ahead of Time(AOT) compilation).
Example:
Here is a grid containing several cells (all are angular components). Clicking on any cell changes the application state that is relevant (only) to that cell. An yellow border appears around the cell whenever Angular runs change detection on it.
Cell-Flip - Default Change Detection - Plunker_import { Component, ChangeDetectionStrategy } from '@angular/core'; import { IGrid, ICell } from…_embed.plnkr.co
Clicks in Action (gif):
As you can see in the below gif, clicking on any cell causes Angular to run the change detection on all the cells.
https://embed.plnkr.co/mx3ZnfhfUtQECb4av3MP/
While default change detection gets the job done without any additional complexity in the code, it will become performance bottleneck in complex Single Page Applications when lot of events are flowing through the system.
OnPush Change Detection
With OnPush change detection strategy, Angular will skip the change detection for a component as long as the references to the inputs do not change (immutable). To update the state, instead of mutating the objects, application code has to return new references. This option can effectively be used to make Angular skip the change detection logic for the large UI subtrees by making their inputs immutable.
This approach is equivalent to utilizing the “shouldComponentUpdate()” life-cycle hook in React to cut down unnecessary render cycles.
Example:
Here is the modified version of the above example with OnPush change detection.
Cell-Flip - OnPush Change Detection - Plunker_import { Component, ChangeDetectionStrategy } from '@angular/core'; import { IGrid, ICell } from…_embed.plnkr.co
// Instead of mutating the flip status of the clicked cell,// a new object is created with the updated status and replaces the // original cell in the list.
let updatedCell = Object.assign({}, this.currentState.cells[index], {flip: !this.currentState.cells[index].flip});
this.currentState.cells = [...this.currentState.cells.slice(0, index),updatedCell,...this.currentState.cells.slice(index + 1)];
Clicks in Action (gif):As you can see in the below gif, clicking on a cell causes Angular to run the change detection ONLY for that particular cell. Change detection checks were skipped entirely for all other cells.
https://embed.plnkr.co/qvQIkHaoN51AdbzwGK81/
This approach obviously makes the change detection cycle lot more performant however introducing immutable state objects will bring its own complexity and challenges.
References:
Thanks @dryzhk0v for reviewing the draft and providing feedback.