Let me define first what change detection is, in simple terms any state change that needs to be propagated to UI and ice versa. Now the application state changes can happen in following ways which apparently all are asynchronous in nature.
- Events – Click, selection changed, keyup
- Remote API Ajax Calls(XHR)
- Timers – setTimeout(), setInterval()
Now for sure we know what how the application state change happens but there is still work of notifying Angular that state has changed and do the further magic. After all in the angular code we are still using Native API’s (like setTimeOut…) there are no angular specific API’s. So there is some thing called as Zone.js which does all the magic. You must have seen zone references whenever u see any error in the console window.
So what are zone and how do they work?
As one liner definition we can say that Zones are basically an execution context for asynchronous operations. Zones basically monkey patch these native methods and provide hooks for executing change detection. As soon as we embed
zone.js in our site, pretty much all methods that cause asynchronous operations are monkey-patched to run in a new zone. For example, when we call
setTimeout() we actually call
So now we know that zones have hooks for asynchronous operation but how does it marry with angular, answer lies in to the ApplicationRef class and source code below, it simplified version of the code….
ApplicationRef listens to NgZones onTurnDone event. Whenever this event is fired, it executes a tick() function which essentially performs change detection.
In Angular, each component has its own change detector. So in the component tree each component will have its own change detector. This allows us to control, for each component individually, how and when change detection is performed
Let us assume that in the component tree an event has been triggered in the Child 2.1 component. As mentioned earlier zones have hook in to these event handlers, zones execute the given handler and notify Angular when it is done, which eventually causes Angular to perform change detection. Now for angular change detection always flows from top to bottom. Change detection is also always performed from top to bottom for every single component, every single time, starting from the root component
This is much more predictable change detection than in Angular JS where it used to digest loop or cycle.
CD (change detection) gets stable after a one pass, if one of our components causes any additional side effects after the first run during change detection, then angular will throw an exception.
Now that we have talked @ Change detectors in Angular but what are these Change detectors, well these are not one fit all generic classes which does the change detection for all components. Angular creates CD classes at runtime for each component, which are monomorphic, because they know exactly what the shape of the component’s model is. Monomorphic are king of opposite of polymorphic which do not have any runt time overrides or sub types.
Angular also gives control of the change detection, if we wish to. We can tell angular to run CD only for the part of the component hierarchy tree. This can be achieved with 2 things
- Immutable data structures
Lets consider optimization through Immutability first, consider the followin component
GrandChildComponent uses as a child component, which has an input property details. We’re passing data to that component with GrandChildComponent own greatGrandChildDetails property. greatGrandChildDetails is an object with two properties. In addition, there’s a method changeGreatGrandChildData(), which changes the first name and last name of greatGrandChildDetails, No magic going on here.
The important part is that changeGreatGrandChildData() mutates greatGrandChildDetails, by changing its first and last name property. Even though that property is going to be changed, the greatGrandChildDetails reference itself stays the same.
Immutable objects cannot be changed and we have to change then we have change the reference.
In the above case Immutable.create is just sample API to create Immutable, you can use any library to create immutable objects. Now when I am changing the first Name property then it is going to return a whole new reference to the object.
this.greatGrandChildDetails !=== this.greatGrandChildDetailsNew from a reference perspective both are pointing to different references. If we use immutable objects in our Angular app, all we need to do is tell Angular that a component can skip change detection, if the input has not changed, let’s look at the great-grand-child component.
We can skip entire subtrees when immutable objects are used and Angular is informed accordingly.
ChangeDetectionStrategy.OnPush – This will inform Angular that our component only depends on its inputs and that any object that is passed to it should be considered immutable.
Now let’s talk @ Observables
Observables give us some guarantees of when a change has happened, unlike immutable objects, they don’t give us new references, instead, they fire events we can subscribe to in order to react to them.
Lets look at this example
We set the change detection strategy to OnPush, so change detection isn’t performed all the time, only when the component’s input properties change, the reference of addChildItemStream will never change, so change detection is never performed for this component’s subtree. This is a problem because the component subscribes to that stream in its ngOnInit life cycle hook and increments the counter. This is application state change and we want to have this reflected in our view. This is where ChangeDetectorRef comes to the rescue, it has an API markForCheck(), it marks the path from our component until root to be checked for the next change detection run, it simply iterates upwards and enables checks for every parent component up to the root.