Une pipe transforme une valeur pour l’affichage, directement dans le template : {{ price | currency }}. C’est une fonction pure réutilisable que l’on importe comme n’importe quel composant. Par défaut, les pipes sont pures : Angular ne les réexécute que lorsque la valeur d’entrée ou les arguments changent.
Pipes intégrées et pipes personnalisées
<p>{{ order().createdAt | date: 'dd MMM yyyy, HH:mm' }}</p>
<p>{{ order().total | currency: 'RON' : 'symbol' : '1.2-2' }}</p>
<p>{{ ratio() | percent: '1.0-1' }}</p>
<p>{{ user().name | initials }}</p> <!-- pipe personnalisée -->
<p>{{ post().publishedAt | timeAgo }}</p>
@for (entry of config() | keyvalue; track entry.key) {
<dt>{{ entry.key }}</dt><dd>{{ entry.value }}</dd>
}
<!-- async : s’abonne et se désabonne tout seul (utile tant que vous avez encore des Observables) -->
@let user = user$ | async;date: 'fullDate' | joi, 22 octombrie 2026 |
date: 'HH:mm' | 09:11 |
currency: 'RON' | 1.234,50 RON |
currency: 'EUR' : 'symbol' : '1.0-0' | 247 EUR |
number: '1.2-2' | 411,50 |
percent | 18,8 % |
initials (custom) | AM |
timeAgo (custom) | il y a 5 h |
keyvalue | limba=ronotificari=truetema=dark |
Comment écrire une pipe
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'timeAgo' }) // standalone par défaut, pure par défaut
export class TimeAgoPipe implements PipeTransform {
transform(value: Date | string | number, now: number = Date.now()): string {
const seconds = Math.round((now - new Date(value).getTime()) / 1000);
if (seconds < 45) return 'à l’instant';
const minutes = Math.round(seconds / 60);
if (minutes < 60) return `il y a ${minutes} min`;
const hours = Math.round(minutes / 60);
if (hours < 24) return `il y a ${hours} h`;
const days = Math.round(hours / 24);
return days === 1 ? 'hier' : `il y a ${days} jours`;
}
}Formatage selon la locale (ro-RO)
// Formatage en roumain pour date, currency, number
import { registerLocaleData } from '@angular/common';
import localeRo from '@angular/common/locales/ro';
import { LOCALE_ID } from '@angular/core';
registerLocaleData(localeRo);
export const appConfig: ApplicationConfig = {
providers: [
{ provide: LOCALE_ID, useValue: 'ro' },
{ provide: DEFAULT_CURRENCY_CODE, useValue: 'RON' },
],
};Pipe, méthode ou computed()
Une méthode appelée depuis le template s’exécute à chaque Change Detection du composant. Une pipe pure et un computed() sont mémoïsés : ils ne se réexécutent pas si leurs entrées n’ont pas changé.
// Comment choisir :
//
// Transformation d’AFFICHAGE, réutilisée dans plusieurs composants → pipe (date, currency, initials, timeAgo)
// Calcul propre à un composant, sur ses Signals → computed()
// Logique métier / dérivation d’état → computed() / Store
// Méthode appelée depuis le template → seulement pour du trivial
//
// Pourquoi pas une méthode ? Elle est appelée à chaque Change Detection du composant.
// La pipe pure et le computed() ne sont recalculés que lorsque leurs entrées changent.Pipes impures
// Pipe impure : s’exécute à CHAQUE Change Detection, quelle que soit l’entrée
@Pipe({ name: 'filterBy', pure: false })
export class FilterByPipe implements PipeTransform {
transform<T>(items: T[], predicate: (item: T) => boolean): T[] {
return items.filter(predicate); // s’exécute souvent → à éviter sur de grandes listes
}
}
// Presque toujours, le bon choix est un computed() dans le composant :
readonly activeUsers = computed(() => this.users().filter((u) => u.active));Quand la valeur dépend de signals, un computed() est souvent plus simple qu’un pipe : voir signal() et computed(). Le formatage des dates et des nombres selon la locale est dans {i18n}.
Quand OUI, quand NON
- Un formatage d’affichage utilisé à plusieurs endroits (date relative, taille de fichier, initiales, masquage d’IBAN).
- Une transformation pure : même entrée → même sortie.
- Vous voulez l’utiliser dans des templates sans classe de logique propre (p. ex. des composants « bêtes »).
- Un calcul propre à un composant →
computed(). - Filtrer/trier des données métier →
computed()ou un Store. - Vous avez besoin de HTTP ou d’état → une pipe n’est pas le bon endroit (utilisez resource / un Service).
- Une pipe impure « pour que ça marche » → il vous faut presque certainement un Signal.
Pièges fréquents
- Vous mutez un tableau et vous vous demandez pourquoi la pipe pure ne se réexécute pas : la référence est la même. Créez un nouveau tableau.
- Vous oubliez l’import : « The pipe 'date' could not be found » → ajoutez
DatePipeàimports. - Locale non enregistrée :
LOCALE_ID = 'ro'sansregisterLocaleData(localeRo)lève une erreur. - De nouveaux arguments à chaque rendu (
| pipe: { a: 1 }) annulent la mémoïsation dans certains cas. Gardez des arguments stables.