~2 min de lecture
Angular : Comprendre les Smart et Dumb Components
Quand on débute avec Angular, on a vite tendance à tout mettre dans le même composant. Et puis au fur et à mesure, on découvre que structurer son UI, ce n’est pas juste une histoire de découpage visuel. C’est aussi une histoire de responsabilités.
C’est là qu’interviennent les concepts de Smart Components (ou container) et Dumb Components (ou présentational).
🌟 Le principe
| Type de composant | Rôle principal |
|---|---|
| Smart Component | Gère la logique métier, l’état, les appels API, etc. |
| Dumb Component | Affiche une UI à partir d’inputs, émet des outputs pour informer le parent |
🧠 Smart Component : le cerveau
Un composant intelligent est responsable de :
- Récupérer les données (via un service http, un store... Peu importe)
- Gérer les états de chargement, d’erreur
- Décider quoi afficher ou transmettre
💡 Il agit comme un chef d’orchestre. Il est composé de plusieurs composants "dumb" pour afficher un tout cohérent.
Exemple :
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TodoList, TodoForm],
template: `
<app-todo-form (add)="onAdd($event)" />
@if (todos.value().length) {
<app-todo-list
[todos]="todos.value()"
(toggleChange)="onToggle($event)"
(deleteChange)="onDelete($event)"
/>
}
`,
})
export class TodosPage {
private readonly todoStore = inject(TodoStore);
protected readonly todos = rxResource({
stream: () => this.todoStore.getAll()
});
async onAdd(label: string): Promise<void> {
const todo: Todo = await firstValueFrom(this.todoStore.add(label));
this.todos.update(((list) => [...list, todo]));
}
async onToggle(todo: Todo): Promise<void> {
const toggledTodo: Todo = await firstValueFrom(this.todoStore.toggle(todo.id));
this.todos.update((list) =>
list.map((t) => (t.id === toggledTodo.id ? toggledTodo : t))
);
}
async onDelete(id: string): Promise<void> {
await firstValueFrom(this.todoStore.remove(id));
this.todos.update((list) => list.filter((t) => t.id !== id));
}
}
Souvent, il s'agit de composants "Page" ou des composants "blocs" constituant une page.
🎨 Dumb Component : l'artiste
Un composant "bête", ce n’est pas une insulte. C’est au contraire un composant purement réutilisable, sans dépendance à l’application.
Ses caractéristiques :
- Ne sait pas d'où viennent les données
- Affiche des données
- Émet des actions
@Component({
selector: 'app-todo-list',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<ul>
@for(todo of todos(); track todo.id) {
<li>
<span (click)="toggleChange.emit(todo)">
{{ todo.label }}
@if(todo.done) {
<small>(fait)</small>
}
</span>
<button (click)="deleteChange.emit(todo.id)">Supprimer</button>
</li>
}
</ul>
`,
})
export class TodoListComponent {
readonly todos = input<ReadonlyArray<Todo>>([]);
readonly toggleChange = output<Todo>();
readonly deleteChange = output<Todo['id']>();
}
✅ Avantages de cette séparation
- Lisibilité : on comprend vite ce qui relève de l’affichage ou de la logique.
- Testabilité : les composants dumb sont faciles à tester.
- Réutilisabilité : un composant "dumb" peut être utilisé dans plusieurs contextes.
- Évolutivité : plus simple de faire évoluer une app bien structurée.
🧱 Dans une architecture clean ?
C’est un alignement parfait :
- Les use cases se branchent dans les smart components.
- Les dumb components deviennent les éléments d’interface réutilisables et testables.
Conclusion
Adopter cette distinction Smart/Dumb te permet d’écrire du code plus propre, plus maintenable, et plus modulaire. Et en Angular, cette séparation s’intègre naturellement avec le système de composants, d’inputs/outputs, et de services.
Tu veux voir ça en action ? Explore nos modules sur easyangularkit.com et découvre comment structurer tes composants avec méthode.