Volver al inicio

Funciones puras en plantillas de Angular 21.2

Desde la versión 21.2 Angular soporta funciones puras de JS en plantillas para filtrar, buscar y actualizar signals sin métodos de componente. Desglose de navegación, ordenación, paso de funciones vía ejemplos de input. Limitaciones: prohibición de expresiones multilínea y pipes dentro de funciones.

Funciones en plantillas de Angular: filtrado y ordenación sin clases
Advertisement 728x90

Funciones Puras en Plantillas de Angular 21.2: Capacidades y Limitaciones

A partir de la versión 21.2, Angular permite el uso de funciones puras de JavaScript directamente en plantillas HTML sin declarar métodos en la clase del componente. Esto simplifica el manejo de datos como el filtrado de arreglos o cálculos basados en señales, pero requiere un estricto cumplimiento de las reglas de las funciones puras.

Considera un componente con una lista de héroes, donde los datos se almacenan en una señal heroes de tipo WritableSignal<Hero[]>:

import { Component, signal, WritableSignal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HeroesList } from '../heroes-list/heroes-list';

export interface Hero {
  id: number;
  name: string;
  lastName: string;
  nickname: string;
  email: string;
}

@Component({
  selector: 'app-heroes',
  imports: [HeroesList, CommonModule],
  templateUrl: './heroes.component.html',
  styleUrl: './heroes.component.scss',
  standalone: true
})
export class HeroesComponent {
  heroes: WritableSignal<Hero[]> = signal<Hero[]>([
    // datos de héroes
  ]);

  selectedHeroId: WritableSignal<number> = signal<number>(1);
}

Salida básica de la lista mediante @for:

Google AdInline article slot
<ul>
  @for (hero of heroes(); track $index) {
    <li>{{ `${hero.name} ${hero.lastName} - ${hero.email}` }}</li>
  }
</ul>

Navegando por la Lista Sin Métodos del Componente

Actualizar la señal selectedHeroId ahora se hace en línea en los manejadores de clic. En lugar de métodos prevHero() y nextHero(), se usa update() con una función lambda:

<div class="hero-navigation">
  <button (click)="selectedHeroId.update(count => count === 1 ? heroes().length : count - 1)">Anterior</button>
  <div class="hero-name">
    {{ heroes()[selectedHeroId() - 1].name + ' ' + heroes()[selectedHeroId() - 1].lastName }}
  </div>
  <button (click)="selectedHeroId.update(count => count === heroes().length ? 1 : count + 1)">Siguiente</button>
</div>
<div class="hero-info">
  {{ heroes()[selectedHeroId() - 1] | json }}
</div>

Esto reduce el código repetitivo, pero la lógica de navegación cíclica (volver al primer elemento) permanece en la plantilla.

Filtrado y Búsqueda en Plantillas

Filtrar héroes por coincidencia de las primeras letras del nombre y apellido:

Google AdInline article slot
<div>
  {{ heroes().filter(hero => hero.name.charAt(0).toLowerCase() === hero.lastName.charAt(0).toLowerCase())
    .map(hero => `${hero.name} ${hero.lastName}`).join(', ') }}
</div>

Resultado: Peter Parker, Stephen Strange, Bruce Banner.

Búsqueda por dominio de correo electrónico:

<div>
  {{ heroes().find(hero => hero.email.endsWith('stark.com'))?.name }}
</div>

Salida: Tony.

Google AdInline article slot

Pasando Funciones como Entrada a Componentes Hijos

El componente hijo HeroesList acepta una función de ordenamiento mediante input:

@Component({
  selector: 'app-heroes-list',
  template: `
    <ul>
      @for (hero of sortedHeroes(); track $index;) {
        <li>{{ hero.name + ' ' + hero.lastName }}</li>
      }
    </ul>
  `,
  standalone: true
})
export class HeroesList {
  heroes: InputSignal<Hero[]> = input.required<Hero[]>();
  sortFn: InputSignal<(a: Hero, b: Hero) => number> = input.required<(a: Hero, b: Hero) => number>();

  sortedHeroes: Signal<Hero[]> = computed(() => [...this.heroes()].sort(this.sortFn()));
}

Uso en la plantilla del componente padre:

<app-heroes-list [heroes]="heroes()" [sortFn]="(a, b) => a.name.localeCompare(b.name)"></app-heroes-list>

Ordenado por nombre: Bruce Banner, Natasha Romanoff, Peter Parker, Stephen Strange, Tony Stark.

Objetos y Literales en Funciones

Para devolver objetos, la función se envuelve en paréntesis para distinguirla del cuerpo de la función:

<ul>
  @for (hero of heroes().map(item => ({ name: 'Super ' + item.nickname, email: item.email })); track $index) {
    <li>{{ `${hero.name} - ${hero.email}` }}</li>
  }
</ul>

Resultado: Super Spider man - [email protected], etc.

Limitaciones de las Funciones Puras

  • Funciones multilínea no son compatibles:

```html

{{ heroes().map(item => { return { name: 'Super ' + item.nickname, email: item.email } }) }}

```

El compilador lanza un error.

  • Funciones que devuelven funciones se compilan como texto:

```html

{{ () => heroes().find(hero => hero.email.endsWith('stark.com'))?.name }}

```

  • Pipes dentro de funciones están prohibidos, ya que esto no es JS puro:

```html

{{ heroes().find(hero => hero.email.endsWith('StaRk.com' | lowercase))?.name }}

```

Aplicar un pipe al resultado está permitido:

```html

{{ heroes().find(hero => hero.email.endsWith('stark.com'))?.name | uppercase }}

```

Resultado: TONY.

Conclusiones Clave

  • Las funciones puras en plantillas de Angular 21.2 son adecuadas para operaciones simples con señales: filtrado, búsqueda, actualización de valores.
  • Uso obligatorio de paréntesis para literales de objetos y prohibición de expresiones multilínea.
  • Pasar funciones mediante input-signal mantiene la reactividad en componentes hijos.
  • Evitar pipes dentro de funciones; aplicarlos solo a los resultados.
  • Rendimiento: las funciones puras se recalculan en cada detección de cambios; prefiere computed o pipes para lógica compleja.

— Editorial Team

Advertisement 728x90

Leer después