← Retour au blog
Angular 17control-flowperformancechange-detection@for @if @switch

Control Flow @if, @for, @switch dans Angular 17+ : maîtriser la syntaxe déclarative moderne

Angular 17 marque un tournant : la fin de la syntaxe astérisque héritée (*ngIf, *ngFor, *ngSwitch) au profit d'une approche déclarative native, alignée sur le futur du framework. Ces directives structural redessinées—@if, @for, @switch—ne sont pas juste une refonte cosmétique. Elles offrent une meilleure traçabilité du contrôle de flux, une gestion du change detection plus fine, et une surface d'erreur réduite. Pour un développeur Angular en production, comprendre ces mécanismes n'est pas optionnel : c'est la différence entre une app qui scale et une qui lag à 10 000 items.

La syntaxe @if : déclaration sans magie

La directive @if remplace *ngIf avec une grammaire explicite et plus lisible. Au lieu de *ngIf="isVisible" , vous écrivez simplement @if (isVisible) { } . Le corps du bloc est du template standard, sans wrapper ng-template. La différence fondamentale : @if évalue l'expression dans le contexte du composant, sans passer par un objet de contexte implicite. Cela signifie que les erreurs de binding sont détectées plus tôt, au moment de la compilation Angular (en mode strict). L'avantage majeur vient de la branche @else : au lieu de nester deux structures, vous écrivez @if (condition) { contenu } @else { contenu alternatif } . Pour les cas multiples, enchaînez : @if (x > 10) { A } @else if (x > 5) { B } @else { C } . Cette chaîne est bien plus lisible qu'un empilement de *ngIf imbriqués.

Exemple concret : un formulaire de panier e-commerce. Avant Angular 17, vous aviez trois *ngIf imbriqués pour gérer (panier vide, panier en cours de chargement, panier rempli). Avec @if, le code se lit comme du pseudocode : @if (loading) { spinner } @else if (items.length === 0) { message vide } @else { tableau des articles } . La lisibilité gagne immédiatement, et les mainteneurs futurs ne devront pas déplier mentalement trois niveaux de template.

La syntaxe @for : itération optimisée et trackBy intégré

La directive @for remplace *ngFor avec une syntaxe plus proche de JavaScript standard : @for (item of items; track item.id) . Notez le paramètre track : c'est la clé de l'optimisation. Dans l'ancien paradigme, vous deviez penser à implémenter une fonction trackBy séparée et la passer en paramètre. Ici, trackBy est obligatoire et visible dans la syntaxe. Angular refuse de compiler votre template si vous oubliez de spécifier comment tracer chaque élément. Cette contrainte est un cadeau déguisé : elle force les bonnes pratiques et évite les re-rendus catastrophiques de listes longues.

Le paramètre track accepte une expression ou une fonction. Pour une liste de produits avec un ID unique, vous écrivez track item.id . Pour une structure imbriquée, vous pouvez composer : track item.metadata.uuid . Si vous devez un peu plus de logique, vous déléguez à une méthode du composant : track trackItem(item) où trackItem(item) { return item.id; } . Contrairement à *ngFor où le trackBy était optionnel (et souvent oublié), @for le rend syntaxiquement impossible à ignorer. Une base de 1 000 produits avec un mauvais trackBy = DOM thrashing et 500ms de lag. Une base de 1 000 produits avec @for et un bon track = lissage à 16ms par frame.

Exemple pratique : afficher une liste de commentaires avec pagination. Chaque commentaire a un id unique. Avant : *ngFor="let comment of comments; trackBy: trackByCommentId" + une méthode trackByCommentId dans le composant. Maintenant : @for (comment of comments; track comment.id) { <app-comment [data]="comment" /> } . Le code est plus compact, et n'importe quel reviewer voit immédiatement que le tracking est en place.

La syntaxe @switch : alternatives sans faux positifs

@switch remplace ngSwitch + [ngSwitchCase] + [ngSwitchDefault]. La syntaxe devient : @switch (value) { @case (1) { A } @case (2) { B } @default { C } } . Pas de variable intermédiaire, pas de binding sur plusieurs directives. Le compilateur Angular valide que tous les cas possibles sont couverts (si le type est un enum, par exemple). Cela prévient les bugs silencieux où une valeur tombe dans le vide faute de cas correspondant.

