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.

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.
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,@fory@switchestá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 ang-templatepara 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:
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:
<!-- 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:
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 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:
@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 (===):
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:
@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:
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:
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:
// 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:
<!-- 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:
# Migrar proyecto completo
ng generate @angular/core:control-flow
# Migrar directorio específico
ng generate @angular/core:control-flow --path=src/app/featuresEl 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.
¡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
tracken@fores obligatoria: usa identificadores únicos comoitem.idpara rendimiento óptimo, evitatrack itemque causa re-renderizados basados en referencias @ifsoporta ramas nativas@else ify@elsesinng-template, y la palabra claveascrea alias para valores verdaderos para reutilización@switchusa igualdad estricta y no tiene fallthrough: las instrucciones@caseconsecutivas 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-flowpara migrar automáticamente los templates existentes
¿Sabrías detectar el bug en Angular?
Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Escrito por
Anthony Fillion-MailletFundador 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
Compartir
Artículos relacionados

Angular Signals y Computed en 2026: Reactividad Fina y Preguntas de Entrevista
Dominar Signals de Angular, computed, effect y linkedSignal en Angular 20+. Patrones de reactividad granular y preparación para entrevistas técnicas.

Preguntas de entrevista Angular 19: Signals, SSR y conceptos imprescindibles
Las preguntas de entrevista Angular 19 más frecuentes: Signals, hidratación incremental, detección de cambios sin Zone.js y nuevas APIs reactivas con ejemplos de código y respuestas esperadas.

RxJS en Angular 2026: operators, Subjects e interop con Signals
RxJS en Angular 2026: domina los operators, los Subjects y los patrones de interop con Signals que se usan en producción, además de las preguntas de entrevista más frecuentes.