# Syntaxe de flux de contrôle Angular en 2026 : @if, @for, @switch et questions d'entretien > Guide complet sur la syntaxe de flux de contrôle Angular avec @if, @for et @switch. Apprenez à remplacer les directives structurelles par les blocs de contrôle modernes et préparez-vous aux entretiens techniques. - Published: 2026-09-03 - Updated: 2026-09-03 - Author: Anthony Fillion-Maillet - Tags: angular, control-flow, templates, signals - Reading time: 10 min --- La syntaxe de flux de contrôle Angular (`@if`, `@for`, `@switch`) remplace les directives structurelles `*ngIf`, `*ngFor` et `*ngSwitch` qui dominaient les templates Angular depuis des années. Introduite dans Angular 17, stabilisée depuis Angular 18 et devenue la syntaxe par défaut dans Angular 22, cette approche basée sur des blocs rapproche les templates des constructions de programmation standard tout en permettant de meilleures optimisations de performance. > **Migration automatique disponible** > > Exécutez `ng generate @angular/core:control-flow` pour migrer automatiquement les templates existants des directives structurelles vers la nouvelle syntaxe de flux de contrôle. Les deux syntaxes coexistent pendant la migration, mais `ngIf`, `ngFor` et `ngSwitch` sont dépréciées de manière douce depuis Angular 19. ## Pourquoi Angular a remplacé les directives structurelles par la syntaxe de blocs Les directives structurelles nécessitaient l'import de `CommonModule`, utilisaient une microsyntaxe différente de JavaScript et forçaient les développeurs à envelopper le contenu dans des `ng-template` pour les conditions complexes. La nouvelle syntaxe de flux de contrôle résout ces problèmes : - Aucun import requis : `@if`, `@for` et `@switch` sont intégrés au framework - Syntaxe proche de JavaScript : les conditions et boucles se lisent comme du code standard - Support natif de `@else` : plus besoin de références `ng-template` pour les branches else - Meilleur tree-shaking : les blocs de flux de contrôle non utilisés n'ont aucun impact sur la taille du bundle La [documentation Angular](https://angular.dev/guide/templates/control-flow) recommande d'adopter pleinement la syntaxe de blocs dans tous les nouveaux projets et de migrer progressivement les bases de code existantes. ## @if : Rendu conditionnel sans ng-template Le bloc `@if` rend conditionnellement du contenu basé sur une expression véridique. Contrairement à `*ngIf`, il supporte les branches `@else if` et `@else` directement : ```typescript // user-status.component.ts import { Component, input } from '@angular/core'; @Component({ selector: 'app-user-status', template: ` @if (user().role === 'admin') { } @else if (user().role === 'editor') { } @else { } ` }) export class UserStatusComponent { user = input.required<{ role: string }>(); } ``` Le bloc `@if` supporte également l'aliasing de variables avec le mot-clé `as`, qui extrait les valeurs des expressions imbriquées : ```html @if (user().profile?.settings?.theme; as theme) {

Thème actuel : {{ theme }}

} ``` Ce pattern est particulièrement utile lors du travail avec des données asynchrones ou des objets profondément imbriqués, puisque la variable aliasée n'est définie que lorsque la condition est véridique. ## @for : Itération avec expression track obligatoire Le bloc `@for` itère sur tout itérable JavaScript, avec des optimisations pour les tableaux. Contrairement à `*ngFor`, il nécessite une expression `track` : ```typescript // product-list.component.ts import { Component, input } from '@angular/core'; interface Product { id: string; name: string; price: number; } @Component({ selector: 'app-product-list', template: ` @for (product of products(); track product.id) { } @empty {

