Während AngularJS die Internationalisierung von Ausgaben erlaubt, bietet es (derzeit noch) keine Unterstützung für das Erfassen von Eingaben, die internationalisiert vorliegen. Allerdings gibt AngularJS dem Entwickler die Möglichkeit, benutzerdefinierte Validatoren festzulegen. Daneben kann der Entwickler angeben, wie die erfassten Eingaben für die Verwendung im Model aufzubereiten sind. Somit kann zum Beispiel der von einem deutschen Benutzer erfasste String „47,11“ im Model durch eine Eigenschaft vom Typ number mit dem Wert 47.11 repräsentiert werden.
Zum Definieren eigener Validatoren implementiert der Entwickler eine benutzerdefinierte Direktive. Dazu nutzt er die Funktion directive, welche AngularJS auf Modul-Ebene anbietet. Der erste Parameter ist der Name der Direktive; der zweite Parameter erwartet eine Funktion, die ein Objekt zurückgibt, das die Direktive beschreibt. Die Eigenschaft require dieses Objekts legt fest, dass die hier definierte Direktive nur für Felder, die auch die Direktive ng-model nutzen, verwendet werden darf. Dies macht sinn, denn ng-model kümmert sich schließlich um die Datenbindung. Die Eigenschaft link verweist auf eine Funktion, welche die Direktive für ein Feld aktiviert. Über ihre Parameter erhält diese Funktion von AngularJS den aktuellen Scope (scope), ein jQuery-Objekt, das das auf die Direktive angewandte HTML-Element repräsentiert (elm), ein Objekt, welches die Attribute dieses Elements beinhaltet (attrs), sowie ein Objekt, welches das Steuerelement im Form-Controller repräsentiert.
Die Direktive im nachfolgenden Listing prüft unter Verwendung von Globalize (https://github.com/jquery/globalize), ob die Eingabe des Benutzers einer Zahl entspricht, die entsprechend der aktuell gewählten Kultur formatiert wurde. Um das gewünschte Format zu ermitteln, greift link auf den Wert des Attributes gnumber zu. Dabei ist zu beachten, dass dieses Attribut denselben Namen wie die Direktive hat und somit jenen Wert aufweist, der der Direktive zugewiesen wurde.
Anschließend richtet link für das Eingabefeld einen Parser sowie einen Formatter ein. Bei beiden Konzepten handelt es sich um Funktionen. Ein Parser ist eine Funktion, die die Eingabe des Benutzers parst. Dabei kann sie dem Steuerelement mitteilen, ob die Eingabe korrekt validiert werden konnte, indem sie dessen Funktion $setValidity verwendet. Der erste Parameter von $setValidity ist der Name der Validierungslogik; der zweite Parameter zeigt an, ob ein Validierungsfehler aufgetreten ist. Jenen Wert, den der Parser zurückgibt, schreibt AngularJS zurück ins Model. Auf diese Weise kann die link-Funktion veranlassen, dass Benutzereingaben nicht nur in Form von Strings sondern in Form anderer Typen in das Model geschrieben werden. Besonders nützlich ist dies bei der Behandlung von Datumswerten, wo das Zurückschreiben eins Date-Objektes wünschenswert ist.
Der Formatter kümmert sich um die Formatierung des Wertes, bevor dieser an das Eingabefeld gebunden wird. Er formatiert den Wert unter Verwendung von Globalize und des an die Direktive übergebenen Formats.
Bei Betrachtung des vorliegenden Quellcodes fällt auf, dass ein Steuerelement mehrere Parser und Formatter besitzen kann, zumal es sich bei den Eigenschaften $parsers und $formatters um Arrays handelt. Dies ist notwendig, zumal pro Steuerelement mehrere Direktiven mit jeweils einer eigenen Validierungslogik verwendet werden können.
var app = angular.module("Flug", []);
[…]
app.directive(gnumber, function () {
return {
require: ngModel,
link: function (scope, elm, attrs, ctrl) {
var format = attrs.gnumber;
ctrl.$parsers.unshift(function (viewValue) {
var number = Globalize.parseFloat(viewValue);
if (!isNaN(number)) {
ctrl.$setValidity(gnumber, true);
return number;
}
else {
ctrl.$setValidity(gnumber, false);
return undefined;
}
});
ctrl.$formatters.unshift(function (value) {
var formatted = Globalize.format(value, format);
return formatted;
});
}
};
});
Das nachfolgende Listing demonstriert, wie die im letzten Beispiel beschriebene Direktive eingesetzt werden kann. Dazu erhält das zu validierende Input-Element ein Attribut, welches dem Namen der Direktive entspricht. Der Wert dieses Attributes wird auf das Format gesetzt, das Globalize verwenden soll. Um herauszufinden, ob die Validierung erfolgreich war, greift das Beispiel auf die Eigenschaft form.flugNummer.$error.gnumber zu, wobei gnumber jener Bezeichner ist, den die Direktive an $setValidity übergibt.
<div>Preis</div>
<div><input ng-model="vm.preis" gnumber="N2" /></div>
<div ng-show="form.flugNummer.$dirty && form.flugNummer.$error.gnumber">
Muss eine Zahl sein!
</div>