Coding exercise: build a Todo application in Angular.
Model answer
Use a signal for the list, a computed value for the remaining count, and @for with a track expression. Update signals immutably.
@Component({
selector: 'app-todos',
imports: [FormsModule],
template: `
<form (ngSubmit)="add()">
<input name="text" [(ngModel)]="text" placeholder="New task" />
<button>Add</button>
</form>
<p>{{ remaining() }} left</p>
<ul>
@for (t of items(); track t.id) {
<li>
<input type="checkbox" [checked]="t.done" (change)="toggle(t.id)" />
<span [style.text-decoration]="t.done ? 'line-through' : 'none'">{{ t.text }}</span>
<button type="button" (click)="remove(t.id)">Delete</button>
</li>
} @empty { <li>Nothing to do.</li> }
</ul>`,
})
export class Todos {
text = '';
items = signal<{ id: string; text: string; done: boolean }[]>([]);
remaining = computed(() => this.items().filter((t) => !t.done).length);
add() {
if (!this.text.trim()) return;
this.items.update((l) => [...l, { id: crypto.randomUUID(), text: this.text.trim(), done: false }]);
this.text = '';
}
toggle(id: string) { this.items.update((l) => l.map((t) => (t.id === id ? { ...t, done: !t.done } : t))); }
remove(id: string) { this.items.update((l) => l.filter((t) => t.id !== id)); }
}Extras: move the list into an injectable service so other components can share it, and save to localStorage.
Tips
- Mention that track is required in @for and should be a stable id.
Practise it yourself in the React and Angular code playground.
Learn this topic
Angular Tutorial: Beginner to Advanced .
Sign in to add your own answer.
More questions
- Coding exercise: create a search box with debounce in Angular.
- Coding exercise: fetch API data and display it in a table in Angular.
- Coding exercise: implement pagination in Angular.
- Coding exercise: create a login form with validation in Angular.
- Coding exercise: build a parent-child communication example in Angular.
- Coding exercise: implement sorting and filtering in Angular.