Retour à l'accueil

Fonctions pures dans les templates Angular 21.2

Depuis la version 21.2, Angular prend en charge les fonctions pures JS dans les templates pour le filtrage, la recherche et la mise à jour des signaux sans méthodes de composant. Analyse de la navigation, du tri, du passage de fonctions via des exemples input. Limitations : interdiction des expressions multilignes et des pipes à l'intérieur des fonctions.

Fonctions dans les templates Angular : filtrage et tri sans classes
Advertisement 728x90

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 :

Google AdInline article slot
<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 :

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>

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.

Google AdInline article slot

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 computed ou des pipes pour une logique complexe.

— Editorial Team

Advertisement 728x90

Lire ensuite