Angular Signals - Quick introduction
When to use Signals. Are they better than RxJS?
Angular signals, the synchronous reactivity
Signals help the developer to handle the state of the application allowing Angular to optimize the rendering updates.
They are wrappers around values that communicate to consumers the state change.
Signals :
- impact the change detection in Angular
- allows to communicate to the template that the data has changed
- add reactivity to the code, reducing the need for RxJS
Here an example of initialization:
const firstValue = signal(12.23); // or const firstValue : WritableSignal<number> = signal(12.23);
const secondValue = signal(23.23);
const sum = computed(()=>firstValue() + secondValue())
firstValue.set(0);
// automatically recalculate sum -> sum() => 23.23
signals are getter functions
firstValue() means the the value of firstValue
.set() changes the value for a writable signal
.update() compute a new value from the previous one:
firstValue.update(previousValue=> previousValue *2)
computed signal
It derives his value from other signals, it's not writable. It's lazy calculated. When one of the values computed changes, it is flagged for recalculation. Next time it will read, it will be recalculated.
In OnPush components, the update signal will marks the component for update.
fullName = computed(() => `${this.firstName()} ${this.familyName()}`);
and in the template:
Welcome, {{fullName()}}
signal as input - from 17.1.1
Signals can be declared as @Input()
without an explicit decorator.
You can for example:
firstName = input<string>(); // string|undefined
lastName = input.required<string>(); // string mandatory
age = input(0); // number with default value
year: InputSignal<any,number> = input() // explicit declaration
In the HTML you can use the signals:
<app-input-signals
[firstName]="'Marco'"
[lastName]="'Molteni'"
[year]="2024" ></app-input-signals>
You can pass parameters to input, e.g.:
city = input<string|undefined>(undefined, {alias: 'locality'})
effects (developer preview)
Consumers can be notified when signals values change.
constructor() {
effect(() => {
console.log(this.firstName());
})
}
With this code a message will be logged every time the value (this.firstName) will change.
Effects are executed at least once and are asynchronous.
The Angular team don't recommend effects for propagation of state changes.
The effect requires the injection of a context, for this reason it can be created:
- in the constructor
- assigned to a field
or passing the injector in the properties:effect(() =>{...}, {injector: this.injector});
Should I use signals for every property?
No, only states that will change and will be reflected in the UI (template).
Advantages compared to RxJS
Signals have functionalities that can be replicated with RxJS operators.
signal(), compute() and effects() have similar features to RxJS Subscriptions and Observables, their big advantage is that they don't require any subscription to access the value ... so they are easier to implement.
They are destroyed automatically, so signal don't require an unsubscribe, reducing memory risks in the implementation.
With some potential advantages linked to the caching mechanism and signals doesn't use zone.js
When to use signals and when to use RxJS
The 2 are reactive with a big difference that :
- signals are synchronous
- rxjs is asynchronous
For this reason signals are not a replacement for RxJS! If you are waiting for an undetermined (in quantity or time) event, you have to continue to use RxJS. If the event is determined and can be synchronous, signals could reduce the complexity of your code.
The long term goal of the Angular team is to make RxJS optional. This is a step in that direction.