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
: leseffects
.
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