With Angular 16, we can now use Signals.
But it doesn't seem clear to me how to do a simple push into an array.
The documentation suggests from Angular Signal official doc:
When working with signals that contain objects, it's sometimes useful to mutate that object directly. For example, if the object is an array, you may want to push a new value without replacing the array entirely. To make an internal change like this, use the .mutate method
So it would be for example:
messages = signal<Message[]>([]);
ngOnInit(): void {
const newMessage = ...
this.messages.mutate(values => values.push(newMessage));
}
But it seems pretty weird to refer 2 times to the list. The first time with this.messages and the second one with values.
How can I push an item into a Signal array?
You should be using update (instead of mutate that has been remove in v17).
Signal expect to be immutable (if you don't provide a compareFn when creating them).
messages = signal<Message[]>([]);
ngOnInit(): void {
const newMessage = ...
this.messages.update(values => {
return [...values, newMessage];
});
}
To sum up the types we are working with here:
Signal<Message[]>Message[]When you call update() you're invoking the method on a Signal. The signal here is the reactive primitive not the value itself.
You call that value with : messages(). This is signals 101.
Cf the definition of signals by Angular:
const SIGNAL = Symbol('SIGNAL');
export type Signal<T> = (() => T)&{
[SIGNAL]: unknown;
};
So when you call update, you're actually telling the Signal to update its value.
Signal.set() just accepts a value to set so you're changing the reference, while Signal.update() takes a callback to update and you should be returning a new reference.
To expand upon what Matthieu noted, the magically awesome mutate() method was removed from the public API in the Angular 17 release. See this commit for more information about why it was removed.
It is important to note that both set() and update() rely upon reference equality for change detection, so calling array.push() will not change the value. You can try the following for
The following component will demonstrate how signals behave with array (and presumably objects as well):
import {
Component,
signal,
computed,
WritableSignal,
} from '@angular/core';
@Component({
selector: 'app-root',
template: `
Array Value: {{ computedDisplay() }}
<div>
<button
(click)="appendValue()">
Append value
</button>
<button
(click)="appendAndShow()">
Append and show
</button>
<button
(click)="showWithUpdate()">
Show only (using update())
</button>
</div>
`,
standalone: true,
})
export class AppComponent {
private count = 1;
private arraySignal: WritableSignal<string[]> = signal([]);
public computedDisplay = computed(() => {
return this.arraySignal().join(', ');
});
public appendValue() {
this.arraySignal.update((arr: string[]) => {
arr.push(`Update ${this.count++}`);
return arr;
});
}
public appendAndShow() {
this.arraySignal.update((arr: string[]) => {
arr.push(`Update ${this.count++}`);
return arr.slice(0);
});
}
public showWithSet() {
this.arraySignal.set(this.arraySignal().slice(0));
}
public showWithUpdate() {
this.arraySignal.update((arr: string[]) => {
return arr.slice(0);
});
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With