Démonstration 2 : Service, Observable, réactivité et formulaires
Nous allons maintenant faire évoluer notre application pour adopter une architecture plus robuste et professionnelle. L'objectif est de séparer la gestion des données de l'affichage en introduisant un service. Ce service agira comme une fausse API (une "API en mémoire"), gérant l'état de nos tâches.
Nous allons également créer un formulaire réactif pour ajouter de nouvelles tâches, ce qui nous permettra de voir comment la liste se met à jour en temps réel grâce à la puissance des signaux.
Architecture cible : service et réactivité
Ce diagramme montre comment les composants interagissent avec le service pour lire les données (flux de réactivité) et pour envoyer des actions (flux d'événement).
graph TD
subgraph "Flux de Données (Réactivité)"
direction TB
S(TodoApiService) -- "gère" --> SP["signal privé todos"]
SP -- "expose" --> SR["signal public todos$.asReadonly()"]
TLC(TodoListComponent) -- "lit (s'abonne)" --> SR
TLC -- "affiche les données" --> HTML[Template HTML]
end
subgraph "Flux d'Action (Événement)"
direction LR
User[Utilisateur] -- "clique" --> HTML
HTML -- "appelle (click)='toggleTodo(id)'" --> TLC
TLC -- "délègue l'action" --> S
S -- "met à jour l'état" --> SP
end
Objectifs pédagogiques
À la fin de cette démonstration, vous saurez :
- Créer un service pour centraliser la logique et l'état des données.
- Utiliser l'injection de dépendances avec
inject()pour fournir le service aux composants. - Exposer un état réactif depuis un service en utilisant un
signalpublic en lecture seule (.asReadonly()). - Créer un formulaire réactif complet avec
FormBuilderet des validateurs. - Gérer la soumission d'un formulaire, appeler une méthode de service asynchrone (
Observable) et gérer la navigation. - Construire une application entièrement réactive où les changements de données dans le service sont instantanément reflétés dans l'interface utilisateur.
Installation
- Ouvrir le projet sous WebStorm.
- Ouvrir un terminal dans WebStorm.
- Lancer la commande
npm installpour installer les dépendances. - Lancer la commande
ng servepour démarrer le serveur de développement. - Ouvrir le navigateur à l'adresse
http://localhost:4200/.
Flux de soumission du formulaire
L'étape 3 vous demandera de gérer la création d'une tâche. Voici le flux logique de la soumission du formulaire que vous allez implémenter.
graph TD
A["Utilisateur clique Enregistrer"] --> B(TodoFormComponent.saveTodo)
B -- "Vérifie" --> C{Formulaire valide ?}
C -- "Non" --> D[Fin : Affiche erreurs de validation]
C -- "Oui" --> E[Prépare le payload des données]
E --> F(Appelle TodoApiService.createTodo)
F -- "Retourne" --> G["Observable simule délai réseau"]
B -- "Souscrit à l'Observable" --> G
G -- "Émet la tâche créée (next)" --> H(Callback .subscribe)
H -- "Appelle" --> I(Router.navigate vers la liste)
I --> J[Fin : Redirection effectuée]
Instructions
Le projet est déjà configuré. Votre travail est de compléter le code dans les fichiers indiqués en suivant les // TODO.
Étape 1 : le cerveau de l'application - le service
Ouvrez le fichier app/core/services/todo.service.ts. C'est ici que nous allons centraliser toute la gestion de nos tâches.
// TODO 1.1: Créer la source de vérité.
- Déclarez un
signalprivé nommétodospour stocker le tableau de tâches.
// TODO 1.2: Exposer les données de manière sécurisée.
- Déclarez une propriété publique
todos$qui expose le signaltodosen lecture seule grâce à la méthode.asReadonly(). C'est ce que les composants utiliseront pour lire les données.
// TODO 1.3: Implémenter la création d'une tâche.
- Dans la méthode
createTodo, créez un nouvel objetTodo. - Utilisez
this.todos.update()pour ajouter la nouvelle tâche à la liste. - Retournez la nouvelle tâche dans un
Observablequi simule un délai réseau avecof(...).pipe(delay(...)).
// TODO 1.4: Implémenter les interactions.
- Complétez les méthodes
toggleTodoetdeleteTodopour qu'elles mettent à jour le signal privétodosen utilisant.map()(pour basculer) et.filter()(pour supprimer).
Étape 2 : connecter la liste au Service
Ouvrez le fichier app/features/todo/todo-list/todo-list.component.ts. Ce composant ne gérera plus les données lui-même, il va simplement les afficher et déléguer les actions au service.
// TODO 2.1: Injecter le service.
- Utilisez
inject(TodoApiService)pour obtenir une instance de votre service.
// TODO 2.2: Se connecter au flux de données.
- Connectez le signal
todosdu composant directement au signal publictodos$de votre service.
// TODO 2.3: Déléguer les actions.
- Dans les méthodes
toggleTodoetdeleteTododu composant, appelez simplement les méthodes correspondantes de votre service.
Étape 3 : créer le formulaire d'ajout
Ouvrez le fichier app/features/todo/todo-form/todo-form.component.ts. Nous allons construire ici le formulaire de création.
// TODO 3.1: Injecter les outils nécessaires.
- Injectez le
FormBuilder, leRouteret votreTodoApiService.
// TODO 3.2: Construire le formulaire.
- Utilisez
this.fb.nonNullable.group({...})pour définir la structure de votre formulaire avec les champstitleetcompletedet leurs validateurs.
// TODO 3.3: Gérer la soumission.
- Dans la méthode
saveTodo, vérifiez si le formulaire est valide. - Préparez le
payload(les données à envoyer) à partir des valeurs du formulaire. - Appelez la méthode
createTodode votre service. Comme elle retourne unObservable, vous devez y souscrire avec.subscribe({...}). - Dans le
nextde la souscription, redirigez l'utilisateur vers la liste des tâches avecthis.router.navigate(...).
Étape 4 : activer les interactions dans les templates
Il ne reste plus qu'à connecter les actions dans les fichiers HTML.
// TODO 4.1: Danstodo-list.component.html
- Assurez-vous que les événements
(click)sur le<span>et le bouton de suppression appellent bien les méthodestoggleTodo(todo.id)etdeleteTodo(todo.id).
// TODO 4.2: Danstodo-form.component.html
- Liez le
formGroupà la balise<form>. - Liez l'événement
(ngSubmit)à votre méthodesaveTodo(). - Connectez chaque
inputà sonformControlName.
Une fois toutes les étapes terminées, votre application sera entièrement fonctionnelle... et réactive !