Default Strategy
If you’re using Angular, you’ve probably heard about Zone.js. It’s an amazing library which does a lot of magic but feels somewhat unnecessary. The library’s only purpose is to monkey-patch events to detect all changes made inside your application and magically rerender the view.
The idea behind Zone.js is that you don’t have to worry about how and when to rerender the view. While Zone.js works pretty well, it does require a lot of JavaScript code to be shipped before your application even starts. Additionally, Zone.js doesn’t really know where each change occurs and needs to dirty check and refresh the entire component tree whenever a change is detected.
For example, when you click a button or update a property in any component, Zone.js will detect that change and check the entire component tree starting from the parent component down to each component. While Zone.js is well-optimized and this process is really fast, Angular ends up recalculating and rechecking a lot of unnecessary code because we don’t really know where the change occurred. All we know is that a click event or update event has been triggered somewhere, and the view might need to be rerendered somewhere.
A property is updated inside Component XThe entire component tree gets refreshed
OnPush Strategy
To optimize the change detection process, Angular introduced the ChangeDetectionStrategy.OnPush. This strategy checks only the component branch that was impacted by the change, down to the component where the change occurred.
✅ This approach saves unnecessary calculations and improves performance.
❌ However, using the OnPush strategy requires developers to have more knowledge of Angular in order to trigger change detection correctly. (manual markForCheck, async pipe, new instance of Input, …).
(Please note that this article does not focus on the workings of the OnPush strategy.)
A property is updated inside Component XThe component tree branch down to the impacted component gets refreshed
Signals Change Detection
A more optimized change detection mechanism is the reason why Angular needed a new system to detect changes more precisely. To accomplish this, Angular introduced a new primitive called “Signal”.
A Signal is a primitive that encapsulates an object, string, or other type of data. When the encapsulated variable is updated, the framework can precisely determine which View is impacted and needs to be refreshed.
As of the time of writing (Angular v16), the Signal component has not yet been introduced, and Signal change detection still relies on Zone.js. The following section is based only on the RFC.
If we simplify the concept of View to its component template, and update a signal listen to inside the template, we have the following schema:
signal gets updated, and component X listens to that signalonly component X is refreshed
When a signal is updated using the set , update or mutate method, any templates that listen to that signal are notified and the Views and only those Views are refreshed.
Only Signals that are listened to inside a template trigger a new cycle of change detection. If a Signal is only called inside a function, no rerender will happen on the UI side.The entire template gets refreshed, not only the Signal binding. This means that all functions and properties will get reevaluated.
Why update the entire view instead of each binding individually?
It’s a tradeoff that the Angular team has made. They decided to reduce the granularity of an application to its Views. Setting the dependency graph is expensive because it takes up memory. A View is already a small enough fragment of the UI to provide good rerender performance. (Remember that with Zone.js, Angular rerenders the entire application.)
What does VIEW mean ?
A View is a piece of code encapsulated inside a ng-template. Every structural directive, such as ngIf , ngFor creates a new VIEW. So if your template has a large data table created with ngFor , each row will have a separate view and only the row impacted by a signal change will be rerendered.
If we take our previous illustration and create two Views inside our component X, we will get the following:
signal isupdated, and VIEW 1 is listening to that signalonly VIEW 1 is updateded
👉 All of this will be working with and without Zone.js on signal based component in future version of Angular.
That’s it for this article! I hope you now have a better understanding of how signals will improve the performance of your Angular application in the near future. Get ready to start experimenting with signals, as the API is already available in Angular v16.
The future of Angular is looking very bright. 🚀
You can find me on Medium, Twitter or Github. Don’t hesitate to reach out to me if you have any questions.
Future of Change Detection in Angular with Signals was originally published in ngconf on Medium, where people are continuing the conversation by highlighting and responding to this story.