Volver al blog
Angular Templates Performance Developer Experience
Imagen de portada del artículo: Dominando el nuevo Control Flow de Angular: Adiós *ngIf y *ngFor

Dominando el nuevo Control Flow de Angular: Adiós *ngIf y *ngFor

10 min
German

Angular ha evolucionado significativamente su motor de plantillas. Las directivas estructurales clásicas como *ngIf y *ngFor han servido bien durante años, pero dependían de librerías externas (CommonModule) y tenían limitaciones de rendimiento y tipado.

El nuevo Control Flow está integrado directamente en el compilador.

Bloque @if: Condicionales más limpios

La nueva sintaxis es mucho más intuitiva y se asemeja a JavaScript estándar. Ya no necesitamos contenedores ng-container solo para la lógica.

html
@if (isLoggedIn) {
  <user-profile [user]="user" />
} @else if (isLoading) {
  <loading-spinner />
} @else {
  <login-button />
}

Ventajas sobre *ngIf

  1. Sintaxis más limpia: No más importaciones de CommonModule.
  2. Mejor inferencia de tipos: TypeScript entiende mejor el contexto dentro de los bloques.
  3. Sin sobrecarga: No crea elementos DOM intermedios innecesarios.

Ejemplo real: Formulario con validación

Veamos un caso de uso típico donde gestionamos distintos estados de un formulario:

html
@if (form.controls.email; as emailCtrl) {
  <input [formControl]="emailCtrl" type="email" />
  
  @if (emailCtrl.hasError('required') && emailCtrl.touched) {
    <span class="error">El email es obligatorio</span>
  } @else if (emailCtrl.hasError('email')) {
    <span class="error">El formato del email no es válido</span>
  }
}

Fíjate cómo la variable local emailCtrl (declarada con as) permite acceder al control de forma tipada dentro del bloque.

Bloque @for: Iteraciones optimizadas

El cambio más drástico de rendimiento viene con el bucle @for. Ahora es obligatorio el uso de track para garantizar la eficiencia en el renderizado.

html
<ul>
  @for (item of items; track item.id; let i = $index, e = $even) {
    <li [class.gray]="e">
      {{ i }} - {{ item.name }}
    </li>
  } @empty {
    <li>No hay elementos en la lista</li>
  }
</ul>

Características Clave

Ejemplo: Tabla de datos con estados

html
<table>
  <thead>
    <tr><th>Nombre</th><th>Rol</th><th>Acciones</th></tr>
  </thead>
  <tbody>
    @for (user of users(); track user.id; let isFirst = $first, isLast = $last) {
      <tr [class.border-t-2]="isFirst" [class.border-b-2]="isLast">
        <td>{{ user.name }}</td>
        <td>{{ user.role }}</td>
        <td>
          <button (click)="editUser(user)">Editar</button>
        </td>
      </tr>
    } @empty {
      <tr>
        <td colspan="3" class="text-center py-8">
          No se encontraron usuarios
        </td>
      </tr>
    }
  </tbody>
</table>

Bloque @switch

El ngSwitch siempre fue un poco verboso. La nueva sintaxis lo simplifica enormemente:

html
@switch (userRole) {
  @case ('admin') {
    <admin-dashboard />
  }
  @case ('editor') {
    <editor-tools />
  }
  @default {
    <user-view />
  }
}

Comparativa de rendimiento

El nuevo Control Flow ofrece mejoras medibles:

Migración

Angular ofrece una herramienta automática para migrar tu proyecto:

ng generate @angular/core:control-flow

Esto escaneará tus plantillas y convertirá las directivas antiguas a la nueva sintaxis automáticamente.