12. Pipes
<p>The technlogy's birthday is {{ birthday | date }}</p>
birthday = new Date(2016, 8, 16);
Paremeterizing a pipe:
<p>The technlogy's birthday is {{ birthday | date:"dd/MM/yyyy" }}</p>
<p>The technlogy's birthday is {{ birthday | date:format }}</p>
<button (click)="toggleFormat()">Toggle Format</button>
birthday = new Date(2016, 8, 16);
toggle = true;
get format() { return this.toggle ? 'shortDate' : 'fullDate'; }
toggleFormat() { this.toggle = !this.toggle; }
Chaining pipes:
<p>The chained technlogy's birthday is {{ birthday | date | uppercase }}</p>
<p>The chained technlogy's birthday is {{ birthday | date:'fullDate' | uppercase }}</p>
Custom Pipes:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
transform(value: number, exponent: string): number {
const exp = parseFloat(exponent);
return Math.pow(value, isNaN(exp) ? 1 : exp);
}
}
AppComponent:
<h2>Power Booster</h2>
<p>Super power boost: {{2 | exponentialStrength: 10}}</p>
Power Booster Calculator:
<h2>Power Booster Calculator</h2>
<div>Normal power: <input [(ngModel)]="power"></div>
<div>Boost factor: <input [(ngModel)]="factor"></div>
<p>Super power boost: {{power | exponentialStrength: factor}}</p>
power = 2;
factor = 8;
Pure Pipe:
import { Pipe, PipeTransform } from '@angular/core';
import { Flyer } from './technology';
@Pipe({
name: 'flyingTechnologiesPure'
})
export class FlyingTechnologiesPipe implements PipeTransform {
transform(allTechnologies: Flyer[]) {
return allTechnologies.filter(technology => technology.canFly);
}
}
FlyingTechnologiesComponent:
import { Component } from '@angular/core';
import { TECHNOLOGIES } from './technology';
@Component({
selector: 'app-flying-technologies',
template: `
<p>
New technology:
<input type="text" #box (keyup.enter)="addTechnology(box.value); box.value=''"
placeholder="technology name">
<input id="can-fly" type="checkbox" [(ngModel)]="canFly"> can fly
</p>
<p>
<input id="mutate" type="checkbox" [(ngModel)]="mutate">Mutate array
<button (click)="reset()">Reset</button>
</p>
<h4>Technologies that fly (piped)</h4>
<div *ngFor="let technology of (technologies | flyingTechnologiesPure)">
{{ technology.name }}
</div>
<h4>All Technologies (no pipe)</h4>
<div *ngFor="let technology of technologies">
{{ technology.name }}
</div>
`
})
export class FlyingTechnologiesComponent {
technologies: any[] = [];
canFly = true;
mutate = true;
title = 'Flying Technologies (pure pipe)';
constructor() { this.reset(); }
addTechnology(name: string) {
name = name.trim();
if (!name) { return; }
const technology = { name, canFly: this.canFly };
if (this.mutate) {
// pure pipe won't update display because technologies array reference is unchanged
// impure pipe will display
this.technologies.push(technology);
} else {
// pipe updates display because technologies array is a new object
this.technologies = this.technologies.concat(technology);
}
}
reset() {
this.technologies = TECHNOLOGIES.slice();
}
}
AppComponent:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{ title }}</h1>
<div class="left">
<app-flying-technologies></app-flying-technologies>
</div>
<div class="right">
<img [src]="imageUrl" class="img-round" alt="princess image">
</div>
`,
styles: [`h1 { font-weight: bold; }`]
})
export class AppComponent {
title = 'Tour of Technologies';
imageUrl = '../assets/polymer1.jpg-large';
}
Impure Pipe:
import { Pipe } from '@angular/core';
import { FlyingTechnologiesPipe } from './flying-technologies.pipe';
@Pipe({
name: 'flyingTechnologiesImpure',
pure: false
})
export class FlyingTechnologiesImpurePipe extends FlyingTechnologiesPipe { }
FlyingTechnologiesImpureComponent:
import { Component } from '@angular/core';
import { FlyingTechnologiesComponent } from './flying-technologies.component';
@Component({
selector: 'app-flying-technologies-impure',
template: `
<p>
New technology:
<input type="text" #box (keyup.enter)="addTechnology(box.value); box.value=''"
placeholder="technology name">
<input id="can-fly" type="checkbox" [(ngModel)]="canFly"> can fly
</p>
<p>
<input id="mutate" type="checkbox" [(ngModel)]="mutate">Mutate array
<button (click)="reset()">Reset</button>
</p>
<h4>Technologies that fly (piped)</h4>
<div *ngFor="let technology of (technologies | flyingTechnologiesImpure)">
{{ technology.name }}
</div>
<h4>All Technologies (no pipe)</h4>
<div *ngFor="let technology of technologies">
{{ technology.name }}
</div>
`
})
export class FlyingTechnologiesImpureComponent extends FlyingTechnologiesComponent { }
Impure AsyncPipe:
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';
@Component({
selector: 'app-technology-message',
template: `
<h2>Async Technology Message and AsyncPipe</h2>
<p>Message: {{ message$ | async }}</p>
<button (click)="resend()">Resend</button>
`
})
export class TechnologyAsyncMessageComponent {
message$: Observable<string>;
private messages = [
'Angular 5 Service Worker!',
'Angular 5 Animations!',
'Angular Dart 4 Performance!'
];
constructor() { this.resend(); }
resend() {
this.message$ = Observable.interval(500)
.map(i => this.messages[i])
.take(this.messages.length);
}
}
Impure Caching Pipe:
import { Pipe, PipeTransform } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
@Pipe({
name: 'fetch',
pure: false
})
export class FetchJsonPipe implements PipeTransform {
private cachedData: any = null;
private cachedUrl = '';
constructor(private http: Http) { }
transform(url: string): any {
if (url !== this.cachedUrl) {
this.cachedData = null;
this.cachedUrl = url;
this.http.get(url)
.map(result => result.json())
.subscribe(result => this.cachedData = result);
}
return this.cachedData;
}
}
AppComponent:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{ title }}</h1>
<div class="left">
<h2>Technologies from JSON File</h2>
<div *ngFor="let technology of ('assets/technologies.json' | fetch)">
{{ technology.name }}
</div>
<p>Technologies as JSON:
{{ 'assets/technologies.json' | fetch | json }}
</p>
</div>
<div class="right">
<img [src]="imageUrl" class="img-round" alt="princess image">
</div>
`,
styles: [`h1 { font-weight: bold; }`]
})
export class AppComponent {
title = 'Tour of Technologies';
imageUrl = '../assets/polymer1.jpg-large';
}
Let's have a look at the results in Chromium:

