9 oct 2023 · Actualizado 15 jul 2026

Control Flow en Angular: @if, @for y @switch (Adiós ngIf)

angularcontrol-flowfrontendtypescript

@if, @for y @switch reemplazan ngIf, ngFor y ngSwitch desde Angular 17. Aprende la nueva sintaxis de control flow y migra automáticamente con ng generate.

TL;DR: @if, @for y @switch reemplazan a *ngIf, *ngFor y *ngSwitch. No necesitan imports, @else funciona sin ng-template, y @for exige una expresión track. Migra un proyecto existente automáticamente con ng generate @angular/core:control-flow.

El control de flujo en las plantillas de Angular ahora es declarativo. Introducido en Angular 17, estable desde Angular 18 y por defecto en proyectos nuevos desde Angular 19, la sintaxis con bloques reemplaza a las directivas estructurales ngIf, ngFor y ngSwitch — hoy soft-deprecated.

Aquí la forma antigua frente a la nueva:

Directiva estructuralNuevo Control FlowDiferencia clave
*ngIf / *ngIf...else@if / @elseNo necesita ng-template ni variables de referencia
*ngFor@fortrack es obligatorio, ya no opcional
*ngSwitch / *ngSwitchCase@switch / @caseNo necesita el binding de directiva [ngSwitch]

@if: condiciones sin ng-template

La sintaxis clásica necesitaba una referencia de plantilla para el caso else:

<div *ngIf="usuarioLogueado; else noAutenticado"></div> <ng-template #noAutenticado> </ng-template>

Y si además querías manejar la rama del usuario logueado por separado:

<div *ngIf="usuarioLogueado; then autenticado else noAutenticado"></div> <ng-template #autenticado> </ng-template> <ng-template #noAutenticado> </ng-template>

La misma lógica con el nuevo Control Flow:

@if (usuarioLogueado) { <p>¡Bienvenido de nuevo!</p> } @else { <p>Por favor inicia sesión.</p> }

Sin referencias, sin ng-template, y las ramas se leen de arriba a abajo como código normal.

@for: bucles con track obligatorio

La sintaxis antigua:

<div *ngFor="let pais of paises"> <option value="{{ pais.id }}">{{ pais.nombre }}</option> </div>

La nueva sintaxis:

@for (pais of paises; track pais.id) { <option value="{{ pais.id }}">{{ pais.nombre }}</option> }

La expresión track es obligatoria — esa es la mayor diferencia de comportamiento. Con *ngFor, trackBy era opcional y la mayoría de los proyectos lo omitían, pagando en silencio el costo de reconciliación del DOM. Compara lo que antes hacía falta para el tracking:

<div *ngFor="let pais of paises; trackBy: trackByFn"> <option value="{{ pais.id }}">{{ pais.nombre }}</option> </div>
trackByFn(_: number, pais: Pais) { return pais.id; }

Todo un método del componente, reemplazado por dos palabras en la plantilla. @for también te da @empty gratis:

@for (pais of paises; track pais.id) { <option value="{{ pais.id }}">{{ pais.nombre }}</option> } @empty { <p>No se encontraron países.</p> }

@switch: casos sin directivas

@switch (lenguaje) { @case ('python') { <span>El lenguaje de programación seleccionado es: Python</span> } @case ('ruby') { <span>El lenguaje de programación seleccionado es: Ruby</span> } @case ('java') { <span>El lenguaje de programación seleccionado es: Java</span> } @default { <span>No hay ningún lenguaje de programación seleccionado</span> } }

Un cambio significativo comparado con:

<section [ngSwitch]="lenguaje"> <span *ngSwitchCase="'python'">El lenguaje de programación seleccionado es: Python</span> <span *ngSwitchCase="'ruby'">El lenguaje de programación seleccionado es: Ruby</span> <span *ngSwitchCase="'java'">El lenguaje de programación seleccionado es: Java</span> <span *ngSwitchDefault>No hay ningún lenguaje de programación seleccionado</span> </section>

La plantilla se reduce considerablemente y se vuelve más legible — y por lo tanto más mantenible.

