Novedades del template en Angular 21: regex, spread, instanceof y más

Angular 21 trajo varias novedades al template que parecen pequeñas pero cambian el día a día. Regex literals directamente en el HTML, spread operator en arrays y objetos, instanceof en expresiones, exhaustive switch blocks. Menos lógica empujada al componente, más expresividad en el template.

Regex literals en el template (v21)

Antes, si quería filtrar con una expresión regular en el template, tenía que crear una propiedad en el componente. Ahora puedo usar el literal directamente.

// Antes: declarar el regex en el componente
// emailRegex = /@empresa\.com$/;

// Ahora: regex literal directo en el template
@if (userEmail() | match: /@empresa\.com$/) {
  <span class="badge">Email corporativo</span>
}

@if (/^\d{8}-\d$/.test(cuit())) {
  <span class="valid">CUIT válido</span>
}

Spread operator en templates (v21.1)

// Spread de arrays
@let todosLosItems = [...itemsActivos(), ...itemsArchivados()];

@for (item of todosLosItems; track item.id) {
  <li>{{ item.nombre }}</li>
}

// Spread de objetos en property binding
<mi-componente [config]="{ ...configBase(), ...configOverride() }" />

instanceof en expresiones del template (v21.2)

// Antes: casteo forzado en el componente
// get esError(): boolean { return this.resultado() instanceof Error; }

// Ahora: directo en el template
@if (resultado() instanceof Error) {
  <app-error-display [error]="resultado()" />
} @else if (resultado() instanceof Array) {
  <app-lista [items]="resultado()" />
} @else {
  <app-detalle [dato]="resultado()" />
}

Exhaustive switch blocks (v21.2)

type Estado = 'activo' | 'pausado' | 'eliminado';

@switch (usuario.estado()) {
  @case ('activo') {
    <span class="badge green">Activo</span>
  }
  @case ('pausado') {
    <span class="badge yellow">Pausado</span>
  }
  @case ('eliminado') {
    <span class="badge red">Eliminado</span>
  }
  // Sin @default: el compilador verifica que todos los casos estén cubiertos
}

Multiple switch case matching (v21.1)

// Múltiples valores en un @case
@switch (rol()) {
  @case ('admin', 'superadmin') {
    <app-panel-admin />
  }
  @case ('editor', 'revisor') {
    <app-panel-editor />
  }
  @default {
    <app-panel-usuario />
  }
}

@let: variables locales en el template

// Sin @let: el Signal se evalúa 3 veces
<p>Hola, {{ usuario().nombre }}</p>
<p>Email: {{ usuario().email }}</p>
<img [src]="usuario().avatar" />

// Con @let: evaluación única, más eficiente
@let u = usuario();
<p>Hola, {{ u.nombre }}</p>
<p>Email: {{ u.email }}</p>
<img [src]="u.avatar" />

ngTemplateOutlet con injector personalizado (v21.2)

<ng-container
  [ngTemplateOutlet]="miTemplate"
  [ngTemplateOutletContext]="{ $implicit: datos() }"
  [ngTemplateOutletInjector]="injectorPersonalizado">
</ng-container>

Artículo anterior: Signal Forms: cuando los formularios reactivos finalmente tienen sentido | Serie Angular 20 → 21.2 | Próximo: Vitest reemplaza a Karma y Angular habla con tu IA: el nuevo ecosistema de herramientas →

0 comentarios

Dejar un comentario

¿Quieres unirte a la conversación?
Siéntete libre de contribuir!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.