Un pipe transformă o valoare pentru afișare, direct în template: {{ price | currency }}. E o funcție pură, refolosibilă, pe care o importi ca pe orice componentă. Implicit pipe-urile sunt pure: Angular le re-execută doar când se schimbă valoarea de intrare sau argumentele.
Pipe-uri incluse și pipe-uri custom
<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 custom -->
<p>{{ post().publishedAt | timeAgo }}</p>
@for (entry of config() | keyvalue; track entry.key) {
<dt>{{ entry.key }}</dt><dd>{{ entry.value }}</dd>
}
<!-- async: se abonează și se dezabonează singur (util când încă ai Observable-uri) -->
@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) | acum 5 h |
keyvalue | limba=ronotificari=truetema=dark |
Cum scrii un pipe
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'timeAgo' }) // standalone implicit, pur implicit
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 'chiar acum';
const minutes = Math.round(seconds / 60);
if (minutes < 60) return `acum ${minutes} min`;
const hours = Math.round(minutes / 60);
if (hours < 24) return `acum ${hours} h`;
const days = Math.round(hours / 24);
return days === 1 ? 'ieri' : `acum ${days} zile`;
}
}Formatare locală (ro-RO)
// Formatare în română pentru 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 vs metodă vs computed()
O metodă apelată din template rulează la fiecare change detection a componentei. Un pipe pur și un computed() sunt memoizate: nu rulează din nou dacă intrările nu s-au schimbat.
// Cum alegi:
//
// Transformare de AFIȘARE, refolosită în multe componente → pipe (date, monede, inițiale, timeAgo)
// Calcul specific unei componente, pe semnalele ei → computed()
// Logică de business / derivare de stare → computed() / store
// Metodă apelată din template → doar pentru lucruri triviale
//
// De ce nu metodă? Se apelează la fiecare change detection a componentei.
// Pipe-ul pur și computed()-ul se recalculează doar când li se schimbă intrările.Pipe-uri impure
// Pipe impur: rulează la FIECARE change detection, indiferent de input
@Pipe({ name: 'filterBy', pure: false })
export class FilterByPipe implements PipeTransform {
transform<T>(items: T[], predicate: (item: T) => boolean): T[] {
return items.filter(predicate); // rulează des → evită pe liste mari
}
}
// Aproape mereu, varianta corectă e un computed() în componentă:
readonly activeUsers = computed(() => this.users().filter((u) => u.active));Când valoarea depinde de semnale, un computed() e de obicei mai simplu decât un pipe: vezi signal() și computed(). Formatarea datelor și a numerelor după limbă e la {i18n}.
Când DA, când NU
- Formatare de afișare folosită în mai multe locuri (dată relativă, mărime fișier, inițiale, mascare IBAN).
- Transformare pură: aceeași intrare → aceeași ieșire.
- Vrei să o folosești și în template-uri care nu au clasă proprie de logică (ex. componente dumb).
- Calcul specific unei componente →
computed(). - Filtrare/sortare pe date de business →
computed()sau store. - Ai nevoie de HTTP sau de stare → pipe-ul nu e locul (folosește resource / serviciu).
- Pipe impur „ca să meargă” → aproape sigur îți trebuie un semnal.
Capcane frecvente
- Mutezi un array și te miri că pipe-ul pur nu se re-execută: referința e aceeași. Creează un array nou.
- Uiți importul: „The pipe 'date' could not be found” → adaugă
DatePipeînimports. - Locale neînregistrat:
LOCALE_ID = 'ro'fărăregisterLocaleData(localeRo)aruncă eroare. - Argumente noi la fiecare randare (
| pipe: { a: 1 }) anulează memoizarea în unele cazuri. Ține argumentele stabile.