Service en ligne 100% Gratuit Utilitaires Web AngularForAll

- Générateur NgRx Boilerplate Angular

Générateur Ngrx Boilerplate Angular Ngrx Actions Reducer Effects Selectors Ngrx Entity Adapter Angular State Management Angular Ngrx Ngrx Store Generator Online

Générez le boilerplate NgRx complet (Actions, Reducer, Effects, Selectors) pour votre feature Angular. Code TypeScript prêt à l'emploi avec EntityAdapter.

Générateur NgRx Boilerplate Angular

🔧 Configuration

Clé du store NgRx (lowercase)
Laissez vide pour dériver du nom de feature
📋 Opérations CRUD
⚙️ Options
💡 Conseil : Le code se met à jour automatiquement à chaque modification.

NgRx et le pattern Redux dans Angular

NgRx est la bibliothèque de gestion d'état la plus populaire pour Angular. Elle implémente le pattern Redux — inspiré de Flux — qui repose sur un principe simple : l'état de l'application est stocké dans un objet unique et immuable, le Store. Toute modification passe par des Actions explicites qui déclenchent des Reducers purs, garantissant ainsi une traçabilité totale et une testabilité maximale.

Adopter NgRx dans vos projets Angular apporte plusieurs bénéfices concrets :

  • Prévisibilité — chaque action produit un état déterministe
  • Débogage avancé — Redux DevTools avec time-travel debugging
  • Séparation des préoccupations — la logique async est isolée dans les Effects
  • Performance — Selectors mémorisés avec createSelector
  • Scalabilité — architecture features indépendantes
Quand utiliser NgRx ? Pour des applications Angular de taille moyenne à grande avec de nombreux composants partageant le même état, des flux de données complexes ou un besoin de synchronisation avec plusieurs API.

Architecture NgRx : les 5 piliers

NgRx s'articule autour de cinq concepts fondamentaux qui forment un cycle de données unidirectionnel (unidirectional data flow) :

Concept Rôle Fichier
Store Source unique de vérité — contient l'état global app.config.ts
Actions Événements qui décrivent ce qui s'est passé *.actions.ts
Reducers Fonctions pures qui calculent le nouvel état *.reducer.ts
Effects Gèrent les effets de bord (API, localStorage…) *.effects.ts
Selectors Fonctions mémorisées pour lire/dériver l'état *.selectors.ts

Le cycle complet : Component → dispatch une Action → le Reducer calcule le nouvel état → le Store met à jour → les Selectors notifient les composants abonnés. Les Effects interceptent certaines actions pour appeler l'API, puis dispatchent des actions de succès ou d'erreur.

Actions NgRx : conventions et bonnes pratiques

Les Actions NgRx sont définies avec createAction() depuis @ngrx/store. Chaque action possède un type unique — conventionnellement au format [Source] Événement — et des props optionnelles via props<T>().

La convention de nommage des types d'actions suit la structure [Feature] Verbe Entité [Résultat] :

// ✅ Convention recommandée
export const loadProducts = createAction('[Products] Load Products');
export const loadProductsSuccess = createAction(
  '[Products] Load Products Success',
  props<{ products: Product[] }>()
);
export const loadProductsFailure = createAction(
  '[Products] Load Products Failure',
  props<{ error: string }>()
);

