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.

Sintaxis de flujo de control Angular con @if, @for y @switch

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 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:

user-status.component.tstypescript
import { Component, input } from '@angular/core';

@Component({
  selector: 'app-user-status',
  template: `
    @if (user().role === 'admin') {
      <app-admin-panel />
    } @else if (user().role === 'editor') {
      <app-editor-panel />
    } @else {
      <app-viewer-panel />
    }
  `
})
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
<!-- Evita accesos repetidos a propiedades -->
@if (user().profile?.settings?.theme; as theme) {
  <p>Tema actual: {{ theme }}</p>
}

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:

product-list.component.tstypescript
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) {
      <app-product-card [product]="product" />
    } @empty {
      <p>No hay productos disponibles</p>
    }
  `
})
export class ProductListComponent {
  products = input.required<Product[]>();
}

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 TrackCaso de usoImpacto en rendimiento
track item.idElementos con identificadores únicosÓptimo: actualizaciones DOM mínimas
track $indexListas estáticas que nunca se reordenanAceptable: re-renderizado completo al reordenar
track itemTracking por referenciaMalo: 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) {
  <li class="item" [class.last]="isLast">
    {{ i + 1 }}. {{ item.name }}
  </li>
}

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

¿Listo para aprobar tus entrevistas de Angular?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

@switch: Ramificación condicional type-safe

El bloque @switch proporciona pattern matching exhaustivo con comparación de igualdad estricta (===):

status-badge.component.tstypescript
import { Component, input } from '@angular/core';

type OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered';

@Component({
  selector: 'app-status-badge',
  template: `
    @switch (status()) {
      @case ('pending') {
        <span class="badge badge-gray">Pendiente</span>
      }
      @case ('processing') {
        <span class="badge badge-blue">Procesando</span>
      }
      @case ('shipped') {
        <span class="badge badge-yellow">Enviado</span>
      }
      @case ('delivered') {
        <span class="badge badge-green">Entregado</span>
      }
    }
  `
})
export class StatusBadgeComponent {
  status = input.required<OrderStatus>();
}

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') {
    <app-admin-dashboard />
  }
  @case ('editor')
  @case ('reviewer') {
    <app-editor-dashboard />
  }
  @default {
    <app-viewer-dashboard />
  }
}

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, permitiendo reactividad de grano fino:

dashboard.component.tstypescript
import { Component, computed, signal } from '@angular/core';

@Component({
  selector: 'app-dashboard',
  template: `
    @if (isLoading()) {
      <app-skeleton />
    } @else if (hasError()) {
      <app-error [message]="errorMessage()" />
    } @else {
      @for (item of filteredItems(); track item.id) {
        <app-item-card [item]="item" />
      } @empty {
        <p>Ningún elemento coincide con tus filtros</p>
      }
    }
  `
})
export class DashboardComponent {
  items = signal<Item[]>([]);
  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, 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)
<div *ngIf="user$ | async as user">{{ user.name }}</div>

// Con @if y signals (moderno)
@if (user(); as user) {
  <div>{{ user.name }}</div>
}

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
<!-- Usar @switch para una sola expresión, múltiples valores -->
@switch (status()) {
  @case ('active') { ... }
  @case ('inactive') { ... }
}

<!-- Usar @if para múltiples expresiones -->
@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 estructuralEquivalente 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.

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

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 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
Reto diario

¿Sabrías detectar el bug en Angular?

Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador de SharpSkill

Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.

Actualizado el 3 de septiembre de 2026

Etiquetas

#angular
#control-flow
#templates
#signals

Compartir

Artículos relacionados