1. Livres & vidéos
  2. Angular
  3. Les signaux
Extrait - Angular Développez vos applications web avec le framework JavaScript de Google (4e édition)
Extraits du livre
Angular Développez vos applications web avec le framework JavaScript de Google (4e édition)
1 avis
Revenir à la page d'achat du livre

Les signaux

Introduction

Ce chapitre aborde la notion de "signal". Bien que nouveau au sein d’Angular, ce concept existe déjà dans d’autres langages tels que ELS, Swift ou même la bibliothèque KnockoutJS, mais récemment le terme s’est principalement répandu grâce au framework SolidJS. Aujourd’hui, Angular évolue de manière significative en intégrant les signaux, nous apportant un nouveau concept de réactivité dans notre code.

Dans les chapitres précédents, lorsque le terme de "réactivité" était mentionné, cela faisait référence au binding entre les propriétés d’un composant et le template HTML, ou encore aux objets observables venant de la bibliothèque RxJS (Reactive extensions for JavaScript) (voir chapitre Les services). Les signaux constituent donc une nouvelle composante qui vient compléter cet écosystème.

La programmation réactive correspond à un paradigme qui vise à mettre l’accent sur le fait de faciliter la propagation d’une modification de donnée à travers notre application. Le terme "signal" est seulement un nom donné à un objet, qui respecte certains standards de programmation réactive. Chaque framework et chaque bibliothèque peut implémenter...

Créer et consommer des signaux

Le but de cette section est d’abord d’acquérir les bases des signaux afin de pouvoir, par la suite, les utiliser au sein d’Angular. Nous allons voir comment les créer, les manipuler et les écouter.

1. Différents types de signaux

On peut distinguer deux types de signaux : les signaux de type WritableSignal qui permettent de contrôler la valeur manuellement à tout moment, et les signaux de type Signal, qui sont en lecture seule et se mettent à jour uniquement grâce à une logique définie à l’instanciation.

On peut instancier un signal de la première catégorie via la fonction signal() avec une valeur par défaut en paramètre. Ce signal peut être lu en l’invoquant comme une fonction à l’aide de parenthèses, sans nécessiter de paramètres. Pour mettre à jour le signal, on peut utiliser la méthode set() avec une nouvelle valeur en paramètre, ou bien update() qui permet, grâce à un callback en paramètre, de récupérer la valeur actuelle du signal avant de le modifier.

import { Component, signal } from '@angular/core'; 
 
@Component({ 
 selector: 'app-card', 
 template: ` <p>{{ text() }}</p> 
    <button (click)="onClick()">Click me to set</button> 
    <button (click)="onClickToUpdate()"> 
      Click me to update 
    </button>`, 
  styles: [], 
}) 
export class MyComponent { 
  text = signal<string>('Bonjour'); 
 
  onClick() { 
    this.text.set('Hello'); 
  } 
 
  onClickToUpdate() { 
    this.text.update((oldValue) => oldValue + ' Angular'); 
  } 
} 

Il convient de souligner que, si nous essayons d’assigner plusieurs fois la même valeur à un signal, celui-ci détectera la répétition et ignorera la mise à jour. En reprenant l’exemple précédent...

Autres signaux

Avec les signaux au cœur de la démarche d’évolution, Angular a introduit une version basée sur des signaux, de plusieurs décorateurs, comme @Input et @Output, mais aussi pour les requêtes d’éléments : @viewChild, @viewChildren, @contentChild et @contentChildren. Leur nom n’a pas changé ; néanmoins, étant de simples fonctions au lieu de décorateurs, ils ne sont pas préfixés d’un symbole @. Le but est en effet de normaliser certains éléments pour faciliter et moderniser les développements.

1. Input et Output

Pour créer un signal d’entrée, nous devons déclarer une propriété et l’assigner avec la fonction input(). Voici un comparatif :

import { Component, Input, input } from '@angular/core'; 
 
@Component({...}) 
export class MyComponent { 
 
  @Input() value1: string = ''; 
 
  value2 = input<string>(''); 
} 

On peut passer deux paramètres à un Input, une valeur par défaut, et/ou un objet un peu plus complexe qui représente trois paramètres :

  • alias : qui sert, comme pour le décorateur @Input, à donner un nom différent à l’attribut que le parent doit fournir....

Bonnes pratiques d’utilisation

1. Structurer les signaux

Pour optimiser la réactivité et le rendu, on va prioriser les signaux pour toute variable intégrée au template, car ce sont celles-là qui déclencheront les mises à jour des composants (entre autres). Lorsque des variables doivent dépendre d’un signal, il faut penser à utiliser des Computed ou des LinkedSignal.

