Angular Signals : que faut t-il retenir quand je débute ?

Depuis la sortie de Angular 16, une nouvelle fonctionnalité a été introduite. Il s'agit du concept de Signals. Cette fonctionnalité est à présent stable et constitue un atout majeur dans la conception des applications avec Angular. Dans cet article, nous verrons ensemble ce dont il est question quand on parle de Signals en Angular et ce qu'il faut en retenir.

Qu'est-ce qu'un signal en Angular ?

Avant d'entrer dans le vif du sujet, faisons un peu d'histoire. Dans ce précédent article Les nouveautés en Angular 16 (Partie 1), j'ai parlé de Signal et de pourquoi il est innovant. Je vous conseille de le lire pour mieux comprendre la suite.

En nous basant sur la documentation de Angular, voici comment il a été défini :

Angular Signals est un système qui suit de manière granulaire comment et où votre état est utilisé dans une application, permettant au framework d'optimiser les mises à jour de rendu.

Ce qui captive l'attention dans cette définition c'est l'expression suivre de manière granulaire. Cela signifie tout simplement que le système mis en place écoute en permance point à point les changements des états des variables utilisées dans votre application qui sont basées sur le concept de Signals. Ainsi, le temps de réponse lié au rendu de ces variables ou objets dans le DOM est considérablement réduit ce qui rend notre application plus performante.

En allant plus loin dans la définition, nous apprenons que les Signals peuvent contenir n'importe quelle valeur c'est-à-dire des primitives ou des données complexes. Quand on parle de primitives, il s'agit des types de base à savoir string, number, etc...

Pour finir, il y a un élément crucial à retenir : les Signals peuvent être en écriture ou en lecture seule.

Comment créer un signal et mettre à jour sa valeur ?

Dans cette section, nous verrons comment utiliser un signal en mode écriture c'est-à-dire créer un signal et modifier sa valeur :

  • Création d'un signal

Pour créer un signal, voici comment procéder :

const count = signal(0);
console.log('The count is: ' + count());

Comme nous le remarquons sur le code ci-dessus, la création d'un signal se fait au travers de la méthode signal() avec une valeur d'initialisation. Ici, cette valeur c'est 0.
En outre, pour lire la valeur du signal créé qui est count dans l'exemple ci-dessus, nous avons utilisé une syntaxe particulière à savoir count(). Il s'agit tout simplement d'une syntaxe simplifiée lorsque l'objet manipulée possède une méthode get() qui nous retourne la valeur de la variable ou de l'objet. On parle de getter function (en anglais).

  • Modification d'un signal

Tout comme la création d'un signal, la modification de sa valeur est aussi simple. Cependant, nous avons deux façons de la faire :

count.set(count() + 1);

// Ou

count.update(value => value + 1);

Dans le premier cas, nous avons utilisé la méthode set() ce qui n'est pas surprenant vu que nous avons une méthode get().

Vous l'aurez compris certainement !
set() pour modifier la valeur
get() pour récupérer la valeur
🤓

Dans le second cas, nous sommes passé par la méthode update(). L'avant de cette méthode réside dans le fait que nous avons directement l'ancienne valeur de notre signal sur laquelle nous pourrons procéder à des traitements pour la modifier.

A présent, il est crucial de comprendre que lorsque nous décidons de créer un signal qui peut être modifié c'est-à-dire accessible en écriture, Angular le définit comme un WritableSignal.

Bien-sûr vu que vous êtes super doués on peut déjà se douter qu'il y a des signals qui ne sont pas writable 😂

Pour finir, voici ce que donnerait la création de notre signal avec la déclaration explicite du type :

const count: WritableSignal<number> = signal(0);

Comment créer un signal en lecture seule (read only) ?

Dans le jargon technique de Angular, les signals en lecture seule sont appelés Computed signal.

Pour créer un signal en lecture seule, voici comment procéder :

