skills/dev-skills/angular-developer/references/pipes.md
Pipes transform data declaratively inside Angular templates using the | operator.
Import the pipe class and add it to the component's imports array.
import {Component} from '@angular/core';
import {DatePipe, CurrencyPipe} from '@angular/common';
@Component({
selector: 'app-invoice',
imports: [DatePipe, CurrencyPipe],
template: `
<p>Date: {{ issuedOn | date: 'mediumDate' }}</p>
<p>Total: {{ amount | currency }}</p>
`,
})
export class Invoice {
issuedOn = new Date();
amount = 49.99;
}
Do NOT inject pipe classes into services or other classes. Pipes are template operators, not injectable services. Injecting them causes DI errors in standalone contexts and creates unnecessary coupling.
Extract the logic into a plain function. The pipe delegates to it; services import the function directly.
// kebab-case.ts
export function toKebabCase(value: string): string {
return value.toLowerCase().replace(/ /g, '-');
}
// 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);
}
}
// formatter.service.ts — import the function, NOT the pipe
import {Injectable} from '@angular/core';
import {toKebabCase} from './kebab-case';
@Injectable({providedIn: 'root'})
export class FormatterService {
toSlug(title: string): string {
return toKebabCase(title);
}
}
@angular/common exports a standalone function for each locale-aware built-in pipe:
| Pipe | Standalone function |
|---|---|
DatePipe | formatDate |
CurrencyPipe | formatCurrency |
DecimalPipe | formatNumber |
PercentPipe | formatPercent |
Inject LOCALE_ID to get the current locale and pass it to the function.
// CORRECT — use formatNumber instead of injecting DecimalPipe
import {Injectable, LOCALE_ID, inject} from '@angular/core';
import {formatNumber} from '@angular/common';
@Injectable({providedIn: 'root'})
export class PriceService {
private locale = inject(LOCALE_ID);
formatQuantity(value: number): string {
return formatNumber(value, this.locale, '1.0-0');
}
}
// WRONG — do not inject pipe classes
import {Injectable} from '@angular/core';
import {DecimalPipe} from '@angular/common';
@Injectable({providedIn: 'root'})
export class PriceService {
// ❌ DecimalPipe is not designed to be injected
private pipe = inject(DecimalPipe);
}
Use the Angular CLI to generate a pipe:
ng generate pipe path/to/my-pipe
A pipe needs a @Pipe decorator with a name and a transform method implementing PipeTransform.
import {Pipe, PipeTransform} from '@angular/core';
@Pipe({name: 'truncate'})
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 50): string {
return value.length > limit ? value.slice(0, limit) + '…' : value;
}
}
name: camelCase. Do not use hyphens.name with Pipe appended (e.g., TruncatePipe).Mark a pipe pure: false only when you need to detect mutations inside arrays or objects. Impure pipes run on every change-detection cycle and can hurt performance.
@Pipe({name: 'filterItems', pure: false})
export class FilterItemsPipe implements PipeTransform {
transform(items: string[], query: string): string[] {
return items.filter((i) => i.includes(query));
}
}
IMPORTANT: Avoid impure pipes unless absolutely necessary.