DEV Community

Cover image for Angular Signals Introduction
Dino Dujmovic
Dino Dujmovic

Posted on • Updated on

Angular Signals Introduction

In essence, a signal is a combination of a variable and a change notification. Unlike observables, signals are synchronous and always possess a value.

🎯 Working with signals

There are three distinct methods that can be used to manipulate signal values in Angular.

set() method allows for the replacement of a signal's current value with a new one.

numb = signal(1);

numb.set(2);
Enter fullscreen mode Exit fullscreen mode

update() method can be used to modify a signal's value based on its current value.

name = signal('Dino');
name.update((value) => value + '1337') // Dino1337
Enter fullscreen mode Exit fullscreen mode

mutate() method is used to modify the content of a signal value, without replacing the signal value itself. When working with arrays, you can use this method to modify individual array elements. Similarly, when working with objects, this method can be used to modify specific object properties.

arr = signal([]);
arr.mutate((v) => v.push(i));

obj = signal({ name });
obj.mutate((v) => v.name = 'Dino');
Enter fullscreen mode Exit fullscreen mode

Computed signals

I believe this is the strongest use case for basic work signals so far, and here is why:

@Component({ 
  template: `
    {{ fullnameA }} // will not update
    {{ fullnameB() }} // will update

    <button (click)="changeSurname()">Change surname</button>
  `
})
export class App {
  nameA = 'John';
  surnameA = 'Doe';
  fullnameA = this.nameA + this.surnameA;

  nameB = signal('Jane');
  surnameB = signal('Doe');
  fullnameB = computed(() => this.nameB() + this.surnameB());

  changeSurname() {
    this.surnameA = 'Haaland';
    this.surnameB.set('Ibrahimovic');
  }
}
Enter fullscreen mode Exit fullscreen mode

At first glance, many might assume that modifying the surnameA variable would automatically update the fullnameA variable, just as it would if we were using an Angular expression like {{ nameA + surnameA }}. However, this is not the case.

Using signals (computed signals), which automatically recalculate whenever any of their dependent signals change, we can achieve this.

But hey, of course this works! Signals are functions and we are binding to function call here. And we all know it's a golden rule not to do that.
We could as well do something like this:

  {{ fullnameA() }}

  fullnameA = () => this.nameA + this.surnameA;
Enter fullscreen mode Exit fullscreen mode

And that will work to... but!

signal_cd_example

This demonstrates that fullnameA() will be called whenever change detection is triggered, whereas signal fullnameB() will only be called when its dependent signal is updated. This feature is remarkable because using signals allows for more precise management of change detection, which can lead to better performance.

Moreover, computed signals are re-calculated under two conditions: when one or more of its dependent signals are altered, and when the value of the computed signal is read.

Using effect

Signal effect are pretty much useful for getting notified when your signal gets updated (changed), so you can log it or make API calls (like you would be able to do with React's useEffect.. kinda).

effect(() => console.log(this.selected()));
effect(() => apiCall(this.param());
Enter fullscreen mode Exit fullscreen mode

🎯 Summary

So far, my favorite feature has been computed signals, as explained earlier. Additionally, effects now offer a convenient way for us to watch and react to our signal updates.

Until then, I recommend trying to learn how both of these tools play together so you can pick best tool for the job in order to write more simple, readable and performant code.

I recommend this video from Josh Morony:

As well as this video from Deborah Kurata:

to check out possible comparisons between using signals vs observables.

Top comments (1)

Collapse
 
jihadistheway profile image
jihad-is-the-way

I found this article very helpful thanks