Replay

Apprendre Angular en 1h

Je me donne un objectif: vous faire découvrir et apprendre Angular en 1 heure: composant, syntaxe dans les templates, les directives, les signaux, les routeurs, les services, l'injection de dépendances, les observables et les requêtes HTTP. Le nécessaire pour faire une application Angular !.

Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Initialisation d'un NgRx Signal Store

L'initialisation d'un Signal Store est la première étape pour mettre en place une gestion d'état efficace. Cette phase consiste à définir la structure de votre état et à configurer votre store avec les fonctionnalités dont vous avez besoin.

Comprendre l'initialisation d'un état

Dans la vie quotidienne, l'initialisation peut être comparée à la préparation d'une nouvelle maison avant d'y emménager. Vous décidez où vont les meubles (structure de données), quelle décoration installer (valeurs par défaut), et comment organiser les pièces (fonctionnalités du store). Une fois cette configuration initiale terminée, votre maison est prête à être habitée et utilisée.

Structure de base d'un Signal Store

Un Signal Store est créé à l'aide de la fonction signalStore() qui accepte plusieurs "features" sous forme de fonctions. Ces features définissent comment votre store se comporte :

ts
import { signalStore, withState } from '@ngrx/signals';

// Définir l'interface de l'état
interface UserState {
  users: User[];
  selectedUserId: number | null;
  isLoading: boolean;
  error: string | null;
}

// Définir l'état initial
const initialState: UserState = {
  users: [],
  selectedUserId: null,
  isLoading: false,
  error: null
};

// Créer le store avec l'état initial
export const UserStore = signalStore(
  withState(initialState)
);

TYPAGE

Il est recommandé de toujours définir une interface pour votre état afin de bénéficier du typage TypeScript. Cela vous aidera à éviter les erreurs de développement.

Options d'injection

Vous pouvez spécifier comment le store sera injecté dans vos composants. Par défaut, chaque composant qui injecte le store aura sa propre instance. Pour partager le même état à travers l'application, utilisez providedIn: 'root' :

ts
// Store global partagé dans toute l'application
export const UserStore = signalStore(
  { providedIn: 'root' }, // Rend le store disponible globalement
  withState(initialState)
);

Si vous préférez une instance par composant (utile pour les états locaux) :

ts
// Store local par composant
export const CounterStore = signalStore(
  withState({ count: 0 })
);

@Component({
  // ...
  providers: [CounterStore] // Fournit une nouvelle instance pour ce composant
})
export class CounterComponent { 
  // ... 
}

Combiner plusieurs features

NgRx Signal Store est modulaire et permet de combiner plusieurs fonctionnalités :

ts
import { signalStore, withState, withComputed, withMethods } from '@ngrx/signals';
import { computed } from '@angular/core';

export const TaskStore = signalStore(
  // État initial
  withState<TaskState>({
    tasks: [],
    filter: 'all'
  }),
  
  // Propriétés calculées
  withComputed((store) => ({
    filteredTasks: computed(() => {
      const tasks = store.tasks();
      const filter = store.filter();
      
      switch (filter) {
        case 'active':
          return tasks.filter(task => !task.completed);
        case 'completed':
          return tasks.filter(task => task.completed);
        default:
          return tasks;
      }
    }),
    
    completedCount: computed(() => 
      store.tasks().filter(task => task.completed).length
    )
  })),
  
  // Méthodes pour mettre à jour l'état
  withMethods(/* ... */)
);

Initialisation avec des données existantes

Dans certains cas, vous voudrez peut-être initialiser votre store avec des données existantes, comme celles stockées dans le localStorage :

ts
import { signalStore, withState } from '@ngrx/signals';

// Fonction pour charger l'état depuis localStorage
function loadInitialState(): TaskState {
  try {
    const savedState = localStorage.getItem('taskState');
    if (savedState) {
      return JSON.parse(savedState);
    }
  } catch (e) {
    console.error('Failed to load state from localStorage', e);
  }
  
  // État par défaut si rien n'est trouvé
  return {
    tasks: [],
    filter: 'all'
  };
}

// Créer le store avec un état chargé dynamiquement
export const TaskStore = signalStore(
  withState(loadInitialState())
);

Features personnalisées

Pour les configurations réutilisables, vous pouvez créer des features personnalisées :

ts
import { signalStoreFeature, withState, withMethods } from '@ngrx/signals';
import { patchState } from '@ngrx/signals';

// Feature réutilisable pour gérer le chargement
export function withLoading() {
  return signalStoreFeature(
    withState({
      isLoading: false,
      error: null as string | null
    }),
    withMethods((store) => ({
      setLoading(isLoading: boolean) {
        patchState(store, { isLoading });
      },
      setError(error: string | null) {
        patchState(store, { error });
      }
    }))
  );
}

// Utiliser la feature personnalisée
export const UserStore = signalStore(
  withState<UserState>({ users: [] }),
  withLoading() // Ajoute isLoading et error à l'état
);

RÉUTILISATION

Les features personnalisées permettent de réutiliser la logique de gestion d'état à travers différents stores, améliorant ainsi la maintenabilité du code.

Exemple complet