Angular Signals🚦: New Change detection strategy
The hype around Angular signals is real. If you are an Angular user, I hope, you already know the answer. Signal is going to be the new change detection strategy.
There are many articles available online now that are deep-diving into the Signals concepts. I suppose it’s also worth sharing my understanding of the concept.
Change detection: What is the current state and What will be the future?
Angular is currently relying on the Zone js for the change detection. Zone Js will monkey patch various built-in APIs to notify the change in the application. If a new Web API is introduced in JavaScript then Zone Js has to be updated by monkey patching the new API so relying on Zone Js is not a future-proof solution.
Another major setback with this approach is that there is no information about the component that is responsible for the change and what changed in the model. Due to this, to reflect the model changes in the DOM, the application has to traverse from top to down to identify and apply the changes. The component traversal can be customized by the OnPush
change detection strategy though.
Also, many users like to have zoneless applications and various experiments can be seen online.
To tackle these problems, Angular started to revisit its current change detection strategy which results in the signals concepts that provide fine-grained control over the component change detection.
Signals bring the below advantages to Angular.
- Updates only the components that interest the model value change by providing the information on the model change.
- Removes top-down component traversal. It only updates the component that uses the signal.
- No dirty monkey patching of global methods/APIs.
Building Blocks of Angular Signal
The below items are the major building blocks of the Angular signal.
- Signal
- Computed signal
- Effect
What is signal?
Signal is a lightweight wrapper around a value that notifies the value changes to the interested consumers. It comes with several methods that allow us to set, update, and mutate its value.
You can initialize the signal value by using the signal
method.
export class AppComponent {
count = signal(0);
}
set
method can be used to change the initialization value of the signal. The signal value is changed to a new value in the resetView
method.
export class AppComponent {
count = signal(0);
resetView() {
this.count.set(0);
}
}
update
method will help you to change the value based on the previous value.
export class AppComponent {
count = signal(0);
addView() {
this.count.update(val => val + 1);
}
resetView() {
this.count.set(0);
}
}
mutate
method will allow you to mutate the signal in its place and notify its dependencies.
Computed signal properties
Another exciting part of the signal is the computed signal properties. The computed signal is a long-requested feature in Angular and, introducing it as a part of the signal has significant advantages for both developer experience and performance of the application.
The computed
function can be used to create a computed signal property.
export class AppComponent {
count = signal(0);
revenue = computed(() => this.count() * 0.5);
addView() {
this.count.update(val => val + 1);
}
resetView() {
this.count.set(0);
}
}
The computed signal property has the following behavior.
- Computed properties are lazily initialized. The computation will not happen until the value is accessed.
- It shuffles the dependency graph dynamically based on the dependency signal change. Angular is being cautious in the dynamic dependency graph since the frequent setting of the dependency graph will come at a cost.
Effect
The effect
function allows you to listen to the signal changes and act based on them. The effects can be used to handle some use cases like,
- Custom updating DOM after one or more signals are changed.
- Trigger network requests.
The effect will auto-subscribe itself based on the signals used in its context. For example, the below list of effect methods used in the constructor will be called in various signal changes.
export class AppComponent {
count = signal(0);
user = signal(0);
constructor() {
effect(() => {
console.log('Empty effect method');
});
effect(() => {
console.log('User effect invoked ' + this.user());
});
effect(() => {
console.log('User and View effect invoked ' + this.user() + ' ' + this.count());
});
effect(() => {
console.log('Count effect invoked ' + untracked(this.count));
});
}
addView() {
this.count.update(val => val + 1);
}
userChange() {
this.user.update(val => val + 1);
}
resetView() {
this.count.set(0);
}
}
This effect method will be called only once since no signal property is used in its context.
effect(() => {
console.log('Empty effect method');
});
The below effect will be called whenever the user
signal is changed.
effect(() => {
console.log('User effect invoked ' + this.user());
});
This callback will be invoked when either the user or count signal is changed.
effect(() => {
console.log('User and View effect invoked '
+ this.user() + ' ' + this.count());
});
This effect method will be called when the very first time the count signal is set then it will become inert since the count
is wrapped in the untracked
method. This method will help you execute the signals inside a non-reactive context.
effect(() => {
console.log('Count effect invoked ' + untracked(this.count));
});
Using signals in the templates
The angular team chooses the getter function approach to use signals inside the templates.
<div>
<span>Unique users: </span>
<span>{{users()}}</span>
</div>
The team sees some clear advantages in using the getter function approach like it implies that it is meant for read operation and maintains consistency between the component and template files.
Over the years Angular developers learned not to use functions inside the template since it would have a considerable performance impact if the method did some heavy work. But it's not the case with signals since they are built to do computations in a minimal way.
Upcoming works — signals
Well, signals in Angular is a bit large topic and I just covered a very small part of it. The angular team is still working on introducing several changes in the input/output, control flow and defer loading to improve the developer experience and ability to add Zone-less(Fully signal)-based applications in the future.
If you find this article helpful, please follow me here and on twitter for more content like this.
Happy coding…