Nicht nur AngularJS, auch JSF bietet die Möglichkeit Eingaben mit einer Validierung zu versehen. In unseren Projekten verzichten wir allerdings seit geraumer Zeit darauf die JSF-eigenen Validatoren zu verwenden und setzen als Mittel der Wahl BeanValidation ein um GUI unabhängig Regeln in unserem Datenmodell zu verankern. Dies geschieht im Fall von JSF und mit der Hilfe von Komponenten Bibliotheken (Primefaces, Richfaces) nahtlos ohne dass wir uns explizit um die Registrierung dieser Regeln kümmern müssten. Aber wie sieht es mit AngularJS aus? Wie können wir die Validierungsregeln, die an unserem Datenmodel deklariert sind, in eine JavaScript Anwendung nutzen? Ein Modul welches uns hier zur Seite steht ist:
valdr
Valdr verwendet bei der Deklaration seiner Regeln einen Model-zentrierten Ansatz. Das heißt anstatt einem konkreten Eingabefeld eine Regel zuzuweisen findet diese Deklaration auf Basis von Objekten und dessen Attributen statt. Hier ein sehr einfaches Beispiel wie eine solche Deklaration aussehen könnte:
valdrProvider.addConstraints({ 'Demo': { //Um welches Objekt handelt es sich? 'messageAttribute': { // welches Attribut? 'size': { // welche Regel soll angewendet werden? 'min': 1, 'max': 10, 'message': 'Zeichen: 1-10' }, //weitere Regeln für 'messageAttribute' }, //weitere Attribute + Regeln für "Demo" }, // weitere Objekte });
Verwendet wir hier eine JSON-Struktur die festlegt welche Objekte für die Validierung zur Verfügung stehen und welche Attribute eine Validierungsregel beinhalten. Neben der hier gezeigten „size“ Regel existieren natürlich noch weitere:
- size – Längen-Prüfung mit min und max
- minLength,maxLength – analog zu ‚size‘ (jedoch nur für Strings)
- min,max – Prüfung von Zahlen
- required – Pflichtfeld
- pattern – Prüfung mittels regex
- email – Prüfung auf gültige Email
- digits – Prüfung auf Zahlen mit Vor- und Nachkommastellen (integer, fraction)
- url – Prüfung auf gültige URL
- future,past – zeitliche Prüfung (zusätzlich wird das Modul: Moment.js benötigt)
Bei der Verwendung innerhalb unserer Formulare ist es nun lediglich nötig das verwendete Objekt über die valdr-type Direktive zu deklarieren und darauf zu achten das unsere Eingabefelder mittels „name“ Attribut mit dem Namen in unseren Validierungsregeln übereinstimmen. Die Ausgabe der Nachrichten erfolgt automatisch wenn zusätzlich zu valdr auch das Module valdr-messages installiert ist und das Eingabefeld in einem Block deklariert ist der die Direktive: valdr-form-group beinhaltet:
<head> < script src="bower_components/angular/angular.js" type="text/javascript"></script> < script src="bower_components/valdr/valdr.js" type="text/javascript"></script> < script src="bower_components/valdr/valdr-message.js" type="text/javascript"></script> < script src="app.js" type="text/javascript"></script> </head> <body ng-controller="bv as vm"> <form valdr-type="Demo" name="vm.demoForm"> < div valdr-form-group> < label for="message">Message</label> < input id="message" name="messageAttribute" ng-model="vm.messageInput"> </div> </form> </body>
Alternativ zur automatischen Fehlerausgabe werden auch die AngularJS – Flags $valid/$invalid auf dem Formular gesetzt + entsprechende valdr-Fehlerobjekte im Formular $error-Attribut abgelegt. Über letztgenanntes lässt sich so auch eine eigene Fehlerausgabe implementieren die z.B. alle Meldungen zu einer Liste zusammenfasst (idealerweise würde man dies in eine Direktive auslagern)
(JavaScript 6 Syntax)
// aktualsiert unserer Liste von Fehlermeldungen $scope.$watch(function() { return vm.demoForm.$valid }, function() { vm.errorMessages = vm.getErrors(); }); // liefert eine flache Liste von Fehlermeldungen im Format: "Feld: Meldung" // die mittels ng-repeat angezeigt werden kann // der erste Fehler des ersten fehlerhaften Attributes liegt z.B. hier: // demoForm.$error.valdr[0].valdrViolations[0] vm.getErrors = function() { if (this.demoForm.$error && this.demoForm.$error.valdr) return this.demoForm.$error.valdr .map(field => field.valdrViolations .map(error => error.field + " : " + error.message)) .reduce((prev, current) => prev.concat(current)); }
Bean Validation
Die Einbeziehung von BeanValidation erfolgt schließlich durch ein zusätzliches Modul: valdr-bean-validation auf AngularJS-Seite und die Einbindung einer Java Bibliothek auf Java-Seite. Die Grundidee ist einfach: aus unserem Java-Projekt werden die Validierungsregeln im oben gezeigten Format auf Basis der BeanValidation Annotationen generiert. Die unterstützen BeanValidation Annotationen sind: NotNull, Min, Max, Size, Digits, Pattern, Future, Past, Email (Hibernate) und URL (Hibernate).
Für die Einbindung stellt das Modul zwei Möglichkeiten zur Verfügung:
- generierung zur Build-Zeit mittels Maven (s. valdr-bean-validation#cli-client
- über ein Servlet, welches zur Laufzeit die entsprechenden Informationen generiert.
Um eine möglichst lose Kopplung unseres Java Projektes und der AngularJS Anwendung zu erreichen hier ein Beispiel über die genannte Servlet Variante. Folgende Maven-Dependency fügen wir hinzu:
<dependency> <groupId>com.github.valdr</groupId> <artifactId>valdr-bean-validation</artifactId> <version>1.1.2</version> </dependency>
Neben der Registrierung des Servlets wird schließlich noch eine Konfiguration benötigt bevor die JSON Formate über den valdrProvider geladen werden können. Ohne weitere Konfiguration wird diese Datei im Classpath unter dem Namen: valdr-bean-validation.json gesucht (kann über den Servlet-Init-Parameter “configFile” angepasst werden)
Java
AngualrJS
Das Modul valdr bietet uns also die Möglichkeit unsere bewährte Deklaration von Validierungsregeln mittels BeanValidation auch in die JavaSccript Welt zu tragen. Neben den hier gezeigten Möglichkeiten ist unter anderem auch die Internationalisierung der Fehlernachrichten und die Entwicklung eigener Validatoren möglich, weitere Details siehe: valdr @ github