# Sintaxis de flujo de control en Angular 2026: @if, @for, @switch y preguntas de entrevista > Guía completa sobre la sintaxis de flujo de control de Angular con @if, @for y @switch. Aprende a reemplazar las directivas estructurales por bloques de control modernos y prepárate para entrevistas técnicas. - Published: 2026-09-03 - Updated: 2026-09-03 - Author: Anthony Fillion-Maillet - Tags: angular, control-flow, templates, signals - Reading time: 10 min --- La sintaxis de flujo de control de Angular (`@if`, `@for`, `@switch`) reemplaza las directivas estructurales `*ngIf`, `*ngFor` y `*ngSwitch` que dominaron los templates de Angular durante años. Introducida en Angular 17, estabilizada desde Angular 18 y convertida en la sintaxis predeterminada en Angular 22, este enfoque basado en bloques acerca los templates a las construcciones de programación estándar mientras permite mejores optimizaciones de rendimiento. > **Migración automática disponible** > > Ejecuta `ng generate @angular/core:control-flow` para migrar automáticamente los templates existentes desde las directivas estructurales a la nueva sintaxis de flujo de control. Ambas sintaxis coexisten durante la migración, pero `ngIf`, `ngFor` y `ngSwitch` están deprecadas de forma suave desde Angular 19. ## Por qué Angular reemplazó las directivas estructurales con sintaxis de bloques Las directivas estructurales requerían importar `CommonModule`, usaban una microsintaxis diferente a JavaScript y forzaban a los desarrolladores a envolver el contenido en `ng-template` para condiciones complejas. La nueva sintaxis de flujo de control resuelve estos problemas: - Sin imports requeridos: `@if`, `@for` y `@switch` están integrados en el framework - Sintaxis similar a JavaScript: las condiciones y bucles se leen como código estándar - Soporte nativo de `@else`: no más referencias a `ng-template` para ramas else - Mejor tree-shaking: los bloques de flujo de control no utilizados tienen impacto cero en el bundle La [documentación de Angular](https://angular.dev/guide/templates/control-flow) recomienda adoptar completamente la sintaxis de bloques en todos los proyectos nuevos y migrar progresivamente las bases de código existentes. ## @if: Renderizado condicional sin ng-template El bloque `@if` renderiza contenido condicionalmente basado en una expresión verdadera. A diferencia de `*ngIf`, soporta ramas `@else if` y `@else` directamente: ```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 }>(); } ``` El bloque `@if` también soporta alias de variables con la palabra clave `as`, que extrae valores de expresiones anidadas: ```html @if (user().profile?.settings?.theme; as theme) {

