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:
<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:
<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.
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
Aún no hay comentarios.