150 lines
6.3 KiB
Markdown
150 lines
6.3 KiB
Markdown
# Démonstration 1 : Todo List (Hello, Signals!) - Gestion d'une Todo-List locale
|
|
|
|
Bienvenue dans cette première démonstration pratique d'Angular !
|
|
L'objectif est de construire une application de type "Todo List" simple et entièrement fonctionnelle.
|
|
Toutes les données et la logique seront contenues dans un seul composant,
|
|
ce qui nous permettra de nous concentrer sur les concepts fondamentaux d'Angular moderne.
|
|
|
|
---
|
|
|
|
### Objectifs Pédagogiques
|
|
|
|
À la fin de cette démonstration, vous saurez :
|
|
|
|
* **Créer un état réactif** avec `signal()` pour stocker les données de votre application.
|
|
* **Dériver un état** à partir d'un autre avec `computed()` pour des calculs automatiques.
|
|
* **Gérer les entrées utilisateur** de manière robuste avec `FormControl`.
|
|
* **Mettre à jour l'état** de manière immuable en utilisant la méthode `.update()` des signaux.
|
|
* **Afficher dynamiquement des données** dans un template en utilisant la nouvelle syntaxe `@if` et `@for`.
|
|
* **Lier des événements** du template (comme un clic) à des méthodes dans votre composant.
|
|
|
|
Voici un aperçu de la circulation des données que vous allez construire :
|
|
|
|
```mermaid
|
|
graph TD
|
|
subgraph "État (Signals)"
|
|
A["todos = signal<Todo[]>"]
|
|
B[remainingTodos = computed]
|
|
end
|
|
|
|
subgraph "Formulaire"
|
|
C[newTodoTitle = FormControl]
|
|
end
|
|
|
|
subgraph "Actions (Méthodes)"
|
|
E[addTodo]
|
|
F[removeTodo]
|
|
G[toggleTodo]
|
|
end
|
|
|
|
subgraph "Vue (Template HTML)"
|
|
D[Affichage & Événements]
|
|
end
|
|
|
|
%% Dépendances de données
|
|
A -- "dérive de" --> B
|
|
A -- "lu par" --> D
|
|
B -- "lu par" --> D
|
|
C -- "lu par" --> D
|
|
|
|
%% Flux d'actions
|
|
D -- "clic / keydown" --> E
|
|
D -- "clic" --> F
|
|
D -- "clic" --> G
|
|
D -- "saisie" --> C
|
|
|
|
E -- "appelle todos.update()" --> A
|
|
F -- "appelle todos.update()" --> A
|
|
G -- "appelle todos.update()" --> A
|
|
E -- "appelle .reset()" --> C
|
|
```
|
|
|
|
-----
|
|
|
|
### 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/`.
|
|
|
|
---
|
|
|
|
### Instructions
|
|
|
|
Le projet est volontairement déjà configuré.
|
|
|
|
Votre mission est de compléter le code dans les fichiers `todo-list.component.ts` et `todo-list.component.html` en suivant les indications `// TODO`.
|
|
|
|
Vous travaillerez sur deux fichiers qui interagissent comme suit :
|
|
|
|
```mermaid
|
|
graph LR
|
|
subgraph "Logique (Étape 1)"
|
|
TS[todo-list.component.ts]
|
|
end
|
|
subgraph "Vue (Étape 2)"
|
|
HTML[todo-list.component.html]
|
|
end
|
|
|
|
TS -- "expose les données (signals, formControl)" --> HTML
|
|
HTML -- "déclenche les méthodes (click), (keydown.enter)" --> TS
|
|
```
|
|
|
|
#### Étape 1 : la logique du composant (`todo-list.component.ts`)
|
|
|
|
Ouvrez le fichier `app/features/todo/todo-list/todo-list.component.ts`.
|
|
Nous allons d'abord définir l'état et les actions de notre composant.
|
|
|
|
1. **`// TODO 1.1` : Créer le signal d'état principal.**
|
|
* Déclarez une propriété `readonly todos` et initialisez-la avec `signal<Todo[]>([...])`.
|
|
* Utilisez le tableau de tâches fourni dans les commentaires pour avoir des données de départ.
|
|
|
|
2. **`// TODO 1.2` : Créer un signal dérivé (computed).**
|
|
* Déclarez une propriété `readonly remainingTodos` et utilisez la fonction `computed()` pour calculer automatiquement le nombre de tâches non complétées à partir du signal `todos`.
|
|
|
|
3. **`// TODO 1.3` : Créer le contrôle de formulaire.**
|
|
* Déclarez une propriété `readonly newTodoTitle` et initialisez-la avec un `new FormControl()`.
|
|
* Assurez-vous qu'il ne peut pas être nul (`nonNullable: true`) et ajoutez des validateurs pour qu'il soit requis (`Validators.required`) et ait une longueur minimale (`Validators.minLength(3)`).
|
|
|
|
4. **`// TODO 1.4` : Implémenter la méthode `addTodo()`.**
|
|
* À l'intérieur de la méthode, vérifiez d'abord si `newTodoTitle` est valide.
|
|
* Créez un nouvel objet `Todo`.
|
|
* Utilisez `this.todos.update(...)` pour ajouter la nouvelle tâche à la liste existante.
|
|
* Réinitialisez le champ de saisie avec `.reset()`.
|
|
|
|
5. **`// TODO 1.5` : Implémenter la méthode `removeTodo()`.**
|
|
* Utilisez `this.todos.update(...)` et la méthode `.filter()` des tableaux pour retourner une nouvelle liste sans la tâche à supprimer.
|
|
|
|
6. **`// TODO 1.6` : Implémenter la méthode `toggleTodo()`.**
|
|
* Utilisez `this.todos.update(...)` et la méthode `.map()` des tableaux pour créer une nouvelle liste où l'état `completed` de la tâche ciblée est inversé.
|
|
|
|
#### Étape 2 : l'affichage et les interactions (`todo-list.component.html`)
|
|
|
|
Ouvrez le fichier `app/features/todo/todo-list/todo-list.component.html`.
|
|
Maintenant que la logique est prête, connectons-la à notre vue.
|
|
|
|
1. **`// TODO 2.1` : Connecter le champ de saisie.**
|
|
* Liez l'input au `FormControl` que vous avez créé en utilisant la liaison de propriété `[formControl]`.
|
|
* Ajoutez un événement `(keydown.enter)` pour appeler la méthode `addTodo()` lorsque l'utilisateur appuie sur "Entrée".
|
|
|
|
2. **`// TODO 2.2` : Connecter le bouton "Ajouter".**
|
|
* Liez l'événement `(click)` du bouton à la méthode `addTodo()`.
|
|
* Désactivez le bouton si le formulaire est invalide en utilisant `[disabled]`.
|
|
|
|
3. **`// TODO 2.3` : Afficher la liste des tâches.**
|
|
* Utilisez un bloc `@if` pour vérifier si la liste `todos()` contient des éléments.
|
|
* À l'intérieur, utilisez une boucle `@for` pour itérer sur `todos()`. N'oubliez pas le `track todo.id` pour les performances !
|
|
* Affichez le titre de la tâche en utilisant l'interpolation `{{ ... }}`.
|
|
* Appliquez la classe `.completed` dynamiquement avec `[class.completed]`.
|
|
|
|
4. **`// TODO 2.4` : Ajouter les actions sur chaque tâche.**
|
|
* Dans la boucle `@for`, liez l'événement `(click)` du `<span>` à la méthode `toggleTodo(todo.id)`.
|
|
* Liez l'événement `(click)` du bouton de suppression à la méthode `removeTodo(todo.id)`.
|
|
|
|
5. **`// TODO 2.5` : Afficher le compteur de tâches restantes.**
|
|
* Dans le `<footer>`, affichez la valeur du signal `remainingTodos()` en utilisant l'interpolation. N'oubliez pas les parenthèses `()` pour lire la valeur d'un signal !
|
|
|
|
Une fois toutes les étapes terminées, votre application devrait être entièrement fonctionnelle !
|