Dieser Beitrag bezieht sich auf Angular 2 Beta 0. Bei künftigen Releases kann es zu Änderungen kommen.
AngularJS 1.x erlaubt mit seinem Parser-Konzept das Verarbeiten von Benutzereingaben, bevor ng-model
sie über die Datenbindung ans Model zurückschreibt. Analog dazu können Formatter gebundene Daten aus dem Model formatieren, bevor sie in einem Eingabefeld aufscheinen.
In Angular 2 (Beta 0) gibt es solch ein Konzept nicht - zumindest nicht auf den ersten Blick. Allerdings findet man hier das Konzept des ValueAccessors
. Dabei handelt es sich um Klassen, die für die Synchronisation zwischen Steuerelementen und dem Model zuständig sind. Da unterschiedliche Steuerelemente, wie Checkboxes oder Eingabefelder, hierbei unterschiedlich zu nutzen sind, gibt es auch verschiedene ValueAccessor
-Implementierungen.
Für eigene Implementierungen bietet sich u. a. die Basis-Klasse DefaultValueAccessor
an. Zum Zurückschreiben ins Model weist sie die Callbacks onChange
und onTouched
auf. Diese Callbacks sind nach Änderungen im jeweiligen Steuerelement oder beim Verlassen des jeweiligen Steuerelements anzustoßen. Daneben gibt es eine Methode writeValue
, die einen Wert aus dem Model bekommt und ins Steuerelement schreibt.
Das nachfolgende Beispiel demonstriert, wie Entwicklungs-Teams damit die gebundenen Daten im Rahmen der Datenbindung modifizieren können. Es wandelt ein Datum, das im Model als ISO-String vorliegt, in ein deutsches Datum um und schreibt Modifikationen an diesem Datum in Form eines ISO-Strings ins Model zurück.
Um das Zurückschreiben ins Model zu beeinflussen, richtet das betrachtete Beispiel Event-Handler für das input
- und das blur
-Event des Host-Steuerelements ein. Beim Host-Steuerelement handelt es sich beispielsweise um ein Eingabefeld (<input>
) oder eine Textarea. Diese definiert es über die Eigenschaft host
des Directive
-Dekorators. Der Event-Handler für input
modifiziert die Eingaben durch Aufruf des zuvor besprochenen Callbacks onChange
.
Zum Beeinflussen der im Steuerelement präsentierten Daten überschreibt das Beispiel die Methode writeValue
. Sie kümmert sich um die Formatierung und delegiert anschließend an die Basis-Implementierung von writeValue
, welche ins Steuerelement schreibt, weiter.
Über die Eigenschaft selector
gibt die Implementierung bekannt, für welche Elemente sie zu nutzen ist. Der Wert input[date]
adressiert dabei input
-Elemente mit einem date
-Attribut, beispielsweise <input date [(ng-model)]="datum">
.
Damit Angular 2 die Direktive tatsächlich als ValueAccessor
nutzt, ist ein Provider einzurichten, der sie ans Token NG_VALUE_ACCESSOR
bindet. Zur Laufzeit ruft Angular 2 sämtliche Elemente, die an dieses Token gebunden wurden, ab und nutzt sie zur Datenbindung beim jeweiligen Element. Die beim Definieren des Providers hinterlegte Angabe multi: true
sagt aus, dass mehrere Elemente an NG_VALUE_ACCESSOR
gebunden sein dürfen. Die Indirektion über forwardRef
löst das Henne-Ei-Problem, das durch die gegenseitige Referenzierung zwischen dem Provider und dem ValueAccessor
entsteht.
import {Directive, Renderer, ElementRef, Self, forwardRef, Provider} from angular2/core;
import {NG_VALUE_ACCESSOR, DefaultValueAccessor } from angular2/common;
import {CONST_EXPR} from angular2/src/facade/lang;
const PROVIDER = CONST_EXPR(new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => DateValueAccessor), multi: true}));
@Directive({
selector:
input[date],
host: {(input): input($event.target.value), (blur): blur()},
providers: [PROVIDER]
})
export class DateValueAccessor extends DefaultValueAccessor {
constructor(_renderer: Renderer, _elementRef: ElementRef) {
super(_renderer, _elementRef);
}
blur() {
this.onTouched();
}
input(value) {
// Write back to model
if (value) {
value = value.split(/\./);
value = value[2] + "-" + value[1] + "-" + value[0];
}
this.onChange(value);
}
writeValue(value: any): void {
// Write to view
if (value) {
var date = new Date(value);
value = date.getDate() + "." + (date.getMonth()+1) + "." + date.getFullYear();
}
super.writeValue(value);
}
}