Tema actual: {{ theme }}

} ``` Este patrón es particularmente útil cuando se trabaja con datos asíncronos u objetos profundamente anidados, ya que la variable con alias solo se define cuando la condición es verdadera. ## @for: Iteración con expresión track obligatoria El bloque `@for` itera sobre cualquier iterable de JavaScript, con optimizaciones para arrays. A diferencia de `*ngFor`, requiere una expresión `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 {

No hay productos disponibles

} ` }) export class ProductListComponent { products = input.required(); } ``` La expresión `track` le indica a Angular cómo identificar cada elemento entre re-renderizados. Elegir la propiedad de tracking correcta impacta directamente el rendimiento: | Expresión Track | Caso de uso | Impacto en rendimiento | |-----------------|----------|-----------------| | `track item.id` | Elementos con identificadores únicos | Óptimo: actualizaciones DOM mínimas | | `track $index` | Listas estáticas que nunca se reordenan | Aceptable: re-renderizado completo al reordenar | | `track item` | Tracking por referencia | Malo: nueva referencia = nuevo nodo DOM | ### Variables contextuales en bloques @for Angular proporciona variables implícitas dentro de los bloques `@for` que exponen metadatos de iteración: ```html @for (item of items(); track item.id; let i = $index, isLast = $last) {
  • {{ i + 1 }}. {{ item.name }}
  • } ``` Variables contextuales disponibles: - `$index`: posición base cero - `$count`: número total de elementos - `$first`, `$last`: flags booleanos para el primer/último elemento - `$even`, `$odd`: flags booleanos basados en la paridad del índice ## @switch: Ramificación condicional type-safe El bloque `@switch` proporciona pattern matching exhaustivo con comparación de igualdad estricta (`===`): ```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') { Pendiente } @case ('processing') { Procesando } @case ('shipped') { Enviado } @case ('delivered') { Entregado } } ` }) export class StatusBadgeComponent { status = input.required(); } ``` A diferencia del `switch` de JavaScript, el `@switch` de Angular no tiene comportamiento fallthrough. Las condiciones múltiples que apuntan al mismo bloque requieren instrucciones `@case` consecutivas: ```html @switch (userRole()) { @case ('admin') @case ('superadmin') { } @case ('editor') @case ('reviewer') { } @default { } } ``` ### Verificación de tipos exhaustiva con @default never Angular 22 soporta verificación de exhaustividad en tiempo de compilación. Usar `@default never;` declara que no deberían existir casos restantes: ```typescript type Theme = 'light' | 'dark' | 'system'; // Error de compilación si falta un valor Theme en los bloques @case @switch (theme()) { @case ('light') { /* ... */ } @case ('dark') { /* ... */ } @case ('system') { /* ... */ } @default never; } ``` Agregar un nuevo valor a la unión `Theme` dispara un error de compilación, forzando a los desarrolladores a manejar todos los casos. ## Integración de la sintaxis de flujo de control con Angular Signals Los bloques de flujo de control se integran perfectamente con [Angular Signals](/technologies/angular/interview-questions/angular-signals), permitiendo reactividad de grano fino: ```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 {

    Ningún elemento coincide con tus filtros

    } } ` }) export class DashboardComponent { items = signal([]); filter = signal(''); isLoading = signal(true); hasError = signal(false); errorMessage = signal(''); // El signal computed solo recalcula cuando las dependencias cambian filteredItems = computed(() => this.items().filter(item => item.name.toLowerCase().includes(this.filter().toLowerCase()) ) ); } ``` Cuando un signal se actualiza, Angular reevalúa solo los bloques de flujo de control afectados. Combinado con la [detección de cambios zoneless de Angular](/blog/angular/angular-19-zoneless-change-detection-performance), esto permite un renderizado altamente optimizado. ## Preguntas de entrevista frecuentes sobre el flujo de control de Angular Las entrevistas técnicas frecuentemente evalúan la comprensión de la sintaxis de flujo de control, especialmente las diferencias con las directivas estructurales. ### Pregunta 1: ¿Por qué track es obligatorio en @for? La expresión `track` proporciona a Angular una identidad estable para cada elemento. Sin ella, Angular no puede determinar eficientemente qué nodos DOM crear, actualizar o destruir cuando la colección cambia. Hacer `track` obligatorio fuerza a los desarrolladores a tomar una decisión explícita sobre la identidad, evitando las trampas de rendimiento del tracking implícito por referencia. ### Pregunta 2: ¿Cómo difiere @if de *ngIf con async pipe? Ambos pueden manejar observables, pero `@if` con signals proporciona acceso síncrono a los valores: ```typescript // Con *ngIf y async pipe (legacy)
    {{ user.name }}
    // Con @if y signals (moderno) @if (user(); as user) {
    {{ user.name }}
    } ``` El enfoque con signals evita la gestión de subscripciones y se integra mejor con la detección de cambios de Angular. ### Pregunta 3: ¿Puede @switch reemplazar cadenas complejas @if/@else if? `@switch` debería usarse cuando se compara una sola expresión con múltiples valores discretos. Para condiciones booleanas complejas que involucran diferentes expresiones, `@if/@else if` sigue siendo más apropiado: ```html @switch (status()) { @case ('active') { ... } @case ('inactive') { ... } } @if (isAdmin() && hasPermission('write')) { ... } @else if (isEditor()) { ... } ``` ### Pregunta 4: ¿Qué sucede con @for cuando la colección está vacía? Cuando el iterable no contiene elementos, Angular omite completamente el contenido del `@for`. El bloque opcional `@empty` renderiza contenido de respaldo en este caso. Sin `@empty`, no se renderiza nada, lo cual difiere de algunos frameworks que renderizan un contenedor vacío. ## Migración desde directivas estructurales al flujo de control El CLI de Angular proporciona un schematic de migración automatizado: ```bash # Migrar proyecto completo ng generate @angular/core:control-flow # Migrar directorio específico ng generate @angular/core:control-flow --path=src/app/features ``` El schematic maneja la mayoría de las transformaciones automáticamente, pero hay que revisar la salida para casos límite: | Directiva estructural | Equivalente flujo de control | |---------------------|------------------------| | `*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` | Después de la migración, elimina los imports de `CommonModule` de los componentes standalone que ya no usan directivas estructurales. ## Puntos clave sobre la sintaxis de flujo de control de Angular - La sintaxis de flujo de control de Angular (`@if`, `@for`, `@switch`) es el enfoque recomendado desde Angular 19, reemplazando las directivas estructurales - La expresión `track` en `@for` es obligatoria: usa identificadores únicos como `item.id` para rendimiento óptimo, evita `track item` que causa re-renderizados basados en referencias - `@if` soporta ramas nativas `@else if` y `@else` sin `ng-template`, y la palabra clave `as` crea alias para valores verdaderos para reutilización - `@switch` usa igualdad estricta y no tiene fallthrough: las instrucciones `@case` consecutivas apuntan al mismo bloque, y `@default never;` habilita la verificación de tipos exhaustiva - Los bloques de flujo de control se integran con [Signals](/technologies/angular/interview-questions/angular-signals) para reactividad de grano fino: solo los bloques afectados se re-renderizan cuando los signals se actualizan - Ejecuta `ng generate @angular/core:control-flow` para migrar automáticamente los templates existentes --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/angular/angular-control-flow-syntax-if-for-switch-guide