Fonctions pures dans les templates Angular 21.2 : Capacités et limites
Depuis la version 21.2, Angular permet d'utiliser des fonctions JavaScript pures directement dans les templates HTML sans déclarer de méthodes dans la classe du composant. Cela simplifie la manipulation des données comme le filtrage de tableaux ou les calculs basés sur les signaux, mais nécessite de respecter strictement les règles des fonctions pures.
Prenons un composant avec une liste de héros, où les données sont stockées dans un signal heroes de type 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[]>([
// données des héros
]);
selectedHeroId: WritableSignal<number> = signal<number>(1);
}
Affichage basique de la liste via @for :
<ul>
@for (hero of heroes(); track $index) {
<li>{{ `${hero.name} ${hero.lastName} - ${hero.email}` }}</li>
}
</ul>
Naviguer dans la liste sans méthodes de composant
La mise à jour du signal selectedHeroId se fait désormais en ligne dans les gestionnaires de clic. Au lieu des méthodes prevHero() et nextHero(), on utilise update() avec une fonction lambda :
<div class="hero-navigation">
<button (click)="selectedHeroId.update(count => count === 1 ? heroes().length : count - 1)">Précédent</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)">Suivant</button>
</div>
<div class="hero-info">
{{ heroes()[selectedHeroId() - 1] | json }}
</div>
Cela réduit le code boilerplate, mais la logique de navigation cyclique (retour au premier élément) reste dans le template.
Filtrer et rechercher dans les templates
Filtrage des héros en faisant correspondre les premières lettres du prénom et du nom :
<div>
{{ heroes().filter(hero => hero.name.charAt(0).toLowerCase() === hero.lastName.charAt(0).toLowerCase())
.map(hero => `${hero.name} ${hero.lastName}`).join(', ') }}
</div>
Résultat : Peter Parker, Stephen Strange, Bruce Banner.
Recherche par domaine d'email :
<div>
{{ heroes().find(hero => hero.email.endsWith('stark.com'))?.name }}
</div>
Sortie : Tony.
Passer des fonctions en entrée aux composants enfants
Le composant enfant HeroesList accepte une fonction de tri via 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()));
}
Utilisation dans le template parent :
<app-heroes-list [heroes]="heroes()" [sortFn]="(a, b) => a.name.localeCompare(b.name)"></app-heroes-list>
Tri par nom : Bruce Banner, Natasha Romanoff, Peter Parker, Stephen Strange, Tony Stark.
Objets et littéraux dans les fonctions
Pour retourner des objets, la fonction est encapsulée dans des parenthèses pour la distinguer du corps de la fonction :
<ul>
@for (hero of heroes().map(item => ({ name: 'Super ' + item.nickname, email: item.email })); track $index) {
<li>{{ `${hero.name} - ${hero.email}` }}</li>
}
</ul>
Résultat : Super Spider man - [email protected], etc.
Limites des fonctions pures
- Fonctions multilignes non prises en charge :
```html
{{ heroes().map(item => { return { name: 'Super ' + item.nickname, email: item.email } }) }}
```
Le compilateur génère une erreur.
- Fonctions retournant des fonctions compilées comme texte :
```html
{{ () => heroes().find(hero => hero.email.endsWith('stark.com'))?.name }}
```
- Pipes à l'intérieur des fonctions interdits, car ce n'est pas du JS pur :
```html
{{ heroes().find(hero => hero.email.endsWith('StaRk.com' | lowercase))?.name }}
```
Appliquer un pipe au résultat est autorisé :
```html
{{ heroes().find(hero => hero.email.endsWith('stark.com'))?.name | uppercase }}
```
Résultat : TONY.
Points clés à retenir
- Les fonctions pures dans les templates Angular 21.2 conviennent pour des opérations simples avec des signaux : filtrage, recherche, mise à jour de valeurs.
- Utilisation obligatoire de parenthèses pour les littéraux d'objets et interdiction des expressions multilignes.
- Le passage de fonctions via input-signal maintient la réactivité dans les composants enfants.
- Évitez les pipes à l'intérieur des fonctions ; appliquez-les uniquement aux résultats.
- Performance : les fonctions pures sont recalculées à chaque détection de changement ; préférez
computedou des pipes pour une logique complexe.
— Editorial Team
Aucun commentaire pour le moment.