// ✅ Action avec payload complexe
export const updateProduct = createAction(
  '[Products] Update Product',
  props<{ id: number; changes: Partial<Product> }>()
);
Bonnes pratiques pour les Actions :
  • Toujours grouper par 3 : action principale + Success + Failure
  • Utiliser Omit<T, 'id'> pour les actions de création (l'ID vient du serveur)
  • Préférer Partial<T> pour les updates partiels (PATCH)
  • Le type de l'action doit être lisible dans Redux DevTools
  • Exporter la clé de feature avec featureKey pour la cohérence

Reducer et @ngrx/entity : gestion optimale des collections

Le Reducer est une fonction pure (state, action) => newState. NgRx propose createReducer() combiné à on() pour un code déclaratif et lisible. Pour les collections d'entités, @ngrx/entity fournit un EntityAdapter qui optimise les opérations CRUD sur les tableaux d'objets.

// Reducer avec EntityAdapter
import { createReducer, on } from '@ngrx/store';
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
import { Product } from '../models/product.model';
import * as ProductActions from './product.actions';

export interface ProductsState extends EntityState<Product> {
  loading: boolean;
  error: string | null;
  selectedId: number | null;
}

// L'adapter gère l'ID automatiquement (id par défaut)
export const adapter: EntityAdapter<Product> = createEntityAdapter<Product>();

export const initialState: ProductsState = adapter.getInitialState({
  loading: false,
  error: null,
  selectedId: null,
});

export const productsReducer = createReducer(
  initialState,

  on(ProductActions.loadProducts, state =>
    ({ ...state, loading: true, error: null })),

  // adapter.setAll() remplace toute la collection en O(n)
  on(ProductActions.loadProductsSuccess, (state, { products }) =>
    adapter.setAll(products, { ...state, loading: false })),

  on(ProductActions.loadProductsFailure, (state, { error }) =>
    ({ ...state, loading: false, error })),

  // adapter.addOne() ajoute un élément sans mutation
  on(ProductActions.createProductSuccess, (state, { product }) =>
    adapter.addOne(product, { ...state, loading: false })),

  // adapter.upsertOne() crée ou met à jour
  on(ProductActions.updateProductSuccess, (state, { product }) =>
    adapter.upsertOne(product, { ...state, loading: false })),

  // adapter.removeOne() supprime par ID
  on(ProductActions.deleteProductSuccess, (state, { id }) =>
    adapter.removeOne(id, { ...state, loading: false })),
);

L'EntityAdapter stocke les entités dans un format normalisé { ids: number[], entities: Record<number, Entity> } — bien plus performant qu'un tableau brut pour les recherches par ID.

Clé d'entité personnalisée : Si votre entité n'utilise pas id comme identifiant, configurez l'adapter : createEntityAdapter<Product>({ selectId: p => p.uuid })

Effects NgRx : logique asynchrone avec RxJS

Les Effects interceptent les actions du Store pour exécuter des opérations asynchrones (appels HTTP, accès localStorage, WebSocket…) et dispatcher de nouvelles actions en résultat. Ils s'appuient sur RxJS pour composer les flux de données de manière déclarative.

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, switchMap, mergeMap } from 'rxjs/operators';
import { ProductService } from '../services/product.service';
import * as ProductActions from './product.actions';

@Injectable()
export class ProductEffects {

  // switchMap annule l'appel précédent si une nouvelle action arrive
  // Idéal pour les lectures (GET list, GET by id)
  loadProducts$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ProductActions.loadProducts),
      switchMap(() =>
        this.productService.getAll().pipe(
          map(products => ProductActions.loadProductsSuccess({ products })),
          catchError(err => of(ProductActions.loadProductsFailure({ error: err.message })))
        )
      )
    )
  );

  // mergeMap exécute chaque appel en parallèle
  // Idéal pour les mutations (POST, PUT, DELETE)
  createProduct$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ProductActions.createProduct),
      mergeMap(({ product }) =>
        this.productService.create(product).pipe(
          map(result => ProductActions.createProductSuccess({ product: result })),
          catchError(err => of(ProductActions.createProductFailure({ error: err.message })))
        )
      )
    )
  );

  constructor(
    private readonly actions$: Actions,
    private readonly productService: ProductService
  ) {}
}
Opérateur RxJS Comportement Usage NgRx
switchMap Annule le flux précédent GET (recherche, chargement liste)
mergeMap Exécute en parallèle POST, PUT, DELETE
concatMap Exécute séquentiellement Opérations ordonnées (upload multi-fichiers)
exhaustMap Ignore si déjà en cours Formulaires de connexion (évite double-submit)

Selectors mémorisés : lire l'état efficacement

Les Selectors sont des fonctions pures qui extraient et transforment des portions du Store. createSelector() les mémorise (memoize) : si les entrées n'ont pas changé, le résultat précédent est réutilisé sans recalcul — optimisation cruciale pour les grandes collections.

import { createFeatureSelector, createSelector } from '@ngrx/store';
import { adapter, ProductsState } from './products.reducer';

// Sélecteur racine de la feature
export const selectProductsState =
  createFeatureSelector<ProductsState>('products');

// Sélecteurs EntityAdapter (optimisés)
export const {
  selectAll: selectAllProducts,
  selectEntities: selectProductEntities,
  selectIds: selectProductIds,
  selectTotal: selectProductsTotal,
} = adapter.getSelectors(selectProductsState);

// Sélecteurs dérivés (mémorisés)
export const selectProductsLoading = createSelector(
  selectProductsState,
  state => state.loading
);

export const selectSelectedProductId = createSelector(
  selectProductsState,
  state => state.selectedId
);

// Composition de sélecteurs
export const selectSelectedProduct = createSelector(
  selectProductEntities,
  selectSelectedProductId,
  (entities, id) => (id != null ? entities[id] : null)
);

// Sélecteur avec transformation
export const selectActiveProducts = createSelector(
  selectAllProducts,
  products => products.filter(p => p.active)
);

// Usage dans un composant
export class ProductListComponent {
  products$ = this.store.select(selectAllProducts);
  loading$  = this.store.select(selectProductsLoading);

