Understanding Change Detection

Over the past eight months, I’ve dedicated most of my free time to reverse-engineering Angular, with change detection emerging as the area that captivated me most. In my view, it’s the cornerstone of the framework, handling all the visible work such as DOM updates, input bindings, and query list refreshes. That deep dive produced a series of detailed articles covering the core concepts and implementation nuances of the mechanism. This piece brings those articles together with a brief rundown of what each one covers. By the time you’ve gone through them, change detection should feel far less mysterious.

The following list includes five comprehensive articles—plus one additional piece—that should push your understanding of Angular’s change detection well beyond its current limits. Because each installment builds on the material from the one before it, I suggest reading them in the sequence presented.

Angular’s $digest is reborn in the newer version of Angular

This piece draws a parallel between the $digest mechanism in AngularJS and the change detection process in modern Angular. It lays out why both frameworks need this capability and demonstrates how they’re constructed on the same foundation of dirty checking. From there, it offers examples of how Angular’s lifecycle hooks can serve as a stand-in for AngularJS’s $watch. It also highlights a key shift: Angular enforces a unidirectional data flow moving top-down, and the article explores the rationale behind this design, its advantages, and the architectural constraints it introduces. This is particularly valuable for seasoned AngularJS developers planning a migration.

A gentle introduction into change detection in Angular

This article offers a more approachable introduction to the change detection engine. It gives you a big-picture view of the parts and processes involved: the internal structures used to model a component, the function of bindings, and the steps taken during each cycle. It also covers zones, showing precisely how this mechanism makes automatic change detection possible in Angular.

Do you still think that NgZone (zone.js) is required for change detection in Angular?

This write-up digs into how NgZone is built on top of the zone.js library and clarifies its position within the framework. Despite what many assume, NgZone isn’t an integral part of change detection itself—it’s what kicks off the process. The article starts by showing how Angular can carry out change detection and rendering even without NgZone or zone.js, then moves on to illustrate the added value NgZone provides and how it works internally. A significant portion is dedicated to explaining common public API members like isStable, onUnstable, and onMicrotaskEmpty. It wraps up with a discussion of a frequent pitfall: missed change detection when relying on third-party libraries such as GoogleAPI.

Everything you need to know about change detection in Angular

For anyone seeking a firm grasp of how change detection operates, this article is essential reading. It delivers a high-level explanation of the engine’s design, complete with useful links for deeper exploration. The discussion opens by introducing the internal representation of a component called a View, clarifying that change detection runs against these views. It then enumerates the full sequence of operations executed during change detection, covering view state updates, rendering, input binding processing, and lifecycle hook calls. Finally, it breaks down the ChangeDetectorRef public API—such as detach, detectChanges, and markForCheck—and offers brief examples of how to use them.

The mechanics of DOM updates in Angular

This article takes a close look at the implementation specifics behind synchronizing application models with the DOM—commonly referred to as one-way data binding or DOM rendering. This operation sits at the heart of change detection, as it’s what actually makes component changes appear in the browser. The piece begins by revealing more about the View concept, including View Factory and a few key View Node types. It then walks through how interpolation or input binding configures DOM updates for these nodes during change detection.

The mechanics of property bindings update in Angular

In the same spirit as the DOM update article, this installment explores how input bindings for child components and directives get updated under the hood. It introduces the notion of a Binding Definition and clarifies its role in the change detection flow. Next, it shows how the compiler generates these binding definitions when it encounters template syntax for property bindings. To round things out, it outlines a step-by-step process for running change detection on View Nodes and refreshing input properties on child components and directives.

Clearing Up Common Misconceptions

Below is an extra set of articles that address recurring points of confusion about change detection frequently seen on Stack Overflow.

He who thinks change detection is depth-first and he who thinks it’s breadth-first are both usually right

This article tackles an interesting debate: does Angular check sibling components first (breadth-first order) or child components (depth-first order)? It reveals how Angular invokes lifecycle hooks on sibling components before performing the actual checks on them, and explains how this sequence can mislead you into the wrong conclusion.

Do you really know what unidirectional data flow means in Angular

Here, the focus is on distinguishing two-way data binding from unidirectional data flow. It illustrates how Angular’s approach to updating input bindings differs from AngularJS and discusses where that difference carries practical significance.

Everything you need to know about the `ExpressionChangedAfterItHasBeenCheckedError` error

This piece sheds light on one of the most frequent and least understood errors in the Angular ecosystem. While many developers perceive it as a flaw, it’s actually a deliberate design choice aimed at improving performance by restricting change detection to a single pass instead of AngularJS’s multiple $digest cycles. The article explains how raising this error helps avoid mismatches between model data and the UI, preventing outdated or incorrect information from appearing on screen. It’s split into two main sections: the first investigates what triggers the error, and the second suggests ways to resolve it. It also addresses why this error doesn’t surface in production mode.

If you think `ngDoCheck` means your component is being checked

This article unpacks a common puzzle: why the ngDoCheck lifecycle hook fires for components using the OnPush strategy even when their input bindings haven’t changed. It highlights the often surprising reality that hooks run on child components when the parent is undergoing checks, and shows how this mechanism causes ngDoCheck to trigger even without an obvious reason. The latter half of the article explains the purpose of ngDoCheck by presenting several practical use cases.

The essential difference between Constructor and ngOnInit in Angular

This article delivers a thorough response to one of Angular’s most-asked Stack Overflow questions, with over 100k views: Difference between Constructor and ngOnInit. It provides a detailed comparison highlighting the differing use cases and also touches on how components get initialized in the first place.