# 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). ```mermaid 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 `signal` public en lecture seule (`.asReadonly()`). * **Créer un formulaire réactif** complet avec `FormBuilder` et 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 1. Ouvrir le projet sous WebStorm. 2. Ouvrir un terminal dans WebStorm. 3. Lancer la commande `npm install` pour installer les dépendances. 4. Lancer la commande `ng serve` pour démarrer le serveur de développement. 5. 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. ```mermaid 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. 1. **`// TODO 1.1` : Créer la source de vérité.** * Déclarez un `signal` **privé** nommé `todos` pour stocker le tableau de tâches. 2. **`// TODO 1.2` : Exposer les données de manière sécurisée.** * Déclarez une propriété **publique** `todos$` qui expose le signal `todos` en lecture seule grâce à la méthode `.asReadonly()`. C'est ce que les composants utiliseront pour lire les données. 3. **`// TODO 1.3` : Implémenter la création d'une tâche.** * Dans la méthode `createTodo`, créez un nouvel objet `Todo`. * Utilisez `this.todos.update()` pour ajouter la nouvelle tâche à la liste. * Retournez la nouvelle tâche dans un `Observable` qui simule un délai réseau avec `of(...).pipe(delay(...))`. 4. **`// TODO 1.4` : Implémenter les interactions.** * Complétez les méthodes `toggleTodo` et `deleteTodo` pour qu'elles mettent à jour le signal privé `todos` en 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. 1. **`// TODO 2.1` : Injecter le service.** * Utilisez `inject(TodoApiService)` pour obtenir une instance de votre service. 2. **`// TODO 2.2` : Se connecter au flux de données.** * Connectez le signal `todos` du composant directement au signal public `todos$` de votre service. 3. **`// TODO 2.3` : Déléguer les actions.** * Dans les méthodes `toggleTodo` et `deleteTodo` du 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. 1. **`// TODO 3.1` : Injecter les outils nécessaires.** * Injectez le `FormBuilder`, le `Router` et votre `TodoApiService`. 2. **`// TODO 3.2` : Construire le formulaire.** * Utilisez `this.fb.nonNullable.group({...})` pour définir la structure de votre formulaire avec les champs `title` et `completed` et leurs validateurs. 3. **`// 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 `createTodo` de votre service. Comme elle retourne un `Observable`, vous devez y **souscrire** avec `.subscribe({...})`. * Dans le `next` de la souscription, redirigez l'utilisateur vers la liste des tâches avec `this.router.navigate(...)`. #### Étape 4 : activer les interactions dans les templates Il ne reste plus qu'à connecter les actions dans les fichiers HTML. 1. **`// TODO 4.1` : Dans `todo-list.component.html`** * Assurez-vous que les événements `(click)` sur le `` et le bouton de suppression appellent bien les méthodes `toggleTodo(todo.id)` et `deleteTodo(todo.id)`. 2. **`// TODO 4.2` : Dans `todo-form.component.html`** * Liez le `formGroup` à la balise `
`. * Liez l'événement `(ngSubmit)` à votre méthode `saveTodo()`. * Connectez chaque `input` à son `formControlName`. Une fois toutes les étapes terminées, votre application sera entièrement fonctionnelle... et réactive !