Guides approfondis
Templates

Pipes

Vue d'ensemble

Les pipes sont des opérateurs spéciaux des expressions de template Angular qui vous permettent de transformer des données de manière déclarative dans votre template. Les pipes vous permettent de déclarer une fonction de transformation une seule fois, puis d'utiliser cette transformation dans plusieurs templates. Les pipes Angular utilisent le caractère barre verticale (|), inspiré du pipe Unix.

Note : La syntaxe des pipes d'Angular s'écarte du JavaScript standard, qui utilise le caractère barre verticale pour l'opérateur OU bit à bit. Les expressions de template Angular ne prennent pas en charge les opérateurs bit à bit.

Voici un exemple utilisant quelques pipes intégrés fournis par Angular :

import {Component} from '@angular/core';
import {CurrencyPipe, DatePipe, TitleCasePipe} from '@angular/common';

@Component({
  selector: 'app-root',
  imports: [CurrencyPipe, DatePipe, TitleCasePipe],
  template: `
    <main>
      <!-- Transform the company name to title-case and
       transform the purchasedOn date to a locale-formatted string -->
      <h1>Purchases from {{ company | titlecase }} on {{ purchasedOn | date }}</h1>

      <!-- Transform the amount to a currency-formatted string -->
      <p>Total: {{ amount | currency }}</p>
    </main>
  `,
})
export class ShoppingCart {
  amount = 123.45;
  company = 'acme corporation';
  purchasedOn = '2024-07-08';
}

Quand Angular rend le composant, il s'assure que le format de date et la devise appropriés dépendent de la locale de l'utilisateur. Si l'utilisateur se trouve aux États-Unis, le rendu serait :

<main>
  <h1>Purchases from Acme Corporation on Jul 8, 2024</h1>
  <p>Total: $123.45</p>
</main>

Consultez le guide approfondi sur l'i18n pour en savoir plus sur la façon dont Angular localise les valeurs.

Pipes intégrés

Angular inclut un ensemble de pipes intégrés dans le package @angular/common :

Nom Description
AsyncPipe Lit la valeur d'une Promise ou d'un Observable RxJS.
CurrencyPipe Transforme un nombre en chaîne monétaire, formatée selon les règles de la locale.
DatePipe Formate une valeur Date selon les règles de la locale.
DecimalPipe Transforme un nombre en chaîne avec un séparateur décimal, formatée selon les règles de la locale.
I18nPluralPipe Associe une valeur à une chaîne qui met la valeur au pluriel selon les règles de la locale.
I18nSelectPipe Associe une clé à un sélecteur personnalisé qui renvoie la valeur souhaitée.
JsonPipe Transforme un objet en représentation sous forme de chaîne via JSON.stringify, destinée au débogage.
KeyValuePipe Transforme un Object ou une Map en tableau de paires clé-valeur.
LowerCasePipe Transforme le texte entièrement en minuscules.
PercentPipe Transforme un nombre en chaîne de pourcentage, formatée selon les règles de la locale.
SlicePipe Crée un nouvel Array ou une nouvelle String contenant un sous-ensemble (une tranche) des éléments.
TitleCasePipe Transforme le texte en casse de titre.
UpperCasePipe Transforme le texte entièrement en majuscules.

Utiliser les pipes

L'opérateur pipe d'Angular utilise le caractère barre verticale (|) dans une expression de template. L'opérateur pipe est un opérateur binaire : l'opérande de gauche est la valeur passée à la fonction de transformation, et l'opérande de droite est le nom du pipe suivi d'éventuels arguments supplémentaires (décrits ci-dessous).

<p>Total: {{ amount | currency }}</p>

Dans cet exemple, la valeur de amount est passée au CurrencyPipe, dont le nom de pipe est currency. Il affiche ensuite la devise par défaut correspondant à la locale de l'utilisateur.

Combiner plusieurs pipes dans la même expression

Vous pouvez appliquer plusieurs transformations à une valeur en utilisant plusieurs opérateurs pipe. Angular exécute les pipes de gauche à droite.

L'exemple suivant montre une combinaison de pipes pour afficher une date localisée entièrement en majuscules :

<p>The event will occur on {{ scheduledOn | date | uppercase }}.</p>

Passer des paramètres aux pipes

Certains pipes acceptent des paramètres pour configurer la transformation. Pour spécifier un paramètre, ajoutez après le nom du pipe un deux-points (:) suivi de la valeur du paramètre.