const count: WritableSignal<number> = signal(0);
const doubleCount: Signal<number> = computed(() => count() * 2);

Comme nous pouvons le lire dans le code ci-dessus, le type de notre signal doubleCount est Signal qui est l'opposé de WritableSignal. Nous nous sommes basés sur l'exemple précédent afin de faire remarquer les différences.

Le signal doubleCount est en lecture seule mais il dépend du signal count qui est en écriture. Si le signal count se met à jour, automatiquement le signal doubleCount le sera.

Pour les plus curieux qui cherchent à comprendre comment s'effectue la lecture seule, voici ce que la documentation de Angular nous enseigne :

Les signals calculés sont à la fois évalués paresseusement et mémoïsés.

La fonction de dérivation de doubleCount ne s'exécute pas pour calculer sa valeur avant la première lecture de doubleCount. La valeur calculée est alors mise en cache, et si vous lisez doubleCount à nouveau, il retournera la valeur mise en cache sans recalculer.

Si vous changez ensuite de compte, Angular sait que la valeur mise en cache de doubleCount n'est plus valide, et la prochaine fois que vous lirez doubleCount, sa nouvelle valeur sera calculée.

Par conséquent, vous pouvez effectuer en toute sécurité des dérivations coûteuses en calcul dans les signaux calculés, comme le filtrage des tableaux.

Dans cette explication, ce qui retient l'attention c'est l'expression mémoïsation. La mémoïsation est une expression en jargon informatique qui signifie tout simplement la mise en cache de la valeur d'une fonction.

Pour finir, on retient de cette section que nous avons deux types de Signals à savoir WritableSignal et Signal.
WritableSignal peut être modifié tandis que Signal est en lecture seule.

Dans la prochaine section, nous parlerons d'une fonctionnalité importante des Signals : les effects.

Les effects

Supposons un instant que nous aimerions faire des traitements lorsque notre signal change de valeur. Jusqu'à présent, nous n'avons aucun moyen de le faire. Comme vous l'auriez compris, les effects nous servirons à les faire.

Un effect est donc une méthode qui se déclenche chaque fois qu'une ou plusieurs valeurs de signals changent. Pour metter en place un effect, voici ce qu'il faut faire :

effect(() => {
  console.log(`The current count is: ${count()}`);
});

Dans le code ci-dessus, nous avons notre effect avec une fonction de callback. La fonction de callback nous permet d'effectuer nos traitements suivant la modification de notre signal.

Les effects sont toujours exécutés au moins une fois. Lorsqu'un effect s'exécute, il suit toutes les valeurs de signals lues. Chaque fois que l'une de ces valeurs change, l'effect s'exécute à nouveau. Comme pour les signals calculés, les effects suivent leurs dépendances de manière dynamique et ne suivent que les signals qui ont été lus lors de l'exécution la plus récente.

Les effects s'exécutent toujours de manière asynchrone, pendant le processus de détection des changements.

En nous rapportant toujours à la documentation de Angular, voici quelques cas d'utilisation des effects :

  • Enregistrement des données affichées et de leurs modifications, à des fins d'analyse ou comme outil de débogage.

  • Garder les données synchronisées avec window.localStorage.

  • Ajouter un comportement DOM personnalisé qui ne peut pas être exprimé avec la syntaxe des modèles.

  • Rendre le comportement personnalisé à un , à une bibliothèque graphique ou à une autre bibliothèque d'interface utilisateur tierce.

En somme, nous avons vu ensemble l'essentiel de Angular Signals ainsi que la philosophie derrière cette approche. Nous avons compris à présent la pertinence de Signals en Angular et comment l'utiliser de façon très subtile.

Nous aborderons dans de prochains articles, tout l'écosystème autour de ce concept qui a révolutionné le fonctionnement du framework Angular depuis la version 16.

Pour plus d'informations 👉 Angular Signals

Did you find this article valuable?

Support Ezéchiel Amen AGBLA by becoming a sponsor. Any amount is appreciated!