Understanding Angular Change Detection Strategy
As an Angular developer, sometimes we might be curious about how Angular detects changes in the data (model) and then renders them to the view. This is called Angular Change Detection Strategy. This time we will discover that and also learn to pick the best one for each use case.
What is Change Detection?
The key technique of change detection is to execute checks against two states: the current state and the new state. If one of these states differs from the other, something has changed, meaning the view needs to be rendered.
Change Detection in Angular?
Generally change detection in Angular have two parts:
- Developer update the data model
- Angular re-renders the updated data model into the view
During the Change Detection Cycle, Angular searches for all bindings, re-executes all expressions, compares them to the previous values and propagates the change to the DOM Elements if a change is identified.
Let's look at below picture:
Angular Application is basically made up of what is known as a component tree. On the figures above you can see ForgotPasswordComponent is the child of LoginComponent, so when we as developers make changes to the data model on ForgotPasswordComponent the change detection will be triggered.
On the above image you can see that after change detection kicks in on ForgotPasswordComponent, change detection goes through every component in the component tree. The flow would be like this:
- Every component in the component tree is checked to see if the model it depends changed. Change detection will start from top to bottom.
- If Angular found the other model also changed it will update the component and re renders the component view.
The way Angular runs the change detection, starting at the top and working its way down, makes the system more predictable and performant. This is also can happen because of the unidirectional data-flow that Angular implemented. Meaning that the view cannot be updated once it has been composed. It will take a whole change detection loop again to render the view with updated changes. How Angular enforce with unidirectional data flow also helps us to troubleshoot inconsistency bugs. I will not explain further about unidirectional data flow in this article, and we will proceed with the Angular Change Detection Strategy.
Angular Change Detection Strategies
Angular Framework provides two strategies to run change detection:
- Default
- OnPush
Default Change Detection Strategy
By default, without configuring the change detection strategy to use, Angular will use ChangeDetectionStrategy.default
change detection strategy. With this strategy, Angular will have no assumption on the component’s dependency and will check every component from the component tree from top to bottom every time an event triggers change detection on browser events, timers, XHRs, and promises. This type of behavior can have a negative impact on performance for a large application that has many components.
OnPush Change Detection Strategy
We can change the detection strategy from default to ChangeDetectionStrategy.OnPush
by adding changeDetection
property on @Component
decorator.
With OnPush strategy we can skip checks for components that uses OnPush strategy and all of its child component.
With this strategy, Angular will only update the component if one or more of these conditions happens:
- The input reference changed
- An event from the component or one of its children.
- Async pipe linked to the template emits a new value.
- Manually triggered the change detection
We can look for more details below.
Input Reference
By default, angular will run the change detector every time @Input()
data is changed or modified. But with OnPush
strategy, the change detector is only triggered if the data passed on @Input()
has a new reference. This is why using immutable objects is preferred, because immutable objects can be modified only by creating a new object reference. With immutable objects plus OnPush
change detection strategy, we can guarantee that OnPush
change detection is triggered for each change.
Event Handler is Triggered
With OnPush
change detection strategy, every time component or one of its child triggers an event handler, change detection will be triggered for all components in the component tree.
Be mindful that the following actions do not trigger the change detection with OnPush
change detection strategy:
setTimeout
setInterval
- Promises
- Any RxJS observable subscription
Async Pipe
Angular built-in AsyncPipe
internally calls markForCheck
each time a new value is emitted, which will be explained later on how to manually trigger the change detection. You can check the source code here. With how the AsyncPipe
is implemented, AsyncPipe
works automatically with OnPush
change detection strategy.
Angular Built-in AsyncPipe Implementation
So how do we trigger the change detection without the AsyncPipe
? We can trigger the change detection manually with the below methods.
Trigger Change Detection Manually
We can trigger change detection manually by using detectChanges()
, ApplicationRef.tick()
, and markForCheck()
that we mentioned earlier on AsyncPipe
.
detectChanges()
onChangeDetectorRef
which will run change detection on this view and its children.ApplicationRef.tick()
which triggers change detection for every components in the component tree (basically whole application)markForCheck()
onChangeDetectorRef
which marks allOnPush
ancestors to be checked once, either on the current or next detection cycle. This method does not trigger the change detector directly.
Summary
Angular Change Detection is powerful enough to provide us with two options to choose from, either with the default strategy or the OnPush
strategy. Basically with OnPush
strategy, you get more performance because Angular doesn’t need to check or guess on every change detection cycle. It’s also making you embrace immutability with input reference detection.
Learn More about Angular
Want to learn more about Angular? We have a few articles that you can read here on our blog.
Join Us
Scalability, reliability and maintainability are the three pillars that govern what we build at Halodoc Tech. We are actively looking for engineers at all levels and if solving hard problems with challenging requirements is your forte, please reach out to us with your resumé at careers.india@halodoc.com.
About Halodoc
Halodoc is the number 1 all around Healthcare application in Indonesia. Our mission is to simplify and bring quality healthcare across Indonesia, from Sabang to Merauke. We connect 20,000+ doctors with patients in need through our Tele-consultation service. We partner with 3500+ pharmacies in 100+ cities to bring medicine to your doorstep. We've also partnered with Indonesia's largest lab provider to provide lab home services, and to top it off we have recently launched a premium appointment service that partners with 500+ hospitals that allow patients to book a doctor appointment inside our application. We are extremely fortunate to be trusted by our investors, such as the Bill & Melinda Gates Foundation, Singtel, UOB Ventures, Allianz, GoJek, Astra, Temasek and many more. We recently closed our Series C round and In total have raised around USD$180 million for our mission. Our team works tirelessly to make sure that we create the best healthcare solution personalised for all of our patient's needs, and are continuously on a path to simplify healthcare for Indonesia.