In Part 1, I showcased the best practice in Angular change detection. Now I aim to delve into the rationale behind it. This topic is about Zone.js

What is change detection

Angular is a component-based framework. For a component, there are two parts. One part is the component class, which is all about state. Another part is the template. The bindings connect these two parts. Angular runtime creates the UI by rendering the template. When a user interacts with UI, event binding updates the state. Change detection compares the state before and the state after events, and updates the UI to reflect the state difference.

In the following demo app, there are two parts. One part is a Non-Angular world, another is an Angular world. They both share the same state, which is a global object. In the non-angular world, the count button updates the state, but it uses a low-efficiency timer constantly to check the state change to update the UI. In the Angular world, when the button also updates the state, Zone.js will automatically trigger a change detection cycle and update the UI.

Although two worlds share the same state, if you change the state in non-angular world, the angular world will not detect the change because it is event-driven change detection. If you change state in the Angular world, the angular world can detect the change, because the change detection is constant pulling.

What exactly Zone.js is doing?

Behind the scene, Zone.js performs a technique called monkey patching, which involves modifying the behavior of the browser APIs that respond to application events. This allows Angular to intercept these events after the application has initiated them, which triggers the change detection process to determine what changes have occurred.

Zone.js monkey patches the following

  • Event Listeners
  • setTimouts
  • Promises
  • XHRs
  • IntersectionObserver Basically, any browser API which takes a callback.

What is the relationship between Angular and zone.js

Angular creates NgZone on top of Zone.js. When an event handler finishes, it additionally calls applicationRef.tick() to trigger a change detection cycle. This means the change detection code of Angular, can be called by NgZone, or it can be called manually. Let’s remove zone.js and see if we can manually call the code ourselves. In the following app, I have removed the zone.js, and bootstrap the angular app with NgZone mock. Because zone.js is not there anymore, I also add applicationRef.tick() in the event handler.

After the change, the angular application still works. However, there are some drawbacks.

  • We need manually call it, which is cumbersome.
  • The call is right after state change, which is synchronous and can cause an infinitive loop.

What does applicationRef.tick() do? I will covert it in the next post.