Through this article, you can gain some knowledge:

  • How do Angular DOM updates work?
  • What problem does change detection solve?
  • Learn more about change detection, its definition in Angular source code and its use in NG-Zorro
  • And a little personal insight

Angular DOM update mechanism

So let’s start with the simplest demo



When the button is clicked, we change the component’s name property and the DOM displays the changed value immediately, which seems “magical”.



What happens when you print innterText in the real DOM right after the element change statement, only to find that the value is still the same as it was when the value in the view has changed? If you’re wondering, join me in revealing the answer.

Let’s think about what just happened:

  1. Click on the button
  2. Values change

If you were writing this code in native JS, the view would not change when you click the button, whereas Angular does. If you know a little more about Angular, you’ll know that a library called zone.js does a layer of processing for all possible value changes, such as: zone.js

  • Events: Click, mouseover, mouseout, keyUp, keyDown, etc
  • Timer: setTimeout, setInterval
  • XHR: various requests

Angular also provides a way to disable zone.js.





After the zone is disabled, the view is not updated when we click the button again.

With curiosity, we found the key code for view updates in Angular source code



This time we call the method manually in code.





As expected! The view has been updated, and surprisingly, the innerText printed has also been updated!

At this point, we have concluded that DOM updates rely on tick() firing, and zone.js helps developers eliminate the need for manual firing.

With that in mind, let’s take a closer look at what’s going on behind Angular view updates.

2. Snooping on the secret of change detection

1. Start with a common Error

The ngOnInit of the child component changes the name value of the parent component





However, this is not always an error. For example, we remove the input property of the child component and refresh it to find that the same code works and the name of the parent component can be changed normally.



emmm… Lost in thought…

If you’re like me when I was learning Angular, you can search for this problem in StackOverflow, copy and paste code that you don’t know why it works, and then search and copy and paste it in StackOverflow later. And so on…

As time goes on, the CRUD savvy you become more and more unsatisfied with your stackOverflow programming self, and you start scouring the community, documentation, and forums for answers to your questions, but after reading their answers and articles, all you seem to know is something called change detection. But the exact cause of this bug is not clear, if you are as familiar as I am with the above experience, then continue to find out the truth!

2. What exactly is change detection?

When we change data in the Model, the framework layer needs to know:

  • What has changed about the Model
  • Where does the view need to be updated

React determines which parts of the Dom to update by comparing the new and old states of the Dom, rather than updating all the Dom. This is where Angular’s change detection works the same way.

The entire Angular app is a tree of components. It is not possible for a change in any one component to trigger updates for all components. This is inefficient and time-consuming. This is the purpose of change detection.

By Default (ChangeDetectionStrategy. The Default), the parent component change detection occurs, the child components will trigger the change detection.



(CD stands for changeDetection)

With each change detection, the new and old states are compared, and an error is reported if the results of two change detection (in the development environment) are inconsistent, for example:

Expression has changed after it was checked

This explains why changing the parent’s value in a child component is an error.

But! In both cases, we changed the value of the parent component in the child component. Only the first error is reported, and the second one is updated normally. If you are wondering what the real difference is, read on

3. The key of the problem is the Detection Sequence

Conclusion first:

  1. Update the binding properties of all child components
  2. Call OnChanges, OnInit, DoCheck, AfterContentInit lifecycle hooks for all child components
  3. Update the DOM of the current component
  4. Child components trigger change detection
  5. The AfterViewInit lifecycle hook that calls all child components

We won’t go into too much detail here (don’t wonder why this is the order, just remember that this is how Angular works. If someone wants to talk about Angular design in this section, feel free to leave a comment.)

In the first example, the parent passes in the input attribute name to the child, and the child updates the parent’s name attribute in ngOnInit, which means that the code is ** out of order (** operates on the first step in the second step of the order)!

<p>{{ name }}<p>
<child [name]="name"></child>
Copy the code

In the second example, the operator component also updates the parent component’s name property in ngOnInit, but because the parent component does not enter the name property in the child component binding, it does not break the change detection queue order.

<p>{{ name }}<p>
<child></child>
Copy the code

If the parent component is bound to the child component, whether in OnChanges, OnInit, DoCheck, An error is reported when the following code is executed in either of the declaration cycle hooks of AfterContentInit or AfterViewInit.

this.parentCmpt.name = 'child'
Copy the code

Ok, so we’ve seen the real reason for this error, but LET me remind you that this error will only be triggered in development, the enableProdMode() will be called in production, and change detection will go from 2 to 1, which is also described in the Angular source code.



Of course you can’t force production mode in your development environment because of this bug…

4. People often say that ChangeDetectionStrategy. OnPush?

ChangeDetectionStrategy defaults to Default, that is, the parent component’s CD will trigger the child component’s CD, but obviously in some cases we can judge by ourselves that some child components do not trigger when the parent component’s CD, and OnPush

Is an easy way for Angular to do things for developers.



In GIF form: Check out the link

The well-known Angular open source component library nG-Zorro uses a large OnPush strategy, which is one of the ways Angular optimizes performance.



Three, dig a little deeper

Angular assigns a component view to each component. ChangeDetectorRef is used to get the associated view.

export declare abstract class ChangeDetectorRef {
    abstract checkNoChanges(): void;
    abstract detach(): void;
    abstract detectChanges(): void;
    abstract markForCheck(): void;
    abstract reattach(): void;
}
Copy the code

1. Detach and reattach

Take a look at the GIF below. Components that are detached will not be inspected for changes.

Reattach allows a detached component to detect changes again.

2.markForCheck

Reattach will only reenable change detection for the current component, but will not work if the parent component has not enabled change detection, and markForCheck is a good way to solve this problem.

This can be seen in the ng-Zorro source code.

For example, when changing the active attribute of the NZ-Anchor-Link component in the NZ-Anchor component, since the ChangeDetectionStrategy itself is OnPush, markForCheck needs to be activated to enable detection again. See the github source code for details.

  • nz-anchor.component.ts
  • nz-anchor-link.component.ts

This is the case in a GIF. Notice the change before and after setting the MFC

3.detectChanges

This method is as straightforward as it sounds, triggering a change detection. Remember from the first example in this article, when we did not trigger the tick() manually, we did the same thing with detechtChanges().





Four, the last

At this point, I believe you have a basic understanding of Angular change detection. If you have any questions, feel free to discuss them in the comments section

In writing this article, I participated in a number of community articles and discussions. On the one hand, I felt that there were only a few articles about such an important concept in the Angular Chinese community. On the other hand, although the number of Angular developers in China is much smaller than React and Vue, they are still very enthusiastic about contributing their knowledge and insights to the Angular Chinese community. As a developer who has been using Angular for more than half a year, A deep sense of Google’s engineering aesthetic.

Big and elegant, this is my favorite Angular Web framework, thanks to the developers in the open source community

For the description of the wrong place, but also hope that the big guy criticism is done