Angular Pipes
posted in javascript on • by Wouter Van Schandevijl • • last updated onPipes: Chainable, declarative display-value transformations to use in your Html.
This post covers:
- All Builtin Angular pipes (json, async, string, array, i18n)
- How to install different locales (currency, decimal, date, percent)
- How to generate, implement and test your custom pipes
- Some examples of custom pipes (ngx-pipes and angular-pipes)
An example:
<!-- This does exactly what you'd think -->
{{ value | uppercase }}
Builtin Pipes
JsonPipe (Impure)
Quick object dump with the impure JsonPipe.
Impure: Angular executes an impure pipe during every component change detection cycle. An impure pipe is called often, as often as every keystroke or mouse-move.
Example usage:
<pre>{{ {number: 7} | json }}</pre>
<!-- Combine with the AsyncPipe to dump Observables -->
<code>{{ observable$ | async | json }}</code>
Inject in a Component:
import { CurrencyPipe, formatCurrency } from '@angular/common';
@Component({
providers: [CurrencyPipe]
})
export class AppComponent {
constructor(private cp: CurrencyPipe) {
this.cp.transform(450.657, 'EUR', 'symbol', '0.2-2', 'fr'); // 450,66 €
// Use the formatCurrency instead! (formatDate, formatNumber, ...)
formatCurrency(450.657, 'fr', 'EUR', 'symbol', '0.2-2');
}
}
String Casing Pipes
UpperCasePipe , LowerCasePipe and TitleCasePipe.
lowercase => {{ 'LOWERCASE' | lowercase }}
UPPERCASE => {{ 'uppercase' | uppercase }}
This Is Title Case => {{ 'tHIs is tiTLE CaSe' | titlecase }}
Locale Sensitive Pipes
Used by DatePipe, CurrencyPipe, DecimalPipe and PercentPipe.
Setup
By default, Angular comes with the en-US
locale only.
Import a different locale in your app.module.ts
:
import { registerLocaleData } from '@angular/common';
import { LOCALE_ID, NgModule } from '@angular/core';
import localeFr from '@angular/common/locales/fr';
registerLocaleData(localeFr, 'fr');
@NgModule({
providers: [
{provide: DEFAULT_CURRENCY_CODE, useValue: 'EUR'}, // default: USD
{provide: LOCALE_ID, useValue: 'fr'}, // default: en-US
// default dateFormat: mediumDate
// default timezone: user local system timezone
{provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {dateFormat: 'MM/dd/yy', timezone: '-1200'}},
],
})
export class AppModule { }
Or without module:
import { ApplicationConfig } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
{provide: LOCALE_ID, useValue: 'fr'},
{provide: DEFAULT_CURRENCY_CODE, useValue: 'EUR'},
{provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {dateFormat: 'MM/dd/yy', timezone: '-1200'}},
]
};
bootstrapApplication(AppComponent, appConfig);
CurrencyPipe
Outside html templates, use formatCurrency()
instead!
<!-- Signature -->
{{ value_expression | currency [ : currencyCode [ : display [ : digitsInfo [ : locale ] ] ] ] }}
<!-- Examples -->
€1,500.95 => {{ 1500.953 | currency:'EUR':'symbol' }}
003,96 EUR => {{ 3.955 | currency:'EUR':'code':'3.2-2':'fr' }}
Parameters:
- currencyCode: USD, EUR, GBP, … (full list)
- display: code=USD, symbol=$, a string
- digitsInfo: [minIntegerDigits=1].[minFractionDigits=2]-[maxFractionDigits=2] (ex: 3.2-2 -> 000.00)
- locale: en-US
Parameters digitsInfo
and locale
work exactly the same for DecimalPipe and PercentPipe.
DecimalPipe
Outside html templates, use formatNumber()
instead!
<!-- Signature -->
{{ value_expression | number [ : digitsInfo [ : locale ] ] }}
<!-- Example -->
025.1230 => {{ 25.123 | number:'3.4-4'}}
PercentPipe
Use formatPercent()
outside templates.
<!-- Signature -->
{{ value_expression | percent [ : digitsInfo [ : locale ] ] }}
<!-- Example -->
26% => {{ 0.259 | percent }}
DatePipe
Works on a Date
, a number (ms since UTC epoch), or an ISO string
Use formatDate()
outside templates instead!
All functions like getLocaleDayNames
etc have been deprecated in favor of using Intl
.
<!-- Signature -->
{{ value_expression | date [ : format [ : timezone [ : locale ] ] ] }}
<!-- Basic examples -->
May 1, 2019 => {{ Date.parse('2019-05-01') | date }}
2019-05-01 23:55:00 => {{ Date.parse('2019-05-01T23:55:00') | date:'yyyy-MM-dd HH:mm:ss' }}
<!-- Full blown example -->
1 mai 2019, 21h00 => {{ Date.parse('2019-05-01T23:55:00') | date:"d MMMM yyyy, HH'h'mm":'-0055':'fr' }}
More options for the format:
- Escape characters by placing them inside single quotes
- Week of year: w=7, ww=07
- Week day: E=Tue, EEEE=Tuesday, EEEEE=T, EEEEEE=Tu
- Month standalone: LLLL=September
- Period: a=am/pm
- Hour 1-12: h=1, hh=01
- Zone: z=GMT-8, Z=-0800
- Or named: short, medium, long, full; shortDate, mediumDate (default), longTime, …
Array Pipes
SlicePipe (Impure)
<!-- Signature -->
{{ value_expression | slice : start [ : end ] }}
<!-- Examples -->
[ 0, 1 ] => {{ [0, 1, 2, 3, 4] | slice:0:2 | json }}
[ 4 ] => {{ [0, 1, 2, 3, 4] | slice:-1 | json }}
[ 0, 1 ] => {{ [0, 1, 2, 3, 4] | slice:-5:2 | json }}
[ 0, 1, 2 ] => {{ [0, 1, 2, 3, 4] | slice:-5:-2 | json }}
KeyValuePipe (Impure)
Works on Objects and Maps.
<div *ngFor="let item of object | keyvalue:compareFn">
{{item.key}} => {{item.value}}
</div>
The optional compareFn
parameter signature defined in component.ts
:
export class AppComponent {
object = {key1: 1, key2: 2};
// revert sorting of this.object keys
// interface KeyValue<K, V>{key: K, Value: V}
compareFn(a: KeyValue<string, number>, b: KeyValue<string, number>) {
return b.key.localeCompare(a.key);
}
}
The output would be:
let item of object | keyvalue:compareFn
:key2 => 2
andkey1 => 1
let item of object | keyvalue
:key1 => 1
andkey2 => 2
AsyncPipe (Impure)
Returns the last emitted value. Unsubscribes automatically in ngOnDestroy
.
I typically always use the AsyncPipe
instead of .subscribe()
in TS.
Do keep track on your DevTools Network Tab though, make sure you are not
performing the same http call multiple times when rendering a component!
Time: {{ time$ | async }}
<!-- Capture the value without re-evaluating time$ -->
<div *ngIf="time$ | async as time; else loading">
{{ time }}
</div>
<ng-template #loading>
Waiting...
</ng-template>
Where time$
is an Observable or a Promise. Ex:
export class AppComponent {
time$ = interval(1000).pipe(
map(() => new Date().toString())
)
}
i18n Pipes
I18nPluralPipe
Display “1 item” but “2 items”, check the structure here.
{{ some_number | i18nPlural: pluralItemsMap }}
const pluralItemsMap = {
'=0': 'zero items',
'=1': 'one item',
'other': '# items',
}
I18nSelectPipe
{{ 'F' | i18nSelect: genderMap }}
const genderMap = {
'M': 'Mr',
'F': 'Ms',
'X': 'Mx',
}
Custom Pipes
Create your own pipes for displaying values relevant in your application domain.
Creation
Use ng generate
ng g pipe <name> options
Options:
--export=false
: Add to module declarations and exports?--flat=true
: Write at the top level of the project--lintFix=false
--module=
and--project=
--skipImport=false
and--skipTests=false
Example
import { Pipe, PipeTransform } from '@angular/core';
// Usage:
// 15:30 => {{ someDate | hours }}
// 15h => {{ someDate | hours:true:false }}
@Pipe({name: 'hours', pure: true})
export class HoursPipe implements PipeTransform {
transform(value: Date, showHours = true, showMinutes = true): string {
value = new Date(value);
if (showMinutes) {
return value.getHours() + ':' + value.getMinutes();
}
return value.getHours() + 'h';
}
}
Installation
If you didn’t use ng generate
.
@NgModule({
declarations: [AppComponent, CustomPipe],
exports: [CustomPipe] // Also add to `exports` when declaring in a shared module.
})
Testing
Run tests with ng test
.
import { HoursPipe } from './hours.pipe';
describe('HoursPipe', () => {
const someDate = new Date('2000-01-01T15:30:00');
let pipe: HoursPipe;
beforeEach(() => {
pipe = new HoursPipe();
});
it('shows h:M by default', () => {
const result = pipe.transform(someDate);
expect(result).toBe('15:30');
});
it('shows 15h with arguments {{ value | hours:true:false }}', () => {
const result = pipe.transform(someDate, true, false);
expect(result).toBe('15h');
});
});
Real World Examples
Some custom examples to get in the right mind what Pipes could be useful for.
Nl2brPipe
Convert newlines in string resources to <br>
s.
import { Pipe, PipeTransform } from '@angular/core';
// Usage: <div [innerHTML]="'Line1\nLine2' | nl2br"></div>
// Combine with ngx-translate
// Usage: <div [innerHTML]="'resource.key' | translate | nl2br"></div>
@Pipe({name: 'nl2br'})
export class Nl2brPipe implements PipeTransform {
transform(value: string): string {
return value.replace(/\n/g, '<br />');
}
}
UCFirstPipe
While capitalize
(capitalize each word) is bundled with Angular,
one to capitalize just the first letter is not.
@Pipe({ name: 'ucfirst' })
export class UcFirstPipe implements PipeTransform {
transform(text: any): string {
if (typeof text !== 'string') {
return text;
}
return text.slice(0, 1).toUpperCase() + text.slice(1);
}
}
BytesPipe
Fancy display of filesizes etc.
// Usage: 1024
@Pipe({ name: 'bytes' })
export class BytesPipe implements PipeTransform {
private dict: Array<{max: number; type: string}> = [
{ max: 1024, type: 'B' },
{ max: 1048576, type: 'KB' },
{ max: 1073741824, type: 'MB' },
{ max: 1.0995116e12, type: 'GB' },
];
transform(value: number, precision: number = 0): string | number {
if (isNaN(parseFloat(String(value))) || !isFinite(value)) {
return NaN;
}
const format = this.dict.find(d => value < d.max) || this.dict[this.dict.length - 1];
const calc = value / (format.max / 1024);
const num = this.applyPrecision(calc, precision);
return `${num}${format.type}`;
}
applyPrecision(num: number, precision: number) {
if (precision <= 0) {
return Math.round(num);
}
const tho = 10 ** precision;
return Math.round(num * tho) / tho;
}
}
ngx-pipes
Pretty much any generic pipe you can think of already exists. This is one project with such collection.
npm install ngx-pipes --save
import { NgPipesModule } from 'ngx-pipes';
// Each pipe is also exposed separately
// ex: import { ReversePipe, LeftTrimPipe } from 'ngx-pipes';
@NgModule({
imports: [NgPipesModule]
})
String: shorten , slugify , lpad , camelize
Array: unique , tail , flatten , every , …
Other: isGreaterThan , …
angular-pipes
Pretty much the same collection of pipes in this project.
npm install angular-pipes --save
import { NgModule } from '@angular/core';
import {
NgAggregatePipesModule, NgArrayPipesModule,
NgBooleanPipesModule, NgMathPipesModule,
NgObjectPipesModule, NgStringPipesModule,
} from 'angular-pipes';
// Import all pipes
// import { NgPipesModule } from 'angular-pipes';
@NgModule({
imports: [
NgArrayPipesModule,
NgStringPipesModule,
]
})
export class MyApplicationModule {}
String: decodeURI , latinize , repeat , replace , reverseStr , truncate , wrap , stripTags , …
Array: max , min , sum , count , head , …
Number / Math: bytes , degrees , ceil , round , sqrt , …
Other: defaults , …
- 1 June 2024 : Update to Angular v18: locale configuration, i18n pipes, updated official doc links etc