Résumé
ngrx 21 - Enfin un gestionnaire d'état digne de ce nom
ngrx signals vient de se doter d'une nouvelle fonctionnalité : events.

ngrx 21 - Enfin un gestionnaire d'état digne de ce nom

NGRX signals - Un gestionnaire d'état complet

Avec la montée en puissance des signaux, ngrx signal a pris de plus en plus d'importance. Les devs ont rapidement voulu intégrer ce gestionnaire d'état. A la différence de son grand frère : ngrx, il se voulait plus facile à mettre en place, moins verbeux, moins de fichiers. Mais, il manquait une fonctionnalité pour être vraiment utilisable. Les Events.

 

Les fondamentaux

Création du signal store

export const WeatherStore = signalStore( 
{providedIn: 'root'}, 
withState(initDefaultState())
);

Le withState permet d'injecter l'état initial. 

POINT important : chaque propriété du state est un signal à part.


Ici, noton le provided in root qui permet d'injecter le store de manière singleton dans tout l'application.

 

Tips : plutot qu'un singleton : on peut aussi l'injecter dans le providers de la route.

export const routes: Routes = [
  {
    path: 'afternoons',
    children: afternoonsRoutes,
    providers: [ AfternoonStore ]
  },
Attention, deux providers de même niveau d'injection auront le meme context d'injection. Dit autrement : si tu provides le même service ou store dans deux routes de même niveau : ça sera le même service !
 

Travail sur les méthodes du store ngrx signal

Tu peux ajouter des méthodes dédiées qui communiquent avec le store.

withMethods(store => ({
    addOne(item: Afternoon) {
      patchState(store, ({ items: [...store.items(), item] }))
    },
    clear(): void {
      patchState(store, getDefaultListState());
    }
  })
  ),
Elles vont pouvoir utiliser patchState par exemple : qui permet de mettre à jour le state en cours, avec une nouvelle version (nouvelle référence) de l'état. Ce qui déclenchera l'update côté template, et la mise à jour de tous les : linked signal et computed liés !
 

Ajout de computed dédiés

Tu peux ajouter des données calculés, qui travaillent avec des computed pour être connectés aux signaux du store.

withComputed(store => ({
    nbItems: computed(() => store.items().length),
    filteredItems: computed(() => store.items().filter(item => {
      const filter = store.filter();
      return item.status.includes(filter.query) || item.date.toDateString().includes(filter.query);
    }))
  })),

Ici, on ajoute deux propriétés au store, qui sont des signal en readonly.

 

Connexion à des linked signal

On peut également, et c'est vraiment une belle évolution : créer des linked signal (la fonctionnalité la moins bien connue de angular 21 selon moi ... beaucoup font des effects à la place ... ce qui est une erreur réelle d'implémentation)

 

withLinkedState(store => ({
    first: () => store.items()[0]
  })),
 
Pour rappel, un linked signal, c'est un signal qui a sa vie propre mais qui sera réinitialisé dès que le signal lié (le parent).
 

Le game changer de ngrx signal 21 

Si tu as déjà utilisé ngrx tu sais qu'il y a un point essentiel, qui fait partie du coeur du système : les actions. Elles permettent de parler à N reducers et de déclencher la mise à jour de plusieurs états, sans créer de dépendance entre domain / bounded context / zone métier indépendante. 

 

Il manquait vraiment cette fonctionnalité dans ngrx signal ! Plus d'actions, non, on parlera de "event".

Création d'un event : seul ou par groupe

Première étape : création des événements. On peut les créer indépendemment, ou bien par group.

Je te recommande par groupe : plus propre et plus lisible.

export const afternoonEvents = eventGroup({
  source: 'Afternoons search page',
  events: {
    loadedSuccess: type<Afternoon[]>(),
    loadedFailure: type<string>()
  }
})
Notons ici le type<Afternoon[]> (par exemple). Ce type permet de préciser quel payload on pourra récupérer dans nos reducer !


Création de nos reducers

Les reducers ont le rôle de mettre à jour l'état suivant l'abonnement sur un event.

 withReducer(
    on(afternoonEvents.loadedSuccess, (event, state) => ({
      ...state,
      items: event.payload,
      loading: false,
    })),
    on(afternoonEvents.loadedFailure, (event, state) => ({
      ...state,
      error: event.payload,
      loading: false,
    }))
  ),

Il nous reste plus qu'une étape, crucial : pouvoir appeler l'api et envoyé un event pour informer le reducer ! 
 

withEventHandlers

Avec withEventHandlers, on va pouvoir coupler l'abonnement sur un évément et l'enchainement d'operator rxjs. Ca permet de produire une observable !

  withEventHandlers((store,
                    events = inject(Events),
                    getCurrentWeatherInfra = inject(GetCurrentWeatherInfra)) => ({
                      loadWeather$: events
                      .on(weatherEvents.get)
                      .pipe(
                        tap(() => patchState(store, { loading: true })),
                        switchMap(() => getCurrentWeatherInfra.getOne().pipe(
                          mapResponse({
                            next: weather => weatherEvents.loadedSuccess(weather),
                            error: err => console.error(err)
                          })
                        ))
                      )
                    })),
 
Tu l'as compris, c'est le point névralgique pour travailler avec tes requêtes httpclient ! 
Et tu noteras qu'on peut appeler un service plutot que tout mettre dans le store !
 

Dispatching de l'event

Dernier point :  pour déclencher un évenement, on va utiliser un service dédié : le Dispatcher.

private readonly dispatcher = inject(Dispatcher);
  ngOnInit(): void {
    this.dispatcher.dispatch(weatherEvents.get());
  }
 
Mais on peut aussi l'appeler directement dans notre store, avec un hook ! 
 
withHooks({
    onInit(store) {
      setInterval(() => store.load(), 900);
    }
  })
 
Avec la méthode qui injecte le Dispatcher 
 
withMethods((store, dispatcher = inject(Dispatcher)) => ({
    load(): void {
      dispatcher.dispatch(weatherEvents.get());
    }
  })),
 

Conclusion

Tu l'as vu, ngrx signal, avec l'apparition des events, est vraiment un game changer pour ton application angular 21.

Vu que tout est signal : tupeux travailler avec un form (experimental)  !

Tout s'organise parfaitement !

Reste à se poser la question de comment intégrer les resource avec ce gestionnaire d'état ... nous en reparlerons plus tard ! 

 

Hey, tu sais qu'on donne des formations angular next gen ? 

- ngrx signal : https://www.devtobecurious.fr/formations-developpeur/front-end/angular-19-etat-ngrx-signals
- angular next gen : https://www.devtobecurious.fr/formations-developpeur/front-end/formation-angular-17-19-signal-ready
- angular avancé : https://www.devtobecurious.fr/formations-developpeur/front-end/angular-19-avance

Notre adresse

1 rue du guesclin
44000 Nantes

Notre téléphone

+33 2 79 65 52 87

Société

DevToBeCurious SARL
84860163900018 - Nantes B 848 601 639