Il est acceptable d’appeler plusieurs fois le même signal dans le template pour des cas simples, mais il devient préférable d’utiliser un Computed si :

  • il est nécessaire de manipuler la valeur avant de l’afficher ;

  • certains calculs peuvent se trouver coûteux.

Quelques exemples :

  • Ici, la donnée est traitée. Pour éviter que le traitement soit exécuté plus que nécessaire, il est recommandé d’utiliser un Computed.

// Non recommandé 
<div> 
  <h1>{{ title }}</h1> 
</div> 
 
... 
 
product = input.required<Product>(); 
get title(){ 
return `${this.product().title} ${this.product().quantity} 
} 
 
// Recommandé 
<div> 
  <h1>{{ title() }}</h1> 
</div> 
 
... 
 
product = input.required<Product>(); 
title = computed(()=> 
`${this.product().title} ${this.product().quantity} 
) 

De petits traitements peuvent être acceptables, une valeur par défaut par exemple. Seulement, utiliser un Computed peut être un bon choix lorsque ce traitement est répété plusieurs fois dans le template afin de simplifier le code et le rendre plus lisible.

  • Exemple acceptable car aucune complexité ; l’appel multiple au signal n’est pas un problème.

<div> 
  <h1>{{product().title}}</h1> 
  <span>{{product().type}}</span> 
  <p>{{product().description}}</p> 
</div> 
 
... 
 
product = input.required<Product>(); 

On pourrait aussi varier l’affichage lorsqu’il y a beaucoup de valeurs, en utilisant...

Pour aller plus loin

1. Autres avantages des signaux

Un point important concernant les signaux mérite d’être souligné, notamment leur utilisation au sein des composants. Dans le chapitre Les composants, à la section Détection de changements, nous avions évoqué Zone.js, responsable de déclencher la détection de changements d’Angular. Les signaux jouent un rôle majeur dans l’amélioration de ce système.

Avec l’utilisation des signaux, Angular détecte qu’un signal a émis une modification sans nécessiter Zone.js pour déclencher la détection de changements. De plus, la détection ne parcourt plus l’arbre des composants pour vérifier chaque élément, mais cible directement la source du signal. Ce mécanisme représente un atout majeur pour les performances d’Angular.

Enfin, la caractéristique glitch-free ou "sans-anomalie" constitue un pilier fondamental. Cette propriété garantit qu’un callback de changement de valeur (Effect, signal calculé, etc.) ne sera jamais recalculé plus d’une fois par cycle de détection de changements. Concrètement, Angular orchestre intelligemment la propagation des changements en s’assurant qu’un signal calculé ou Effect n’est réévalué qu’une...

Projet fil rouge

L’application étant encore en phase initiale de développement, la progression du projet fil rouge pour ce chapitre ne comportera donc que peu d’étapes. Nous allons passer en revue les composants et directives pour remplacer les différents éléments remplaçables par des équivalents en signaux. Puis nous préparerons la suite de l’exercice en modifiant la façon dont on récupère les données du tableau.

1. Migration vers les signaux

La première étape est de remplacer les @Input, @Output et @ContentChildren des éléments CardComponent, ColumnComponent, NavMenuComponent, UserCardComponent, DraggableDirective et DroppableDirective par leurs équivalents signaux. Attention à ne pas oublier d’utiliser des parenthèses lors de la récupération ou de l’affichage des valeurs des signaux.

Lorsque l’on a besoin d’afficher les propriétés d’un signal d’objet, par exemple dans le composant CardComponent, on peut appeler plusieurs fois le signal, utiliser des Computed pour chaque propriété, ou bien déclarer une propriété dans le template grâce à @let pour simplifier légèrement la syntaxe :

@let t = ticket(); 
<div id="wrapper" [class]="t.type"> 
  <h4>{{...

Conclusion

Dans ce chapitre, nous avons franchi une étape essentielle en découvrant et en intégrant les signaux, un paradigme réactif qui modernise la gestion des données et des événements dans une application Angular. Nous avons appris à créer et consommer différents types de signaux, tels que les LinkedSignal, Resource et Model, tout en explorant leurs usages spécifiques et leur impact sur la structure et la fluidité des applications.

En approfondissant l’utilisation des signaux, nous avons vu les bonnes pratiques pour en tirer le meilleur parti : structurer les données en préservant l’immuabilité, optimiser les paramètres et orchestrer des actions déclenchées par les changements d’état, tout en maîtrisant des aspects avancés comme la prévention de l’exécution, l’initialisation, et la destruction de ces effets.

Dans la section pratique, nous avons appliqué ces notions en migrant les éléments de notre application Kanban vers les signaux, notamment pour remplacer les mécanismes existants par une gestion réactive des données. Nous avons également introduit une Resource afin d’améliorer l’expérience utilisateur avec un indicateur de chargement, rendant l’interface plus fluide et réactive. Cette...