Der folgende Abschnitt ist veraltet!In der aktuellen Version ist folgendes Vorgehen zu verwenden:
https://javaeeblog.wordpress.com/?p=4409
Angular 2 steht in den Startlöchern bereit, wenn auch erst in einer frühen Beta-Phase. Zeit genug also sich mit der neuen Welt von Anuglar JS vertraut zu machen. Bei den ersten Schritten stolpert man sehr schnell über die Frage: Wie findet eine Konvertierung von Daten zwischen View und Modell statt? In Angular1 standen uns hierfür $formatters und $parsers zur Verfügung, in Angular2 gibt es dazu bisher kein offizielles Konzept (siehe GitHub ). Aber werfen wir einen Blick auf die Möglichkeiten die sich uns heute schon bieten…
Das typische Binding einer Eingabe Komponente in Angular 2 sieht bekanntlich so aus:
<input id="datefield" [(ngModel)]="recordService.record.startTime" />
Durch die doppelte Klammerung („[(….)]“, alternativ: „onbind-ngModel“) zusammen mit der „ngModel“-Direktive führen wir an dieser Stelle ein Two-Way-Binding ein mit dessen Hilfe die Werte unseres Models in der View angezeigt und Änderungen zurück gespielt werden. Nutzen wir dieses Vorgehen auch für ein Datenfeld vom Typ „Date“ führt dies dazu, dass wir in der Anzeige die unansehnliche String Repräsentation eines Date-Objektes finden, das bei Änderungen zu allem Überfluss auch noch als String zurück ins Model geschrieben wird. Ein Converter / Parser muss also her. $parsers und $formatters aus Angular 1 haben (bisher?) ihren Weg noch nicht in den Version 2. geschafft, dennoch gibt es mit den bestehenden Mittel die Möglichkeit eine solche Funktion zu implementieren: eine eigene Direktive:
(hier einmal die gesamte Implementierung in TypeScript, im Folgenden werde wir auf die einzelnen Punkte eingehen)
import {Directive, Renderer, ElementRef} from 'angular2/core' import {NgModel, DefaultValueAccessor} from 'angular2/common' @Directive({ selector: '[tsdate]', host: { '(change)': 'onChanges($event.target.value)' } }) export class DateDirective extends DefaultValueAccessor { constructor(private model: NgModel, private el: ElementRef, private renderer: Renderer) { super(renderer, el); model.valueAccessor = this; } writeValue(obj: Date): void { if (obj) { super.writeValue(this.formatDate(obj)); } } onChanges(value: string) { this.onChange(this.parseDate(value)); } formatDate(inputDate: Date): string { // Datum formatieren return ... } parseDate(inputString: string): Date { // Datum parsen return ...Date... } }
Mittels des TypeScript Decorators „Directive“ erzeugen wir hier eine Direktive die später an HTML Elemente angehängt werden kann. Über welche Property dies geschieht legen wir durch das selector Attribut fest: selector: ‚[tsdate]‘, Verwendung: . Als weiteren Schritt wollen wir geänderte Eingaben des Textfeldes auf dem die Direktive verwendet werden soll mit einer entsprechenden Methode versehen um darauf zu reagieren. Mit Hilfe des „host“ Attributes registrieren wir uns auf das entsprechende „change“-Event des übergeordneten HTML Elements (in unserem Fall: „input“) um zusammen mit dem Wert des Textfeldes als Parameter eine unserer Methoden auf zu rufen.
Die eigentliche Implementierung unserer Logik findet in der anschließend deklarierten Klasse statt:
export class DateDirective extends DefaultValueAccessor
Dabei erben wir von der Angular zur Verfügung gestellten Klasse: DefaultValueAccessor (implements ControlValueAccessor). Diese Klasse wird von der NgModel-Direktive verwendet um Änderungen ins Model zurück zu schreiben und auf Eingaben des Benutzers zu reagieren.
constructor(private model: NgModel, private el: ElementRef, private renderer: Renderer) { super(renderer, el); model.valueAccessor = this; }
Innerhalb des Konstruktors lassen wir uns unter anderem das NgModel injizieren mit dessen Hilfe wir das Ein- und Ausgabeverhalten der Direktive anpassen werden. Dazu ändern wir den valueAccessor auf die aktuelle Klasse um die von uns benötigten Methoden zu überschreiben und nach Bedarf an zu passen.
Die beiden entscheidenen Methoden die wir dazu nutzen sind:
writeValue(obj: Date): void
Die „writeValue“ Methode wird aufgerufen um einen Wert aus dem Model in die View zu übertragen. An dieser Stelle greifen wir ein und nehmen das Date-Objekt aus dem Model um es in eine für uns angenehme String-Repräsentation zu bringen. Das eigentliche setzen des value-Attributes des Textfeldes überlassen wir dann der Standard Implementierung.
onChanges(value: string)
Wie in der Konfiguration angegeben wird diese Methode bei Änderungen des Textfeldes aufgerufen und mit dem geänderten Wert versorgt. An dieser Stelle führen wir die Umwandlung des Strings in ein echtes JavaScript Datum durch, um zum Abschluss erneut die Standard Implementierung dafür zu bemühen diesen Wert ans Model zu übertragen
Wie wir sehen konnten besteht auch mit den in der Beta-Version zur Verfügung gestellten Mitteln eine recht übersichtliche Möglichkeit einen Konverter/Parser zu implementieren. Ob dieses Vorgehen in dieser Form auch in Zukunft Verwendung findet oder es eine ergänzendes Konzept in Angular 2 geben wird muss sich zeigen.
Das Beispiel findet sich noch einmal hier in seiner gesamten Form: github