Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Push in Angular Signals Array

Context:

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.

Question:

How can I push an item into a Signal array?

like image 726
Wandrille Avatar asked Dec 10 '25 16:12

Wandrille


2 Answers

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:

  • message: Signal<Message[]>
  • values: 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.

like image 82
Matthieu Riegler Avatar answered Dec 13 '25 10:12

Matthieu Riegler


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);
    });
  }
  
}
like image 38
tsimon Avatar answered Dec 13 '25 10:12

tsimon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!