Cas d'usage : un composant de statut de commande (pending, shipped, delivered, cancelled). Avant, vous aviez un ngSwitch sur une variable, puis autant de ngSwitchCase que de statuts, avec un ngSwitchDefault pour les cas inattendus. Maintenant : @switch (orderStatus) { @case ('pending') { icône et texte pending } @case ('shipped') { tracking } @case ('delivered') { bouton avis } @default { erreur } } . Le gain en clarté est énorme, et le compilateur TypeScript renforce la cohérence des types.

Change detection et performance : le vrai bénéfice

Ces directives ne sont pas juste une réécriture syntaxique. Angular 17 a redessiné le moteur de change detection pour fonctionner en tandem avec ces structures. Chaque @if, @for, @switch crée un bloc de détection isolé. Quand une condition change, seul le bloc concerné est réévalué, pas la totalité du template. Avec OnPush et les signaux, cette isolation devient encore plus puissante : un changement d'état dans un signal granulaire ne déclenche le re-render que des blocs qui dépendent de ce signal.

Prenez une page avec 10 listes indépendantes, affichées conditionnellement. Avec *ngFor et *ngIf, le moindre changement dans l'une risque de déclencher une vérification globale (sauf si vous avez déjà mis OnPush partout, ce qui n'est pas le cas dans 80% des codebases). Avec @for et @if, chaque liste est une île : vous modifiez une liste, seule sa @for est réévaluée. Sur un dashboard de 200+ items, cela se traduit par 50-70% de réduction en temps de change detection.

Le piège courant : l'abus de track avec des expressions complexes

Beaucoup de développeurs, à l'arrivée de @for, écrivent : track item.name + item.category + item.timestamp . C'est une concaténation de strings qui change à chaque rendu si l'une de ces propriétés varie. Le résultat : Angular considère chaque item comme nouveau, même s'ils ont le même identifiant logique. Cela détruit l'optimisation que @for est censée apporter. Le trackBy doit être une valeur immutable et unique par item : un ID, un UUID, ou au pire une clé composite stable (genre ${item.userId}-${item.postId} ). Évitez les expressions qui dépendent de propriétés mutables ou calculées à chaque rendu.

Un autre piège : utiliser l'index comme track. @for (item of items; track $index) fonctionne, mais c'est un anti-pattern. Si vous réordonnez la liste, les index changent, et Angular recrée tous les DOM. Vous perdez l'état des inputs enfants et les animations. La seule exception : une liste statique où l'ordre ne change jamais (rare).

Intégration avec les signaux et les streams RxJS

Si votre composant utilise des signaux (Signal<Item[]>) ou des observables (Observable<Item[]>), @for fonctionne de manière transparente. Avec les signaux, Angular détecte automatiquement les mutations et met à jour le DOM. Avec les observables, utilisez async en pipeline : @for (item of items$ | async; track item.id) . Attention : l'observable doit être stable (pas recréé à chaque détection), sinon vous perdez les bénéfices de change detection. Préférez les signaux quand c'est possible—ils s'intègrent nativement à @for sans boilerplate.

Migration et compatibilité

Angular 17 supporte toujours *ngIf, *ngFor et ngSwitch. Vous n'êtes pas forcé de migrer immédiatement. Cependant, les nouvelles codebases devraient partir sur @if, @for, @switch. Pour les projets existants, la migration est progressive : remplacez directive par directive, template par template. Les outils de migration automatique (via ng update ) gèrent les cas simples, mais vérifiez le résultat sur les patterns complexes (trackBy avec logique métier, par exemple).

Conclusion

@if, @for, @switch ne sont pas juste des alias cosmétiques. Ils incarnent une philosophie de clarté et de performance. Trackby n'est plus optionnel, les conditions sont explicites, et le change detection est granulaire. Si vous construisez une app Angular moderne, ces directives doivent être vos réflexes. Elles demandent un peu de discipline (bien choisir le track), mais les gains en maintenabilité et en performance les justifient largement. Testez-les sur un petit composant, mesurez le temps de détection avec Angular DevTools, puis migrez progressivement votre codebase.

Développeur Angular & Mobile freelance — Strasbourg.

© 2026 Emilien Pons — Tous droits réservés.Conçu avec Angular, PrimeNG et ❤️