Angular Pro-Tipps: #1 – Angular Pipes

Das Angular 2 Release steht vor der Tür – höchste Zeit also, sich mit den neuen Features vertraut zu machen. Mit diesem Beitrag startet auf JAXenter eine neue Reihe, in der uns Web-Experten ihre persönlichen Tipps zur Arbeit mit Angular 2 verraten. Im ersten Teil zeigt Aaron Czichon, wie man die neuen Pipes in Angular 2 richtig nutzt.
Pro-Tipp #1: Angular Pipes
Angular 2 ist ein Framework voll mit neuen Konzepten und tollen Funktionen. Neben den Components, Provider und vielen weiteren neuen Features gibt es auch die sogenannten Pipes. Dieses Konzept finde ich sehr spannend, weshalb sie auch eines meiner persönlichen Highlights von Angular 2 sind.
Angular Pro-Tipps von Aaron Czichon
- Pro-Tipp #1: Angular Pipes
- Pro-Tipp #2: HTTP-Client
- Pro-Tipp #3: Dependency Injection
- Pro-Tipp #4: Was bringt Angular 5?
- Pro-Tipp #5: Dynamic Imports
- Alle Pro-Tipps auf einen Bick
In AngularJS (1) gibt es das Filter-Konzept, das den Pipes in Angular 2 sehr ähnlich ist. Angular Pipes ermöglichen es, Daten vor dem Rendering im DOM zu transformieren und/oder zu formatieren (etwa bei Strings). Oftmals möchte man mit Blick auf die User Experience die Daten innerhalb einer App nicht eins zu eins auch in der View darstellen. Stellen wir uns vor, wir bekommen von unserem API ein User-Objekt, das ein JavaScript-Date als JSON-String liefert. In etwa so:
let user = { "name": "Aaron Czichon", "last_login": "2016-07-07T18:25:43.511Z" }
Das JSON-Objekt wandeln wir in ein JavaScript-Objekt um und wollen nun dem Benutzer in der View das Datum und die Uhrzeit seines letzten Logins anzeigen. Dazu benötigen wir zunächst die passende View und eine entprechende Komponente.
user-data.html:
<!-- user-data.html --> <div style="display: flex; justify-content: space-around;"> <div id="name"></div> <div id="lastLogin"></div> </div>
// userData.component.ts import { Component } from '@angular/core'; @Component({ selector: 'user-data', templateUrl: 'user-data.html' }) export class UserDataComponent { let user = { name: 'Aaron Czichon', last_login: new Date('2016-07-07T18:25:43.511Z') } }
Um nun die User-Daten an die View zu binden, nutzen wir, wie bereits in AngularJS, Interpolation:
<!-- user-data.html --> <div style="display: flex; justify-content: space-around;"> <div id="name">{{user.name}}</div> <div id="lastLogin">{{user.last_login}}</div> </div>
Da die Interpolation in diesem Fall bei user.last_login
nun die toString()
-Methode nutzt und das Ergebnis davon in die View rendert, wird in das UI folgendes gerendert:
// Output Aaron Czichon Thu Jul 07 2016 20:25:43 GMT+0200 (Central Europe Daylight Time)
Unsere Nutzer interessieren sich aber weder für die aktuelle Zeitzone, noch dafür, um welchen Wochentag es sich handelt. Wir möchten ihm also nur das Datum und die Uhrzeit anzeigen lassen. Um diese Formatierung möglichst elegant umzusetzen, gibt es in Angular 2 die Möglichkeit, dafür Pipes zu nutzen. Man wendet dabei bei der Interpolation eine Art Filter an, der dann das Ergebnis liefert. Dazu benötigen wir eine neue TypeScript-Klasse, die mit einem Decorator als Pipe deklariert wird:
// loginTimeFormat.pipe.ts import { Pipe, PipeTransform } from '@angular/core'; @Pipe({name: 'loginTimeFormat'}) export class LoginTimeFormatPipe implements PipeTransform { transform(value: Date): string { let dd = value.getDate(); let MM = value.getMonth() + 1; let yyyy = value.getFullYear(); let hh = value.getHours(); let mm = value.getMinutes(); let formattedDayTime = `${dd}.${MM}.${yyyy} ${hh}:${mm} Uhr`; return formattedDayTime; } }
Nun müssen wir die Pipe noch mit unserer UserDataComponent
bekannt machen:
// userData.component.ts import { Component } from '@angular/core'; import {LoginTimeFormatPipe} from 'loginTimeFormat.pipe'; @Component({ selector: 'user-data', templateUrl: 'user-data.html', pipe: [LoginTimeFormatPipe] }) export class UserDataComponent { let user = { name: 'Aaron Czichon', last_login: new Date('2016-07-07T18:25:43.511Z') } }
Unsere Komponente wurde nun damit bekannt gemacht, dass es eine Pipe mit dem Namen LoginTimeFormatPipe
gibt, die auf das Schlüsselwort loginTimeFormat hört. Jetzt müssen wir die Pipe noch zur Anwendung bringen. Um eine Pipe zu nutzen, benötigen wir den sogenannten Pipe-Operator ( | ), der in AngularJS früher für Filter genutzt wurde.
<!-- user-data.html --> <div style="display: flex; justify-content: space-around;"> <div id="name">{{user.name}}</div> <div id="lastLogin">{{user.last_login | loginTimeFormat}}</div> </div>
Das Ergebnis wird dann wie erwartet formatiert und in unserer View angezeigt:
// Output Aaron Czichon 7.7.2016 20:25 Uhr
Um die Formatierung bzw. den Rückgabewert einer Pipe noch spezifischer definieren zu können, kann man die Pipes auch mit Parametern versehen. Wollen wir zum Beispiel das Datum optional gestalten und standardmäßig nur die Uhrzeit ausgeben, müssen wir zunächst unsere Pipe etwas erweitern.
// loginTimeFormat.pipe.ts import { Pipe, PipeTransform } from '@angular/core'; @Pipe({name: 'loginTimeFormat'}) export class LoginTimeFormatPipe implements PipeTransform { transform(value: Date, includeDate:boolean = false): string { let dd = value.getDate(); let MM = value.getMonth(); let yyyy = value.getFullYear(); let hh = value.getHours(); let mm = value.getMinutes(); if(includeDate) return `${dd}.${MM}.${yyyy} ${hh}:${mm} Uhr`; return `${hh}:${mm} Uhr`; } }
Wenn wir nun unser Template unverändert lassen, wird nur noch die Uhrzeit ausgegeben:
// Output Aaron Czichon 20:25 Uhr
Ändern wir aber unser Template ein wenig ab und geben als Parameter ein true
mit, wird das Datum wieder mit ausgegeben.
<!-- user-data.html --> <div style="display: flex; justify-content: space-around;"> <div id="name">{{user.name}}</div> <div id="lastLogin">{{user.last_login | loginTimeFormat:true}}</div> </div>
// Output Aaron Czichon 7.7.2016 20:25 Uhr
Ein weiterer Vorteil der Formatierung mit Pipes ist, dass mehrere Pipes in eine Chain (dt. Kette) geschaltet werden können. Es können also mehrere Pipes auf eine Interpolation angewendet werden. Ein kleines Beispiel dazu:
<!-- user-data.html --> <div style="display: flex; justify-content: space-around;"> <div id="name">{{user.name}}</div> <div id="lastLogin">{{user.last_login | loginTimeFormat:true | uppercase}}</div> </div>
Was nun passiert:
- Unser
user.last_login
wird in die LoginTimeFormat-Pipe gesteckt und dort formatiert. Das Datum wird dabei mit ausgegeben. - Das Ergebnis aus der LoginTimeFormat-Pipe wird in die uppercase-Pipe gesteckt.
- Das Ergebnis aus dieser Pipe wird im DOM gerendert.
// Output Aaron Czichon 7.7.2016 20:25 UHR
Angular 2 verfügt über einige Standard-Pipes, die out of the Box genutzt werden können. Dazu zählt in etwa die uppercase-Pipe, die wir im Beispiel oben genutzt haben.