Built-In Pipes:
import { Component } from '@angular/core';
@Component({
selector: 'app-technologies',
template: `
<div class="row">
<div class="col-6">
<h2>{{ title }}</h2>
{{ technology.name | uppercase }} <br>
{{ technology.rating | number:'1.2-2' }} <br>
{{ technology.developers | number }} <br>
{{ technology.price | currency:'EUR':true:'5.2-2' }} <br>
{{ technology.type }} <br>
{{ technology.releaseDate | date:'longDate' }}
</div>
<div class="col">
<img class="w-100 p-3 rounded-circle" [src]="imageUrl" />
</div>
</div>
`
})
export class TechnologiesComponent {
title = 'List of Technologies';
imageUrl = './assets/polymer1.jpg';
technology = {
name: 'Angular 5',
rating: 9.9973,
developers: 1756000,
price: 10256.11,
type: 'Single Page Applications',
releaseDate: new Date(2017, 10, 8)
};
}
Custom Pipe:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'summary'
})
export class SummaryPipe implements PipeTransform {
transform(value: string, limit?: number) {
if (!value) { return null; }
const actualLimit = (limit) ? limit : 50;
return value.substr(0, actualLimit) + '...';
}
}
TechnologiesComponent:
import { Component } from '@angular/core';
@Component({
selector: 'app-technologies',
template: `
<div class="row">
<div class="col-6">
<h2>{{ title }}</h2>
{{ definition | summary:100 }}
</div>
<div class="col">
<img class="w-100 p-3 rounded-circle" [src]="imageUrl" />
</div>
</div>
`
})
export class TechnologiesComponent {
title = 'List of Technologies';
imageUrl = './assets/polymer1.jpg';
definition = `Keras (κέρας) means horn in Greek. It is a reference to a literary image from ancient Greek
and Latin literature, first found in the Odyssey, where dream spirits (Oneiroi, singular Oneiros)
are divided between those who deceive men with false visions, who arrive to Earth through
a gate of ivory, and those who announce a future that will come to pass, who arrive through
a gate of horn. It's a play on the words κέρας (horn) / κραίνω (fulfill), and ἐλέφας (ivory)
/ ἐλεφαίρομαι (deceive).`;
}
TitleCasePipe:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'titleCase'
})
export class TitleCasePipe implements PipeTransform {
transform(value: string): any {
if (!value) { return null; }
const words = value.split(' ');
for (let i = 0; i < words.length; i++) {
const word = words[i];
if (i > 0 && this.isPreposition(word)) {
words[i] = word.toLowerCase();
} else {
words[i] = this.toTitleCase(word);
}
}
return words.join(' ');
}
private toTitleCase(word: string): string {
return word.substr(0, 1).toUpperCase() + word.substr(1).toLowerCase();
}
private isPreposition(word: string): boolean {
const prepositions = ['of', 'the'];
return prepositions.includes(word.toLowerCase());
}
}
TechnologiesComponent:
import { Component } from '@angular/core';
@Component({
selector: 'app-technologies',
template: `
<div class="row">
<div class="col-6">
<h2>{{ title }}</h2>
<input type="text" [(ngModel)]="value">
<br>
{{ value | titleCase }}
</div>
<div class="col">
<img class="w-100 p-3 rounded-circle" [src]="imageUrl" />
</div>
</div>
`
})
export class TechnologiesComponent {
title = 'List of Technologies';
imageUrl = './assets/polymer1.jpg';
value = '';
}