Jusqu'ici tes données étaient de simples propriétés (mesure = 21.4). Un signal, c'est une valeur qui sait quand elle change et qui prévient tout ce qui en dépend. C'est le nouveau cœur de la réactivité Angular : la vue, et les valeurs dérivées, se remettent à jour automatiquement et au plus juste. C'est le concept le plus structurant de toute la phase 1.
signal() / computed(), c'est ne pas pouvoir reviewer leurs composants.
Un signal est un conteneur autour d'une valeur. Tu ne lis plus la valeur directement : tu l'appelles comme une fonction. Et tu ne l'écris plus avec = : tu utilises .set() ou .update(). Ce petit protocole est ce qui permet à Angular de savoir qui lit quoi et quand ça change.
import { signal } from '@angular/core';
export class PosteComponent {
// création : signal(valeurInitiale)
mesure = signal(21.4);
lire() {
const v = this.mesure(); // LIRE = appeler ()
}
ecrire() {
this.mesure.set(23.0); // remplacer la valeur
this.mesure.update(v => v + 1); // dériver de l'ancienne
}
}
Dans le template, pareil : on appelle le signal. {{ mesure() }}, pas {{ mesure }}. Dès que set/update est appelé, toute vue qui lit mesure() se rafraîchit — et uniquement celles-là.
A1 = 21.4 et une cellule B1 = A1 * 2 : tu changes A1, B1 se recalcule tout seul, sans que tu touches B1. Un signal, c'est A1 ; un computed() (section 3), c'est B1. Côté .NET, c'est l'idée d'INotifyPropertyChanged — mais sans écrire l'événement ni le PropertyChanged?.Invoke à la main : la notification est automatique et le système sait exactement qui dépend de qui.
set vs updateDeux façons d'écrire dans un signal :
| Méthode | Sert à | Exemple |
|---|---|---|
.set(v) | remplacer par une valeur connue | seuil.set(80) |
.update(fn) | calculer à partir de l'ancienne | compteur.update(n => n + 1) |
Règle simple : si la nouvelle valeur dépend de l'ancienne (incrément, toggle, ajout dans un tableau), c'est update. Sinon set.
computed() — une valeur dérivéeUn computed est un signal en lecture seule dont la valeur est calculée à partir d'autres signals. Tu ne l'écris jamais : il se recalcule quand l'un de ses ingrédients change.
import { signal, computed } from '@angular/core';
export class PosteComponent {
mesure = signal(21.4);
seuil = signal(25);
// dérivé : recalculé quand mesure() OU seuil() change
enAlerte = computed(() => this.mesure() > this.seuil());
libelle = computed(() =>
this.enAlerte() ? '⚠ Hors seuil' : 'OK'
);
}
Angular détecte automatiquement les signals lus dans la fonction (mesure et seuil) : ce sont ses dépendances. Tu ne les déclares pas, tu les utilises, et le lien se crée tout seul. Note que libelle dépend de enAlerte qui dépend de mesure/seuil : la chaîne se propage.
computed à connaître
(1) Mémoïsé : tant que ses dépendances ne changent pas, il renvoie la valeur en cache sans recalculer — même si tu le lis 100 fois. (2) Paresseux : il ne calcule que si quelqu'un le lit. C'est exactement ce qui règle le piège perf de la leçon 04 : au lieu de {{ calculLourd() }} (rejoué à chaque cycle), tu écris calcul = computed(() => …) et {{ calcul() }} — recalculé seulement quand une vraie dépendance bouge.
<!-- on appelle chaque signal / computed avec () -->
<p>Mesure : {{ mesure() }} °C</p>
<span [class.ko]="enAlerte()">{{ libelle() }}</span>
<button (click)="seuil.set(30)">Relever le seuil</button>
Clique sur le bouton → seuil change → enAlerte et libelle se recalculent → les deux liaisons se rafraîchissent. Tu n'as écrit aucune logique de mise à jour. C'est tout l'intérêt.
computed, jamais une méthode appelée dans le template (qui, elle, est rejouée à chaque détection). C'est le réflexe perf clé. (2) Un computed doit être pur : il calcule et renvoie, il ne modifie rien (pas de .set() d'un autre signal dedans, pas d'appel HTTP). Les effets de bord, ce sera effect() — prochaine leçon. (3) En revue, repère le {{ mesure }} sans parenthèses : ça affiche la fonction, pas la valeur — bug silencieux classique du débutant signals.
signal() nommé mesure ?enAlerte = mesure > seuil, recalculée toute seule. Tu écris ?compteur à partir de sa valeur actuelle ?signal(v) est une valeur réactive : on la lit en l'appelant (mesure()) et on l'écrit avec .set(v) (remplacer) ou .update(fn) (dériver de l'ancienne). Un computed(() => …) est une valeur dérivée en lecture seule : Angular détecte seul ses dépendances, et il est mémoïsé + paresseux. Dans le template, on appelle toujours avec (). Réflexe de lead : valeur dérivée → computed (jamais une méthode dans le template), et un computed reste pur.
Angular — Signals (officiel). Lis l'intro + les sections Writable signals et Computed signals. On verra effect() et les input()/output() signals à la leçon 07.
computed est « paresseux » et ce que ça change concrètement pour la perf ? Demande-moi, ici, maintenant.