1

Considering the following Angular Signals example, I'm somewhat confused about when I should use the Signal API, like mutate() and update(), when the Array API, such as forEach, is sufficient to trigger reactivity and update the UI/template? What is the difference here? Used Angular Version: 16.0.1

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [CommonModule],
  template: `
    {{usersList() | json}}
  `,
})
export class App {
  usersList: Signal<any> = inject(UserService).getUsersList();
  constructor() {
 
  }
}

bootstrapApplication(App);

@Injectable({
  providedIn: 'root',
})
export class UserService {
  private users = signal([
    { id: 1, name: 'Peter', country: 'USA' },
    { id: 2, name: 'Party Boy', country: 'USA' },
    { id: 3, name: 'John Connor', country: 'USA' },
  ]);

  constructor() {
      window.setTimeout(()=>{
        this.changeUsersListWithoutMutate();
      }, 3000),

      window.setTimeout(()=>{
        this.changeUsersListWithMutate();
      }, 5000)
  }

  changeUsersListWithoutMutate(){
    this.users().forEach((item) => {
      if (item.id === 1) {
        item.name = "changeListWithoutMutate";
      }
    });
  }

  changeUsersListWithMutate(){
    this.users.mutate((data) => {
      data.forEach((item) => {
        if (item.id === 1) {
          item.name = "changeListWithMutate";
        }
      });
    });
  }

  getUsersList() {
    return this.users;
  }
}
Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134
Okyo
  • 357
  • 4
  • 17
  • 1
    I would add that anytime you can directly manipulate a signal using the signal API, do it there. Anything else has the chance to inadvertently trigger change detection somewhere else and when you can limit changes to only the precise piece that you want, that is almost always going to be the better solution. – Myles Morrone Aug 21 '23 at 23:24
  • 1
    Here is StackBlitz with your code, but just 1 line is added: "changeDetection: OnPush": https://stackblitz.com/edit/stackblitz-starters-bdcac1?file=src%2Fmain.ts You can see that because of this line, you will only see the second modification. – OZ_ Aug 21 '23 at 23:26

1 Answers1

5

What you are observing in your example is standard change detection because you rely on setTimeout().

setTimeout is one of the APIs patched by zone.js, this means everytime it's called Angular will fire a change detection cycle and refresh your DOM. Since the value wraped by the signal has been changed (no matter which way), the new value will appear in the DOM.

That being said, back to the signal basics:

In changeUsersListWithoutMutate, you're not updating the signal because you're reading its value and changing its nested values. The signal itself has no way to know it has been updated.

This is why changeUsersListWithMutate is the way to go. You're explicitly telling the signal that you are updating it's value and the signal will fire the change detection.

Let me know if something isn't clear in my answer.

Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134
  • But what is the recommendation if I want to use Signals for the Change Detection? Should all components then be activating the .onPush strategy? – Okyo Aug 22 '23 at 08:02
  • Yes you might see some differences if you use OnPush but keep in mind that the full signal integration is not ready yet (I'm talking about signal components and zoneless apps) – Matthieu Riegler Aug 22 '23 at 08:16