Angular 2 Change Detection: “Seeing” It in Action



The Angular 2 change detection system is somewhat of a black box: you update some variables in the model, and the components update automatically. Thoughtram, Victor Savkin, and other websites have written some excellent posts explaining change detection (which we reference in this post). Often, these posts present a series of graphs and diagrams that illustrate how the detect-and-update machinery works theoretically. However, in the spirit of “programming by poking,” we will attempt to match some of the theories with observable behavior using the following Angular 2 demo app:

Loading…

Code for this demo can be found in this plunker or this github repo.

About the demo

Surface level functionality

The above app renders a binary expression tree for an algebraic expression of the form

expression-tree-expression

where the operator times could either be plus, minus, or times. The numbers are restricted to non-negative integers less than 10.

If you play around with the expression tree app for a bit and change the operators and numbers, you will see the nodes flash. Each of these nodes is rendered by its own Angular 2 component, and these flashes correspond to certain change-detection-related events. (We will discuss these events in more detail in the next section.) Of course, the events triggering these flashes occur in such rapid succession that the nodes seem to all light up at once. For this reason, the demo provides controls to record and replay the flashes one step at a time, as they occurred in real time.

The following diagram (Figure 1) summarizes the basic functionality we have described so far:

Figure 1. Explanation of the playback features and an explanation of the node components.

Exposing Angular 2’s change detection system

To reiterate what was said in the introduction, our aim is to match descriptions of Angular 2’s change detection system to observable behavior. The expression tree app’s implementation lends itself to this purpose in a couple ways.

First, the flashes provide a visual log of the change detection system at work. Angular 2’s component lifecycle hooks make this possible. These hooks get called at key points during the change detection process, giving us a programmatic window into the change detection process as it unfolds. For example, the node components trigger a green flash when ngOnChanges is called, a blue flash when ngDoCheck is called, and a purple flash when ngAfterViewChecked is called. An additional yellow flash is emitted when the component’s internal representation of the expression (i.e., the expression model) gets updated.

Second, although it appears the app is rendering an expression tree, it will be much more helpful to set the numbers, operators, and expressions aside and look at the tree as a literal component tree. (If you are unfamiliar with the idea of a component tree, it might be worth reading the aside below.) If you remember from the previous section, we mentioned that each node is rendered by its own component. The component rendering the topmost node is, in fact, the parent of the components rendering the topmost node’s child nodes; the components for the child nodes are the parents of the components rendering their child nodes; and so on. Change detection is often described as traversing the component tree.

A few experiments

When change detection is performed

Let us try to verify some basic change detection behavior. First, we will experiment with the triggers that initiate change detection. As Pascal Precht explains in his article, Angular 2 assumes that any model changes are the result of asynchronous events. (This is certainly the case for our expression tree app; the expression model is only updated when the user clicks and changes the values of the drop-down menus.) Thus, after any asynchronous event, change detection is triggered.1

To start off, let us click the record button. Notice that as soon as we do so, the nodes flash and the log is filled with messages. Change detection was just triggered. This behavior verifies Precht’s explanation: the record button itself is a component, it has a mousedown handler attached it, and our click triggered an asynchronous event, which caused Angular 2 to check for changes.

Admittedly, it is undesirable that our demo app works like this. Ideally we would just record the change detection cycles spawned by working with the expression tree components, but we have opted to just live with this side effect of clicking record. As we move forward with the next experiment (and as you do your own), just remember where this initial change detection cycle—with its initial messages and flashes—comes from.

Component tree traversal

Precht’s same article states, “Change detection is also always performed from top to bottom for every single component, every single time, starting from the root component.” Let us try to verify this idea as well. With the demo app in its initial state, hit record, change the leftmost number from three to five, and then hit record again to end the recording. Now hit the play button. We will skip all the flashes that were triggered by hitting the record button by scrolling down and clicking on the first log message that says, “Expression update: 3 -> 5” (see Figure 3).

Figure 3. The log message for the first model-change event.

Use the right playback arrow to step forward through the flashes. Notice that after the model changes settle (i.e., after all of the yellow messages), the change detection system follows a depth-first search pattern. The root node first flashes green as Angular 2 detects changes to the root’s rendered model and calls ngOnChanges. It then flashes blue as Angular 2 calls ngDoCheck and allows the root node to detect additional changes. The flashes then travel along the leftmost child nodes before going to the right. As the downward traversal unwinds, purple flashes are emitted, indicating ngAfterViewChecked has been called and change detection has concluded for the component subtree rooted at that node. Again, the description nicely matches what we see.

Diving deeper

What other experiments might we run? For one, we might consider how the entire component tree lights up on each change detection cycle. This behavior indicates that—out of the box—the change detection system performs an expensive and exhaustive search across the components. Luckily, we can pare this search down by marking our components as “on push” or by tweaking our inputs to be observables. We could try implementing one of these and see how the flash pattern changes. We could also experiment with detaching a component from change detection. There are many opportunities “poke” at Angular 2’s change detection system. So roll up your sleeves, make a fork of the expression tree app, or perhaps take a look at the PeekABooComponent in the official documentation, and start experimenting.


1. How does Angular 2 know when asynchronous events occur? This topic is best left to another article. (Again, there are some good ones out there.) However, to explain at a cursory level, Angular 2 uses a thing called a “zone” to monkey patch the browsers asynchronous methods (i.e., setTimeout, addEventListener) and keep track of when and where they are called. Thoughtram, again, has another great article on this topic.

12 Comments

  1. I thought I’m gonna see it in action , but looks like I have read the whole article.

  2. Hi Milad. That is a fair criticism. I suppose the aim of this post was to create a visualization of more global change detection behavior (for example when it is triggered and how it travels the component tree). As far as seeing the internals of Angular 2 change detection—just as the people at Angular University suggest—nothing beats walking through the code with the debugger.

  3. In the log the blue flashes appear as ngAfterViewChecked instead of ngDoCheck

  4. Hi David. Thank you for reading the post close enough to catch that. It should be fixed now.

  5. There’s certainly a lot to learn about this subject.
    I like all of the points you’ve made.

  6. Jason, thank you for the feedback. I hope it helped.

  7. Asking questions are actually good thing if you are not understanding something totally, however this post gives pleasant
    understanding even.

  8. Excellent post. I used to be checking continuously this
    weblog and I’m impressed! Extremely useful information specifically the closing part 🙂
    I take care of such info a lot. I was seeking this certain information for a very long time.
    Thank you and best of luck.

  9. Useful info. Fortunate me I discovered your website unintentionally, and I am shocked why this accident did not happened earlier!
    I bookmarked it.

  10. Why does the root flash green when its expression is updated? I thought NgOnChanges is called only when Input changes are detected?

  11. Most of all recent programs offer this.

  12. Tudo isso pode te ajudar a passar em concursos!

Your email address will not be published.