Vistas Diferibles (Deferrable Views)

La misma sintaxis de bloques impulsa @defer, que carga contenido de la plantilla de forma diferida según triggers:

@for (libro of libros; track libro.id) { @defer (on viewport) { <book-card [libro]="libro" /> } }

@defer merece su propia guía a fondo — triggers, placeholders, precarga y el comportamiento de división del bundle están cubiertos en Angular @defer: Lazy Loading de Componentes Fácil.

¿De dónde salió esta sintaxis?

El diseño surgió de las RFC públicas para Control Flow y Deferrable Views. Las primeras propuestas usaban etiquetas al estilo HTML como {#if}, {:else} y {/if}, lo cual podía resultar confuso — tras una votación de la comunidad, el consenso terminó en la sintaxis con @ que tenemos hoy.

Cómo migrar un proyecto existente de *ngIf, *ngFor y *ngSwitch a Control Flow

Un solo comando:

ng generate @angular/core:control-flow

Convierte *ngIf, *ngFor y *ngSwitch en todas tus plantillas automáticamente. Ambas sintaxis coexisten, así que también puedes migrar de forma incremental — pero con las directivas antiguas soft-deprecated desde Angular 19, la dirección está clara.


Control Flow es la mitad de Angular declarativo — la otra mitad es el estado reactivo. Revisa Angular Signals: Guía Completa de Estado Reactivo.

Escrito por

Antonio Cárdenas

Google Developer Expert en Angular · Perfil GDE verificado · Perfil de Google Developers

Mi escritura técnica llega a más de 100,000 desarrolladores en inglés y español. He migrado aplicaciones Angular de producción desde la v8 hasta la v22, y mis habilidades agénticas de código abierto están destacadas en catálogos comunitarios como agentic-awesome-skills.

Preguntas frecuentes

Preguntas frecuentes

¿Qué es el nuevo Control Flow de Angular?

El nuevo Control Flow de Angular es una sintaxis declarativa introducida en Angular 17 que reemplaza las directivas estructurales ngIf, ngFor y ngSwitch. Usa bloques con @ como @if, @for y @switch directamente en la plantilla, sin necesidad de importar módulos adicionales. Es más legible, más eficiente y está integrado directamente en el compilador.

¿Cómo migrar de *ngIf al nuevo @if en Angular?

Reemplaza <div *ngIf='condicion'> por @if (condicion) { <div> }. Para el caso else, en lugar de ng-template con identificador, usa @else { } directamente. Angular 17+ incluye una migración automática: ng generate @angular/core:control-flow convierte automáticamente todas tus plantillas.

¿El nuevo @for requiere track obligatoriamente?

Sí. A diferencia de *ngFor donde trackBy era opcional, el nuevo @for requiere la expresión track. Esto mejora el rendimiento del reconciliado del DOM. Ejemplo: @for (item of items; track item.id) { }. Si no tienes un identificador único, puedes usar track $index aunque no es lo recomendado para listas dinámicas.

¿En qué versión de Angular se introdujo el nuevo Control Flow?

El nuevo Control Flow se introdujo como vista previa del desarrollador en Angular 17 (noviembre 2023) y se estabilizó como API pública en Angular 18. Desde Angular 19 es la sintaxis por defecto en los proyectos nuevos generados con ng new.

¿Puedo usar ngIf y el nuevo @if en el mismo proyecto Angular?

Sí. Ambas sintaxis coexisten durante la migración. Puedes migrar componente por componente. Sin embargo, el equipo de Angular recomienda migrar completamente al nuevo Control Flow, ya que ngIf, ngFor y ngSwitch están soft-deprecated desde Angular 19. Usa ng generate @angular/core:control-flow para la migración automática.

Sigue leyendo

¿Construyendo algo con Angular?

Escribo sobre arquitectura en Angular, Signals y el tooling a su alrededor — guías de migración, rutas reales de actualización y los detalles que la mayoría de artículos se saltan.

Ver todos los artículosAcerca de mí

Más del blog