- AngularJS auf einen Blick: Was das Angular-2-Release zu bieten hat
- Interview mit Christian Weyer: “Angular 2 & Co. meistern besser, woran Java scheiterte: Write once, run anywhere”
- Angular 2 Tutorial: Moderne Frontends für alle Plattformen entwickeln
- Auf dem Weg zu Angular 2
- Video: Angular 2 – Migrationspfade von 1.x zu 2.0
- Angular 2 mit NetBeans und TypeScript testen
- Interview mit Robin Böhm: „“Angular 2 wird sein ganzes Potential wohl erst in einigen Jahren entfalten können”“
- TypeScript, Angular 2, NetBeans IDE: Ein unschlagbares Trio
- Video: Moderne Authentifizierungslösungen mit OAuth 2.0 und AngularJS
Um Probleme mit undefined
– und/oder Null
-Exceptions zu verhindern, gibt es den Safe Navigation Operator. Diesen kann man einsetzen, wenn man sich nicht sicher ist, ob das Property, das gebunden wird, nicht zu einem Objekt gehört, das zum Zeitpunkt des Renderns noch undefined
und/oder null
ist. In unserem Beispiel sieht das so aus:
<!-- user-data.html --> <div style="display: flex; justify-content: space-around;"> <div id="name">{{user?.name}}</div> <div id="lastLogin">{{user?.last_login | loginTimeFormat:true | uppercase}}</div> </div>
Async-Pipes
Ein letztes Feature der Angular Pipes, das ich kurz erklären möchte, sind die sogenannten Async-Pipes. Mit Async-Pipes ist es möglich, Werte, die asynchron geladen werden, direkt an die View zu binden. Das ist vor allem bei Daten, die von Observables und Promises kommen, sehr nützlich. Das nachfolgende Beispiel veranschaulicht die Funktionsweise:
// AsyncPipeTest.component.ts import { Component } from '@angular/core'; @Component({ selector: 'async-test', templateUrl: 'async-pipe-test.html' }) export class AsyncPipeTestComponent { promise: Promise; constructor() { this.promise = new Promise(function(resolve, reject) { setTimeout(function() { resolve('Pipe Value from Promise rendered!'); }, 2000); }); } }
<!-- async-pipe-test.html --> <p>{{ promise | async}}</p>
Das Ergebnis:
// Output Pipe Value from Promise rendered!
Fazit
Pipes ersetzen quasi die Filter aus AngularJS und haben viele der Funktionen übernommen sowie neue Funktion hinzubekommen (wie etwa die Async-Pipes). Pipes ermöglichen es auf einfache Weise, Daten vor deren Anzeige zu transformieren und/oder zu formatieren. Es gibt einige Standard-Pipes, die out of the Box genutzt werden können. Benötigt man eine spezielle Formatierung oder Verarbeitung, können eigene Pipes geschrieben werden. Um Daten asynchron in der View darzustellen, kann die Async-Pipe genutzt werden.
Hier noch eine Liste der Standard-Pipes, die Angular 2 von Haus aus mitliefert:
- currency
- date
- uppercase
- lowercase
- json
- limitTo
- async
- decimal
- percent
Danke für den schönen Artikel!
Einen kleinen Fix würde ich allerdings vorschlagen: let MM = value.getMonth() + 1;