Par exemple, le DatePipe peut prendre des paramètres pour formater la date d'une manière spécifique.

<p>The event will occur at {{ scheduledOn | date: 'hh:mm' }}.</p>

Certains pipes peuvent accepter plusieurs paramètres. Vous pouvez spécifier des valeurs de paramètres supplémentaires séparées par le caractère deux-points (:).

Par exemple, nous pouvons aussi passer un second paramètre optionnel pour contrôler le fuseau horaire.

<p>The event will occur at {{ scheduledOn | date: 'hh:mm' : 'UTC' }}.</p>

Comment fonctionnent les pipes

Conceptuellement, les pipes sont des fonctions qui acceptent une valeur d'entrée et renvoient une valeur transformée.

import {Component} from '@angular/core';
import {CurrencyPipe} from '@angular/common';

@Component({
  selector: 'app-root',
  imports: [CurrencyPipe],
  template: `
    <main>
      <p>Total: {{ amount | currency }}</p>
    </main>
  `,
})
export class AppComponent {
  amount = 123.45;
}

Dans cet exemple :

  1. CurrencyPipe est importé depuis @angular/common
  2. CurrencyPipe est ajouté au tableau imports
  3. La donnée amount est passée au pipe currency

Priorité de l'opérateur pipe

L'opérateur pipe a une priorité plus faible que les autres opérateurs binaires, dont +, -, *, /, %, &&, || et ??.

<!-- firstName and lastName are concatenated before the result is passed to the uppercase pipe -->
{{ firstName + lastName | uppercase }}

L'opérateur pipe a une priorité plus élevée que l'opérateur conditionnel (ternaire).

{{ (isAdmin ? 'Access granted' : 'Access denied') | uppercase }}

Si la même expression était écrite sans parenthèses :

{{ isAdmin ? 'Access granted' : 'Access denied' | uppercase }}

Elle serait plutôt analysée comme :

{{ isAdmin ? 'Access granted' : ('Access denied' | uppercase) }}

Utilisez toujours des parenthèses dans vos expressions quand la priorité des opérateurs peut être ambiguë.

Détection de changements avec les pipes

Par défaut, tous les pipes sont considérés comme pure, ce qui signifie qu'ils ne s'exécutent que lorsqu'une valeur d'entrée primitive (comme un String, Number, Boolean ou Symbol) ou une référence d'objet (comme un Array, Object, Function ou Date) change. Les pipes purs offrent un avantage en matière de performances, car Angular peut éviter d'appeler la fonction de transformation si la valeur passée n'a pas changé.

Par conséquent, les mutations des propriétés d'un objet ou des éléments d'un tableau ne sont pas détectées, à moins que la référence de l'objet ou du tableau entier ne soit remplacée par une autre instance. Si vous voulez ce niveau de détection de changements, consultez détecter les changements dans les tableaux ou les objets.

Créer des pipes personnalisés

Vous pouvez définir un pipe personnalisé en implémentant une classe TypeScript avec le décorateur @Pipe. Un pipe doit avoir deux choses :

  • Un nom, spécifié dans le décorateur du pipe
  • Une méthode nommée transform qui effectue la transformation de la valeur.

La classe TypeScript doit en plus implémenter l'interface PipeTransform pour garantir qu'elle respecte la signature de type d'un pipe.

Voici un exemple de pipe personnalisé qui transforme des chaînes en kebab case :

kebab-case.pipe.ts

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'kebabCase',
})
export class KebabCasePipe implements PipeTransform {
  transform(value: string): string {
    return value.toLowerCase().replace(/ /g, '-');
  }
}

Utiliser le décorateur @Pipe

Quand vous créez un pipe personnalisé, importez Pipe depuis le package @angular/core et utilisez-le comme décorateur de la classe TypeScript.

import {Pipe} from '@angular/core';

@Pipe({
  name: 'myCustomTransformation',
})
export class MyCustomTransformationPipe {}

Le décorateur @Pipe requiert un name qui détermine comment le pipe est utilisé dans un template.

Convention de nommage des pipes personnalisés

La convention de nommage des pipes personnalisés se compose de deux conventions :

  • name - le camelCase est recommandé. N'utilisez pas de tirets.
  • class name - version PascalCase du name, suivie du suffixe Pipe

Implémenter l'interface PipeTransform

