Et oui, Angular change, et change vers une version plus souple, moins Zone.js (bientôt la fin). Après l’arrivée de la Standalone API, ce bien fou dans la strcuturation et l’appel de nos composants, nous passons maintenant à une nouvelle étape : l’utilisation des Signals, pour décider quand on update vraiment la Vue, son template. Fini l’auto update, de Zone.js.
Découvrons ensemble la mise en place des Signals, dans un composant, vers l’utilisation avec ngRx.
Mise en place du signal dans un composant
Un Signal est une fonction, qui gère un état pour un composant, ou un service.
Cet état va être garder en mémoire, jusqu’à la prochaine demande de changement.
Oui, vous avez bien lu ! Nous allons faire la demande de changement. Ce n’est plus zone.js qui va interpréter le binding de notre champ, en détectant si sa valeur / référence change. C’est nous qui décidons le changement.
Si vous avez déjà fait du React, ça ressemble étrangement à un useState. Chut, on ne dira rien de plus …
Création d’un Signal pour mettre à jour notre titre
title = signal<string>('Mes films');
En regardant le type retourné, nous avons un signal, qui est modifiable. Car oui, nous pouvons aussi avoir des signaux en lecture seule, calculés.
Appeler notre signal sur la vue
Notre signal est une fonction. Donc, bien qu’étrange, nous allons exécuter notre fonction pour récupérer la dernière valeur mémoisée. En même temps, nous venons de nous abonner au Signal, en tant qu’observer.
C’est vraiment déroutant, sachant que j’ai pu le répéter de nombreuses fois : normalement il faut éviter d’appeler des fonctions du côté template (du fait de la non mémoization, de l’exécution multiple à cause de Zone.js)
Voyons voir ce que ça donne :
{{ title() }}
Si nous appelons le signal comme un champ, nous récupérons un warning à bien écouter :
(pour bien comprendre les signaux,je vous invite à aller voir aussi cet article qui regarde comment fonctionne en interne un Signal)
Les computed
Avec notre Signal, mutable, nous pouvons construire, tout comme les selectors dans NgRx, des computable signal, qui sont en lecture seul.
isEmptyList = computed(() => {
return this.listItems().length === 0;
})
Utilisable ainsi
@if(isEmptyList())
{
<i>Aucun élément dans la liste</i>
}
Notons que le computed s’exécute toujours après les signaux parents.
Ngrx et les signaux
Ngrx propose une refonte de son store : signalStore.
Ce store peut être soit créé par composant, soit global, en singleton pour toute l’application. Cela change de l’approche ngrx classique (action, effect, selector, reducer).
Préparation du SignalStore
export const BooksStore = signalStore(
{ providedIn: 'root' },
withState(initialBookState),
withMethods((store, bookInfra = inject(GetAllBooksInfrastructure)) => ({
loadAll: rxMethod<string>(
pipe(
tap(() => patchState(store, { isLoading: true })),
switchMap(filter => bookInfra.getAll({ value: filter }).pipe(
tapResponse({
next: items => patchState(store, { items }),
error: () => patchState(store, { isOnError: true }),
finalize: () => patchState(store, { isLoading: false })
})
)),
)
)
}))
Notons ici plusieurs points importants :
Le signalStore s’appuie sur un état initial, qui va créer sa structure aussi au signal renvoyé.
ici notre état est :
export interface BooksState {
items: Books;
isLoading: boolean;
isOnError ?: boolean;
}
export const initialBookState: BooksState = {
items: [],
isLoading: false
}
Puis nous appelons un chargement des données depuis un interop rxjs : rxMethode.
Nous mettons à jour une partie de notre état avec patchState.
Enfin, vous avez du noter le { providedIn: ‘root’ } qui permet de le préparer en singleton.
Utilisation de notre signalStore
private readonly booksStore = inject(BooksStore);
Nous allons pouvoir l’injecter et l’appeler par la suite dans le template. C’est un signal, donc appelons la fonction liée à notre structure de notre State.
Côté component : books$$ = this.application.getAll();
Côté template :
@for (book of books$$(); track book.id) {
<div>
{{book.title}}
</div>
}
Et voilà, le tour est joué avec un gestionnaire d’état ngrx basé sur des signaux !