  constructor(private store: Store) {
    this.store.dispatch(loadProducts());
  }
}
Composition de selectors : Vous pouvez chaîner jusqu'à 8 selectors d'entrée dans createSelector(). Pour des cas plus complexes, combinez plusieurs createSelector() entre eux.

Intégrer NgRx dans un projet Angular

Installez NgRx via la CLI Angular — les schematics configurent automatiquement StoreModule et EffectsModule :

# Installation complète NgRx (core + entity + effects + devtools)
ng add @ngrx/store@latest
ng add @ngrx/effects@latest
ng add @ngrx/entity@latest
ng add @ngrx/store-devtools@latest

# Générer une feature NgRx avec les schematics officiels
ng generate @ngrx/schematics:feature products \
  --module=app.module.ts \
  --api          # génère Effects avec appel HTTP
  --entity       # utilise EntityAdapter

Avec Angular 17+ en mode Standalone, la configuration se fait dans app.config.ts :

// app.config.ts — configuration standalone NgRx 17+
import { ApplicationConfig } from '@angular/core';
import { provideStore, provideState } from '@ngrx/store';
import { provideEffects } from '@ngrx/effects';
import { provideStoreDevtools } from '@ngrx/store-devtools';
import { productsReducer } from './products/product.reducer';
import { ProductEffects } from './products/product.effects';
import { isDevMode } from '@angular/core';

export const appConfig: ApplicationConfig = {
  providers: [
    provideStore(),                                    // Store racine vide
    provideState('products', productsReducer),         // Feature slice
    provideEffects(ProductEffects),                    // Effects de la feature
    provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }),
  ],
};
Structure recommandée d'une feature NgRx :
  • products/models/product.model.ts — Interface TypeScript
  • products/services/product.service.ts — Service HTTP Angular
  • products/store/product.actions.ts — Déclarations d'actions
  • products/store/product.reducer.ts — Reducer + initialState
  • products/store/product.effects.ts — Effets asynchrones
  • products/store/product.selectors.ts — Sélecteurs mémorisés
  • products/store/index.ts — Barrel re-export

FAQ NgRx

NgRx suit le versioning d'Angular. Pour Angular 17, utilisez NgRx 17 ; pour Angular 18, NgRx 18. La commande ng add @ngrx/store@latest installe automatiquement la version compatible. Les versions 16+ supportent les Signals via selectSignal().

  • NgRx Store : état global partagé à l'échelle de l'application
  • ComponentStore : état local à un composant/feature, plus simple que NgRx global
  • Signal Store (NgRx 17+) : alternative moderne basée sur les Signals Angular, moins de boilerplate, idéal pour les nouvelles applications
Pour les nouvelles applications Angular 17+, envisagez le Signal Store pour sa concision.

Installez l'extension Redux DevTools dans Chrome/Firefox puis ajoutez provideStoreDevtools() dans votre config. Vous pouvez alors visualiser toutes les actions dispatchées, inspecter l'état avant/après chaque action, et voyager dans le temps (time-travel debugging) pour reproduire des bugs.

Par défaut, EntityAdapter utilise la propriété id. Pour un UUID ou un autre champ, configurez le sélecteur d'ID :
createEntityAdapter<Product>({ selectId: p => p.uuid })
Vous pouvez aussi trier les entités à l'initialisation avec sortComparer :
createEntityAdapter<Product>({
  sortComparer: (a, b) => a.name.localeCompare(b.name)
})

Ajoutez des champs de pagination dans votre state interface :
interface ProductsState extends EntityState<Product> {
  loading: boolean;
  error: string | null;
  pagination: {
    currentPage: number;
    pageSize: number;
    totalItems: number;
  };
}
Puis créez des actions dédiées : loadProductsPage avec props<{ page: number; size: number }>().

Les Reducers sont des fonctions pures — testez-les directement avec Jest/Jasmine en appelant la fonction avec un état initial et une action.

Pour les Effects, utilisez provideMockActions() de @ngrx/effects/testing et hot()/cold() de jasmine-marbles pour simuler des observables RxJS dans les tests unitaires.

Conclusion

Ce générateur NgRx vous fournit un boilerplate TypeScript propre et prêt à l'emploi pour vos features Angular. Il couvre les 5 piliers essentiels — Actions, Reducer avec EntityAdapter, Effects RxJS, Selectors mémorisés et la configuration du module — en vous laissant choisir précisément les opérations CRUD dont vous avez besoin.

Pour aller plus loin, consultez la documentation officielle NgRx et explorez les NgRx Schematics pour automatiser la génération de code directement depuis la CLI Angular.

Partager