En plus du décorateur @Pipe, les pipes personnalisés doivent toujours implémenter l'interface PipeTransform de @angular/core.

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'myCustomTransformation',
})
export class MyCustomTransformationPipe implements PipeTransform {}

Implémenter cette interface garantit que votre classe de pipe a la bonne structure.

Transformer la valeur d'un pipe

Chaque transformation est invoquée par la méthode transform, dont le premier paramètre est la valeur passée et dont la valeur de retour est la valeur transformée.

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'myCustomTransformation',
})
export class MyCustomTransformationPipe implements PipeTransform {
  transform(value: string): string {
    return `My custom transformation of ${value}.`;
  }
}

Ajouter des paramètres à un pipe personnalisé

Vous pouvez ajouter des paramètres à votre transformation en ajoutant des paramètres supplémentaires à la méthode transform :

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'myCustomTransformation',
})
export class MyCustomTransformationPipe implements PipeTransform {
  transform(value: string, format: string): string {
    let msg = `My custom transformation of ${value}.`;

    if (format === 'uppercase') {
      return msg.toUpperCase();
    } else {
      return msg;
    }
  }
}

Détecter les changements dans les tableaux ou les objets

Quand vous voulez qu'un pipe détecte les changements dans des tableaux ou des objets, il doit être marqué comme fonction impure en passant l'option pure avec la valeur false.

Important : Évitez de créer des pipes impurs sauf en cas d'absolue nécessité, car ils peuvent entraîner une pénalité de performances importante s'ils sont utilisés sans précaution.

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'joinNamesImpure',
  pure: false,
})
export class JoinNamesImpurePipe implements PipeTransform {
  transform(names: string[]): string {
    return names.join();
  }
}

Les développeurs Angular adoptent souvent la convention d'inclure Impure dans le name du pipe et dans le nom de la classe, pour signaler aux autres développeurs le risque potentiel en matière de performances.

Utiliser la logique d'un pipe en dehors des templates

Les pipes sont des opérateurs de template conçus pour transformer des données de manière déclarative dans les templates. Quand vous avez besoin de la même logique de transformation dans un service, une classe utilitaire ou tout autre contexte hors template, évitez d'injecter la classe du pipe. Les pipes ne sont pas conçus comme des services injectables.

Extraire la logique des pipes personnalisés

Quand vous créez un pipe personnalisé, extrayez la transformation dans une fonction standalone. Faites déléguer la méthode transform() du pipe à cette fonction, et importez directement la fonction partout où vous en avez besoin par ailleurs.

kebab-case.ts

export function toKebabCase(value: string): string {
  return value.toLowerCase().replace(/ /g, '-');
}
À privilégier

kebab-case.pipe.ts

import {Pipe, PipeTransform} from '@angular/core';
import {toKebabCase} from './kebab-case';

@Pipe({name: 'kebabCase'})
export class KebabCasePipe implements PipeTransform {
  transform(value: string): string {
    return toKebabCase(value);
  }
}
À privilégier

formatter.service.ts

import {Service} from '@angular/core';
import {toKebabCase} from './kebab-case';

@Service()
export class FormatterService {
  formatSlug(title: string): string {
    return toKebabCase(title);
  }
}
À éviter

formatter.service.ts

import {inject, Service} from '@angular/core';
import {KebabCasePipe} from './kebab-case.pipe';

@Service()
export class FormatterService {
  // Avoid injecting the pipe class into services or other classes.
  private kebabCasePipe = inject(KebabCasePipe);

  formatSlug(title: string): string {
    return this.kebabCasePipe.transform(title);
  }
}

Utiliser les fonctions de formatage pour les pipes intégrés

Les pipes intégrés d'Angular qui tiennent compte de la locale ont chacun une fonction de formatage standalone correspondante, exportée depuis @angular/common. Utilisez ces fonctions plutôt que d'injecter la classe du pipe.

À privilégier
import {Service, LOCALE_ID, inject} from '@angular/core';
import {formatNumber} from '@angular/common';

@Service()
export class PriceService {
  private locale = inject(LOCALE_ID);

  format(value: number) {
    return formatNumber(value, this.locale, '1.2-2');
  }
}
À éviter
import {inject, Service} from '@angular/core';
import {DecimalPipe} from '@angular/common';

@Service()
export class PriceService {
  private decimalPipe = inject(DecimalPipe);

  format(value: number) {
    return this.decimalPipe.transform(value, '1.2-2');
  }
}