Notes on Angular Signals.
Table of contents
Open Table of contents
Introduction
Signals provide a new way for our code to tell our templates (and other code) that our data has changed. It improves Angular’s change detection and makes our code more reactive.
Observe the following example where z
will not react to changes in x
or y
.
let x = 5;
let y = 3;
let z = x + y;
console.log(z); // => 8
x = 10;
console.log(z); // => 8
One of the reasons, we use frameworks/libraries like Angular or React is that we want a reactive user interface. We want to react to changes.
For example, when user changes quantity for an item added into the cart, we want the code to react and recalculate the cart total.
Current options to react to such changes are creating Getters
or Custom Events
. However, this works in the same component.
// Getter.
// When the quantity changes the change detection kicks in
// and the template gets the new extended price.
item = "Product XYZ";
price = 19416.13;
quantity = 1;
get exPrice() {
return price * quantity;
}
// Event
item = "Product XYZ";
price = 19416.13;
quantity = 1;
exPrice = price;
onQuantitySelected(qty: number) {
exPrice = price * quantity;
}
What if we want the updated price in a different component? That’s where signals comes in!
// Signals
item = "Product XYZ";
price = 19416.13;
// 'quantity' is marked as a signal.
quantity = signal(1);
// Here, computed signal reacts and recalculates 'exPrice'
// when the 'quantity' signal changes.
exPrie = computed(() => this.price * this.quantity());
Here is our rewritten first example using signals.
const x = signal(5);
const y = signal(3);
const z = computed(() => x() + y());
console.log(z()); // 8
x.set(10);
console.log(z()); // 13
Create a Signal
- We can create a signal using the
signal
constructor function. - We can also set an optional type where type can be string, number, array, object, or any other type.
- Signal requires a default value.
quantity = signal<number>(1);
// Examples
qtyAvailable = signal([1, 2, 3, 4, 5, 6]);
selectedVehicle = signal<Vehicle>({ id: 1, name: "Hector", price: 19234.13 });
vehciles = signal<Vehicle[]>([]);
Read a Signal
We read a signal by calling its getter function. Below are some examples.
constructor() {
console.log(this.quantity());
}
<option *ngFor="let q of qtyAvailable()">{{ q }}</option>
<div>Vehicle: {{ selectedVehicle().name }}</div>
<div>Price: {{ selectedVehicle().price }}</div>
Set / Update a Signal
// Replace the value.
this.quantity.set(qty);
// Update the value based on the current value.
this.quantity.update(qty => qty * 2);
Computed Signal
- The
computed
function creates a new signal that depends on other signals. - A computed signal recomputes when its dependent signals change.
- A computed signal is read only.
- The computed value is memoized, meaning it stores the computed result.
totalPrice = computed(() => this.price() * this.quantity());
// Here,
// `computed` is a creation function.
// arrow function is a computation function.
Effect for Side Effects
Use an effect
if you want to run some code when a signal changes.
effect(() => console.log(this.selectedVehicle()));