Aucun produit disponible

} ` }) export class ProductListComponent { products = input.required(); } ``` L'expression `track` indique à Angular comment identifier chaque élément entre les re-rendus. Le choix de la bonne propriété de tracking impacte directement les performances : | Expression Track | Cas d'utilisation | Impact performance | |-----------------|----------|-----------------| | `track item.id` | Éléments avec identifiants uniques | Optimal : mises à jour DOM minimales | | `track $index` | Listes statiques qui ne se réordonnent jamais | Acceptable : re-rendu complet lors du réordonnancement | | `track item` | Tracking par référence | Mauvais : nouvelle référence = nouveau nœud DOM | ### Variables contextuelles dans les blocs @for Angular fournit des variables implicites à l'intérieur des blocs `@for` qui exposent les métadonnées d'itération : ```html @for (item of items(); track item.id; let i = $index, isLast = $last) {
  • {{ i + 1 }}. {{ item.name }}
  • } ``` Variables contextuelles disponibles : - `$index` : position base zéro - `$count` : nombre total d'éléments - `$first`, `$last` : flags booléens pour le premier/dernier élément - `$even`, `$odd` : flags booléens basés sur la parité de l'index ## @switch : Branchement conditionnel type-safe Le bloc `@switch` fournit un pattern matching exhaustif avec comparaison d'égalité stricte (`===`) : ```typescript // status-badge.component.ts import { Component, input } from '@angular/core'; type OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered'; @Component({ selector: 'app-status-badge', template: ` @switch (status()) { @case ('pending') { En attente } @case ('processing') { En cours } @case ('shipped') { Expédié } @case ('delivered') { Livré } } ` }) export class StatusBadgeComponent { status = input.required(); } ``` Contrairement au `switch` JavaScript, le `@switch` d'Angular n'a pas de comportement fallthrough. Les conditions multiples ciblant le même bloc nécessitent des instructions `@case` consécutives : ```html @switch (userRole()) { @case ('admin') @case ('superadmin') { } @case ('editor') @case ('reviewer') { } @default { } } ``` ### Vérification de type exhaustive avec @default never Angular 22 supporte la vérification d'exhaustivité à la compilation. L'utilisation de `@default never;` déclare qu'aucun cas restant ne devrait exister : ```typescript type Theme = 'light' | 'dark' | 'system'; // Erreur de compilation si une valeur Theme manque dans les blocs @case @switch (theme()) { @case ('light') { /* ... */ } @case ('dark') { /* ... */ } @case ('system') { /* ... */ } @default never; } ``` L'ajout d'une nouvelle valeur à l'union `Theme` déclenche une erreur de compilation, forçant les développeurs à gérer tous les cas. ## Intégration de la syntaxe de flux de contrôle avec les Signals Angular Les blocs de flux de contrôle s'intègrent parfaitement avec les [Signals Angular](/technologies/angular/interview-questions/angular-signals), permettant une réactivité fine : ```typescript // dashboard.component.ts import { Component, computed, signal } from '@angular/core'; @Component({ selector: 'app-dashboard', template: ` @if (isLoading()) { } @else if (hasError()) { } @else { @for (item of filteredItems(); track item.id) { } @empty {

    Aucun élément ne correspond à vos filtres

    } } ` }) export class DashboardComponent { items = signal([]); filter = signal(''); isLoading = signal(true); hasError = signal(false); errorMessage = signal(''); // Le signal computed ne recalcule que lorsque les dépendances changent filteredItems = computed(() => this.items().filter(item => item.name.toLowerCase().includes(this.filter().toLowerCase()) ) ); } ``` Lorsqu'un signal se met à jour, Angular réévalue uniquement les blocs de flux de contrôle affectés. Combiné avec la [détection de changement zoneless d'Angular](/blog/angular/angular-19-zoneless-change-detection-performance), cela permet un rendu hautement optimisé. ## Questions d'entretien fréquentes sur le flux de contrôle Angular Les entretiens techniques testent fréquemment la compréhension de la syntaxe de flux de contrôle, particulièrement les différences avec les directives structurelles. ### Question 1 : Pourquoi track est-il obligatoire dans @for ? L'expression `track` fournit à Angular une identité stable pour chaque élément. Sans elle, Angular ne peut pas déterminer efficacement quels nœuds DOM créer, mettre à jour ou détruire lorsque la collection change. Rendre `track` obligatoire force les développeurs à prendre une décision explicite sur l'identité, évitant les pièges de performance du tracking implicite par référence. ### Question 2 : Comment @if diffère-t-il de *ngIf avec async pipe ? Les deux peuvent gérer les observables, mais `@if` avec les signals fournit un accès synchrone aux valeurs : ```typescript // Avec *ngIf et async pipe (legacy)
    {{ user.name }}
    // Avec @if et signals (moderne) @if (user(); as user) {
    {{ user.name }}
    } ``` L'approche avec signals évite la gestion des subscriptions et s'intègre mieux avec la détection de changement d'Angular. ### Question 3 : @switch peut-il remplacer les chaînes complexes @if/@else if ? `@switch` devrait être utilisé lors de la comparaison d'une seule expression avec plusieurs valeurs discrètes. Pour les conditions booléennes complexes impliquant différentes expressions, `@if/@else if` reste plus approprié : ```html @switch (status()) { @case ('active') { ... } @case ('inactive') { ... } } @if (isAdmin() && hasPermission('write')) { ... } @else if (isEditor()) { ... } ``` ### Question 4 : Que se passe-t-il avec @for lorsque la collection est vide ? Lorsque l'itérable ne contient aucun élément, Angular ignore entièrement le contenu du `@for`. Le bloc optionnel `@empty` rend du contenu de secours dans ce cas. Sans `@empty`, rien n'est rendu, ce qui diffère de certains frameworks qui rendent un conteneur vide. ## Migration des directives structurelles vers le flux de contrôle La CLI Angular fournit un schématic de migration automatisé : ```bash # Migrer le projet entier ng generate @angular/core:control-flow # Migrer un répertoire spécifique ng generate @angular/core:control-flow --path=src/app/features ``` Le schématic gère automatiquement la plupart des transformations, mais il faut vérifier la sortie pour les cas limites : | Directive structurelle | Équivalent flux de contrôle | |---------------------|------------------------| | `*ngIf="condition"` | `@if (condition) { }` | | `*ngIf="condition; else elseBlock"` | `@if (condition) { } @else { }` | | `*ngFor="let item of items"` | `@for (item of items; track item) { }` | | `*ngFor="let item of items; index as i"` | `@for (item of items; track item; let i = $index) { }` | | `[ngSwitch]` + `*ngSwitchCase` | `@switch` + `@case` | Après la migration, supprimez les imports `CommonModule` des composants standalone qui n'utilisent plus les directives structurelles. ## Points clés sur la syntaxe de flux de contrôle Angular - La syntaxe de flux de contrôle Angular (`@if`, `@for`, `@switch`) est l'approche recommandée depuis Angular 19, remplaçant les directives structurelles - L'expression `track` dans `@for` est obligatoire : utilisez des identifiants uniques comme `item.id` pour des performances optimales, évitez `track item` qui cause des re-rendus basés sur les références - `@if` supporte les branches natives `@else if` et `@else` sans `ng-template`, et le mot-clé `as` crée des alias pour les valeurs véridiques pour réutilisation - `@switch` utilise l'égalité stricte et n'a pas de fallthrough : les instructions `@case` consécutives ciblent le même bloc, et `@default never;` active la vérification de type exhaustive - Les blocs de flux de contrôle s'intègrent avec les [Signals](/technologies/angular/interview-questions/angular-signals) pour une réactivité fine : seuls les blocs affectés se re-rendent lorsque les signals se mettent à jour - Exécutez `ng generate @angular/core:control-flow` pour migrer automatiquement les templates existants --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/angular/angular-control-flow-syntax-if-for-switch-guide