Angular Pipes
posted in javascript on • by Wouter Van SchandevijlPipes: Chainable, declarative display-value transformations to use in your Html.
This post covers:
- All Builtin Angular pipes (json, async, string, array)
- 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 } 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 €
}
}
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
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: LOCALE_ID, useValue: 'fr'}],
})
export class AppModule { }
CurrencyPipe
Default currency is $
, this can as of yet not be set globally.
Consider using a Money class 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
<!-- Signature -->
{{ value_expression | number [ : digitsInfo [ : locale ] ] }}
<!-- Example -->
025.1230 => {{ 25.123 | number:'3.4-4'}}
PercentPipe
<!-- 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
<!-- 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
.
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$ = new Observable<string>(observer => {
setInterval(() => observer.next(new Date().toString()), 1000);
});
}
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 , …