TP done
This commit is contained in:
@@ -5,25 +5,28 @@
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Ajouter une nouvelle tâche..."
|
placeholder="Ajouter une nouvelle tâche..."
|
||||||
|
[formControl]="newTodoTitle"
|
||||||
|
(keydown.enter)="addTodo()"
|
||||||
/>
|
/>
|
||||||
|
<button (click)="addTodo()" [disabled]="newTodoTitle.invalid">Ajouter</button>
|
||||||
<button>Ajouter</button>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<ul class="todo-list">
|
@if (todos().length > 0) {
|
||||||
<li>
|
<ul class="todo-list">
|
||||||
<span class="todo-title">
|
@for (todo of todos(); track todo.id) {
|
||||||
Tâche d'exemple
|
<li [class.completed]="todo.completed">
|
||||||
</span>
|
<span (click)="toggleTodo(todo.id)" class="todo-title">
|
||||||
<button class="btn-remove">X</button>
|
{{ todo.title }}
|
||||||
</li>
|
</span>
|
||||||
</ul>
|
<button (click)="removeTodo(todo.id)" class="btn-remove">X</button>
|
||||||
<footer>
|
</li>
|
||||||
<span>X tâche(s) restante(s)</span>
|
}
|
||||||
</footer>
|
</ul>
|
||||||
|
<footer>
|
||||||
|
<span>{{ remainingTodos() }} tâche(s) restante(s)</span>
|
||||||
<p class="empty-state">Bravo, aucune tâche pour le moment !</p>
|
</footer>
|
||||||
|
} @else {
|
||||||
|
<p class="empty-state">Bravo, aucune tâche pour le moment !</p>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,61 +7,62 @@ import { Todo } from '../../../core/models/todo.model';
|
|||||||
templateUrl: './todo-list.component.html',
|
templateUrl: './todo-list.component.html',
|
||||||
styleUrls: ['./todo-list.component.scss'],
|
styleUrls: ['./todo-list.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [ReactiveFormsModule],
|
imports: [ReactiveFormsModule], // Importer ReactiveFormsModule pour utiliser FormControl
|
||||||
})
|
})
|
||||||
export class TodoListComponent {
|
export class TodoListComponent {
|
||||||
|
|
||||||
// --- ÉTAT (State) ---
|
// --- ÉTAT (State) ---
|
||||||
|
|
||||||
// TODO 1.1: Créer un signal pour stocker la liste des tâches.
|
// Le signal `todos` est la source de vérité pour notre liste de tâches.
|
||||||
// Utilisez `signal<Todo[]>()` et initialisez-le avec le tableau ci-dessous.
|
// On utilise `signal<Type>(valeurInitiale)` pour le créer.
|
||||||
/*
|
readonly todos = signal<Todo[]>([
|
||||||
[
|
{ id: 1, title: 'Apprendre les bases d\'Angular', completed: true },
|
||||||
{ id: 1, title: 'Apprendre les bases d\'Angular', completed: true },
|
{ id: 2, title: 'Comprendre les signals', completed: false },
|
||||||
{ id: 2, title: 'Comprendre les signals', completed: false },
|
{ id: 3, title: 'Créer un premier composant', completed: false },
|
||||||
{ id: 3, title: 'Créer un premier composant', completed: false },
|
]);
|
||||||
]
|
|
||||||
*/
|
|
||||||
readonly todos = signal<Todo[]>([]); // <- REMPLACER [] par le tableau ci-dessus
|
|
||||||
|
|
||||||
|
// Un signal `computed` dérive sa valeur d'autres signaux.
|
||||||
|
// Il se met à jour automatiquement quand `todos` change.
|
||||||
|
readonly remainingTodos = computed(() => this.todos().filter(t => !t.completed).length);
|
||||||
|
|
||||||
// TODO 1.2: Créer un signal "computed" pour calculer le nombre de tâches restantes.
|
// Utilisation d'un FormControl pour gérer l'input de manière réactive.
|
||||||
// Utilisez `computed(() => ...)` et filtrez le signal `todos` pour ne compter
|
readonly newTodoTitle = new FormControl('', {
|
||||||
// que les tâches où `completed` est `false`.
|
nonNullable: true,
|
||||||
readonly remainingTodos = computed(() => 0); // <- REMPLACER 0 par le calcul
|
validators: [Validators.required, Validators.minLength(3)],
|
||||||
|
});
|
||||||
|
|
||||||
// TODO 1.3: Créer un FormControl pour le champ de saisie d'une nouvelle tâche.
|
|
||||||
// Utilisez `new FormControl()` avec une chaîne vide comme valeur initiale.
|
|
||||||
// Dans les options, ajoutez `nonNullable: true` et des validateurs :
|
|
||||||
// `Validators.required` et `Validators.minLength(3)`.
|
|
||||||
readonly newTodoTitle = new FormControl(''); // <- AJOUTER les options
|
|
||||||
|
|
||||||
|
|
||||||
// --- ACTIONS ---
|
// --- ACTIONS ---
|
||||||
|
|
||||||
/** Ajoute une nouvelle tâche à la liste. */
|
/** Ajoute une nouvelle tâche à la liste. */
|
||||||
addTodo(): void {
|
addTodo(): void {
|
||||||
// TODO 1.4: Implémenter la logique d'ajout.
|
if (this.newTodoTitle.invalid) {
|
||||||
// 1. Vérifiez si `this.newTodoTitle` est invalide. Si c'est le cas, arrêtez la fonction avec `return;`.
|
return; // Ne rien faire si l'input est invalide
|
||||||
// 2. Créez un objet `newTodo` de type `Todo` avec un `id` unique (Date.now()),
|
}
|
||||||
// le titre provenant de `this.newTodoTitle.value`, et `completed: false`.
|
|
||||||
// 3. Mettez à jour le signal `todos` avec `this.todos.update(currentTodos => [...currentTodos, newTodo])`.
|
const newTodo: Todo = {
|
||||||
// 4. Réinitialisez le champ de saisie avec `this.newTodoTitle.reset()`.
|
id: Date.now(), // Utilisation d'un timestamp pour un ID unique simple
|
||||||
|
title: this.newTodoTitle.value,
|
||||||
|
completed: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// `update` permet de modifier la valeur du signal en se basant sur la valeur actuelle.
|
||||||
|
// C'est une bonne pratique de créer un nouveau tableau (immuabilité).
|
||||||
|
this.todos.update(currentTodos => [...currentTodos, newTodo]);
|
||||||
|
|
||||||
|
this.newTodoTitle.reset(); // Vider le champ de saisie
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Supprime une tâche de la liste. */
|
/** Supprime une tâche de la liste. */
|
||||||
removeTodo(id: number): void {
|
removeTodo(id: number): void {
|
||||||
// TODO 1.5: Implémenter la logique de suppression.
|
this.todos.update(currentTodos => currentTodos.filter(todo => todo.id !== id));
|
||||||
// Utilisez `this.todos.update(currentTodos => ...)` et la méthode `.filter()`
|
|
||||||
// pour retourner un nouveau tableau sans la tâche correspondant à l'`id`.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Bascule l'état "complété" d'une tâche. */
|
/** Bascule l'état "complété" d'une tâche. */
|
||||||
toggleTodo(id: number): void {
|
toggleTodo(id: number): void {
|
||||||
// TODO 1.6: Implémenter la logique de bascule.
|
this.todos.update(currentTodos =>
|
||||||
// Utilisez `this.todos.update(currentTodos => ...)` et la méthode `.map()`.
|
currentTodos.map(todo =>
|
||||||
// Pour chaque `todo` dans le tableau, si son `id` correspond, retournez un nouvel objet
|
todo.id === id ? { ...todo, completed: !todo.completed } : todo
|
||||||
// avec la propriété `completed` inversée (`!todo.completed`). Sinon, retournez le `todo` original.
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user