6. Clientseitige JavaScript-Validierung
Im vorigen Kapitel haben wir uns mit der serverseitigen Validierung befasst. Kehren wir nun zur Architektur einer Spring-Anwendung MVC zurück:
![]() |
BD
Bislang enthielten die an den Client gesendeten Seiten kein JavaScript. Wir befassen uns nun mit dieser Technologie, die es uns zunächst ermöglichen wird, clientseitige Validierungen durchzuführen. Das Prinzip ist wie folgt:
- JavaScript sendet die Werte an den Webserver;
- und somit kann es vor diesem POST die Gültigkeit der Daten überprüfen und den Aufruf von POST verhindern, falls diese ungültig sind;
Wir werden das Formular verwenden, das wir serverseitig validiert haben. Nun werden wir die Möglichkeit bieten, es sowohl clientseitig als auch serverseitig zu validieren.
Hinweis: Das Thema ist komplex. Leser, die sich nicht für dieses Thema interessieren, können direkt zu Absatz 7 springen.
6.1. Die Funktionen des Projekts
Wir stellen einige Ansichten des Projekts vor, um dessen Funktionen zu veranschaulichen. Die Startseite wird mit dem URL und dem [http://localhost:8080/js01.html] generiert
![]() |
Die Validierungen wurden auf beiden Seiten eingerichtet: auf der Client- und auf der Serverseite. Da die POST nur dann erfolgt, wenn die Werte auf der Clientseite als gültig angesehen wurden, sind die Validierungen auf der Serverseite immer erfolgreich. Daher wurde ein Link bereitgestellt, um die clientseitigen Validierungen zu deaktivieren. In diesem Modus gilt die Funktionsweise, die wir bereits behandelt haben. Hier ein Beispiel:
123 ![]() |
- in [1] die eingegebenen Werte;
- in [2] die Fehlermeldungen zu den Eingaben;
- in [3] eine Zusammenfassung der Fehler mit folgenden Angaben zu jedem einzelnen:
- den Namen des validierten Feldes,
- den Fehlercode,
- die Standardmeldung zu diesem Fehlercode;
Nun aktivieren wir die clientseitige Validierung:
![]() |
- in [1] die eingegebenen Werte. Man kann feststellen, dass fehlerhafte Eingaben eine besondere Formatierung aufweisen;
- in [2] die Fehlermeldungen zu den fehlerhaften Eingaben. Sie sind identisch mit denen, die vom Server generiert werden;
- in [3-4] ist nichts mehr zu sehen, da die Übertragung von POST an den Server nicht stattfindet, solange fehlerhafte Eingaben vorhanden sind;
6.2. Serverseitige Validierung
6.2.1. Konfiguration
Zunächst erstellen wir ein neues Maven-Projekt [springmvc-validation-client]:
![]() |
Wir entwickeln das Projekt wie folgt weiter:
![]() |
Die Klasse [Config] konfiguriert das Projekt. Sie ist identisch mit der Klasse aus den vorherigen Projekten:
package istia.st.springmvc.config;
import java.util.Locale;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
@Configuration
@ComponentScan({ "istia.st.springmvc.controllers", "istia.st.springmvc.models" })
@EnableAutoConfiguration
public class Config extends WebMvcConfigurerAdapter {
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("i18n/messages");
return messageSource;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
@Bean
public CookieLocaleResolver localeResolver() {
CookieLocaleResolver localeResolver = new CookieLocaleResolver();
localeResolver.setCookieName("lang");
localeResolver.setDefaultLocale(new Locale("fr"));
return localeResolver;
}
@Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".xml");
templateResolver.setTemplateMode("HTML5");
templateResolver.setCacheable(true);
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
@Bean
SpringTemplateEngine templateEngine(SpringResourceTemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
}
Die Klasse [Main] ist die ausführbare Klasse des Projekts:
package istia.st.springmvc.main;
import istia.st.springmvc.config.Config;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
public class Main {
public static void main(String[] args) {
// Die Anwendung wird gestartet
ApplicationContext context = SpringApplication.run(Config.class, args);
// Die von Spring gefundenen Beans werden angezeigt
System.out.println("Liste des beans Spring");
String[] beanNames = context.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}
- Zeile 13: Spring Boot wird mit der Konfigurationsdatei [Config] gestartet;
- Zeilen 15–20: In diesem Beispiel zeigen wir, wie man die von Spring verwaltete Objektliste anzeigt. Dies kann nützlich sein, wenn man manchmal den Eindruck hat, dass Spring eine unserer Komponenten nicht verwaltet. Auf diese Weise lässt sich dies überprüfen. Außerdem kann man so die von Spring Boot vorgenommene Autokonfiguration überprüfen. In der Konsole erhält man eine Liste, die in etwa wie folgt aussieht:
Wir haben die in der Klasse [Config] definierten Objekte hervorgehoben.
6.2.2. Das Formularmodell
Setzen wir die Erkundung des Projekts fort:
![]() |
Die Klasse [Form01] ist die Klasse, die die übermittelten Werte entgegennimmt. Sie sieht wie folgt aus:
package istia.st.springmvc.models;
import java.util.Date;
import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;
import org.hibernate.validator.constraints.URL;
import org.springframework.format.annotation.DateTimeFormat;
public class Form01 {
// Übermittelte Werte
@NotNull
@AssertFalse
private Boolean assertFalse;
@NotNull
@AssertTrue
private Boolean assertTrue;
@NotNull
@Future
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dateInFuture;
@NotNull
@Past
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dateInPast;
@NotNull
@Max(value = 100)
private Integer intMax100;
@NotNull
@Min(value = 10)
private Integer intMin10;
@NotNull
@NotBlank
private String strNotEmpty;
@NotNull
@Size(min = 4, max = 6)
private String strBetween4and6;
@NotNull
@Pattern(regexp = "^\\d{2}:\\d{2}:\\d{2}$")
private String hhmmss;
@NotNull
@Email
@NotBlank
private String email;
@NotNull
@Length(max = 4, min = 4)
private String str4;
@Range(min = 10, max = 14)
@NotNull
private Integer int1014;
@NotNull
@DecimalMax(value = "3.4")
@DecimalMin(value = "2.3")
private Double double1;
@NotNull
private Double double2;
@NotNull
private Double double3;
@URL
@NotBlank
private String url;
// Validierung auf Client-Seite
private boolean clientValidation = true;
// Lokal
private String lang;
...
}
Wir finden hier bereits bekannte Validatoren wieder. Darüber hinaus werden wir den Begriff der spezifischen Validierung einführen. Dabei handelt es sich um eine Validierung, die nicht mit einem vordefinierten Validator formalisiert werden kann. Hier wird gefordert, dass [double1+double2] im Intervall [10,13] liegt.
6.2.3. Der Validator
Der Prüfer [JsController] lautet wie folgt:
![]() |
package istia.st.springmvc.controllers;
import istia.st.springmvc.models.Form01;
...
@Controller
public class JsController {
@RequestMapping(value = "/js01", method = RequestMethod.GET, produces = "text/html; charset=UTF-8")
public String js01(Form01 formulaire, Locale locale, Model model) {
setModel(formulaire, model, locale, null);
return "vue-01";
}
...
// Vorbereitung der Vorlage für die Ansicht „vue-01“
private void setModel(Form01 formulaire, Model model, Locale locale, String message) {
...
}
}
- Zeile 9: die Aktion [/js01];
- Zeile 10: Ein Objekt vom Typ [Form01] wird instanziiert und automatisch in die Vorlage eingefügt, verknüpft mit dem Schlüssel [form01];
- Zeile 10: Die Locale und die Vorlage werden in die Parameter eingefügt;
- Zeile 11: Mit diesen Informationen wird die Vorlage vorbereitet;
- Zeile 12: Die Ansicht [vue-01.xml] wird angezeigt;
Die Methode [setModel] lautet wie folgt:
// Vorbereitung des Modells für die Ansicht vue-01
private void setModel(Form01 formulaire, Model model, Locale locale, String message) {
// Es werden nur die Sprachversionen fr-FR und en-US unterstützt
String language = locale.getLanguage();
String country = null;
if (language.equals("fr")) {
country = "FR";
formulaire.setLang("fr_FR");
}
if (language.equals("en")) {
country = "US";
formulaire.setLang("en_US");
}
model.addAttribute("locale", String.format("%s-%s", language, country));
// die eventuelle Meldung
if (message != null) {
model.addAttribute("message", message);
}
}
- Der Zweck der Methode [setModel] besteht darin, folgende Informationen in die Vorlage einzufügen:
- Informationen zur Ländereinstellung sowie
- die als letzten Parameter übergebene Meldung;
- Zeile 14: In die Vorlage werden Informationen zur Locale (Sprache, Land) eingefügt;
- Zeilen 16–18: Die gegebenenfalls als Parameter übergebene Meldung wird in die Lokalisierung eingefügt;
- Zeilen 8, 12: Die Informationen zur Locale werden ebenfalls im Formular [Form01] gespeichert. Das JavaScript wird diese Informationen verwenden;
Die im Formular [vue-01.xml] eingegebenen Werte werden an die nachfolgende Aktion [/js02] übermittelt:
@RequestMapping(value = "/js02", method = RequestMethod.POST, produces = "text/html; charset=UTF-8")
public String js02(@Valid Form01 formulaire, BindingResult result, RedirectAttributes redirectAttributes, Locale locale, Model model) {
Form01Validator validator = new Form01Validator(10, 13);
validator.validate(formulaire, result);
...
}
- Zeile 2: Die Anmerkung [@Valid Form01 formulaire] bewirkt, dass die übermittelten Werte den Validatoren der Klasse [Form01] unterzogen werden. Wir wissen, dass es im Intervall [10,13] eine spezifische Validierung [double1+double2] gibt. Bei Erreichen von Zeile 3 wurde diese Validierung noch nicht durchgeführt;
- Zeile 3: Es wird das folgende Objekt [Form01Validator] angelegt:
![]() |
package istia.st.springmvc.validators;
import istia.st.springmvc.models.Form01;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class Form01Validator implements Validator {
// Validierungsbereich
private double min;
private double max;
// Konstruktor
public Form01Validator(double min, double max) {
this.min = min;
this.max = max;
}
@Override
public boolean supports(Class<?> classe) {
return Form01.class.equals(classe);
}
@Override
public void validate(Object form, Errors errors) {
// validiertes Objekt
Form01 form01 = (Form01) form;
// der Wert von [double1]
Double double1 = form01.getDouble1();
if (double1 == null) {
return;
}
// der Wert von [double2]
Double double2 = form01.getDouble2();
if (double2 == null) {
return;
}
// [double1+double2]
double somme = double1 + double2;
// Validierung
if (somme < min || somme > max) {
errors.rejectValue("double2", "form01.double2", new Double[] { min, max }, null);
}
}
}
- Zeile 8: Um eine spezifische Validierung zu implementieren, erstellen wir eine Klasse, die die Spring-Schnittstelle [Validator] implementiert. Diese Schnittstelle verfügt über zwei Methoden: [supports] in Zeile 21 und [validate] in Zeile 26;
- Zeilen 21–23: Die Methode [supports] erhält ein Objekt vom Typ [Class]. Sie muss true zurückgeben, um anzugeben, dass sie diese Klasse unterstützt, andernfalls false;
- Zeile 22: Wir legen fest, dass die Klasse [Form01Validator] nur Objekte vom Typ [Form01] validiert;
- Zeilen 15–18: Zur Erinnerung: Wir wollen die Einschränkung [double1+double2] im Intervall [10,13] implementieren. Anstatt uns auf dieses Intervall zu beschränken, werden wir die Einschränkung [double1+double2] im Intervall [min, max] überprüfen. Aus diesem Grund haben wir einen Konstruktor mit diesen beiden Parametern;
- Zeile 26: Die Methode [validate] wird mit einer Instanz des validierten Objekts aufgerufen, also hier einer Instanz von [Form01], sowie mit der Sammlung der derzeit bekannten Fehler [Errors errors]. Wenn die durch die Methode [validate] durchgeführte Validierung fehlschlägt, muss sie ein neues Element in der Sammlung [Errors errors] anlegen;
- Zeile 43: Die Validierung ist fehlgeschlagen. Mit der Methode [Errors.rejectValue] wird ein Element zur Sammlung [Errors errors] hinzugefügt, deren Parameter wie folgt lauten:
- Parameter 1: normalerweise der Name des fehlerhaften Feldes. Hier wurden die Felder [double1, double2] getestet. Man kann entweder eines der beiden angeben,
- die zugehörige Fehlermeldung oder genauer gesagt deren Schlüssel in den externen Meldungsdateien:
[messages_fr.properties]
form01.double2=[double2+double1] doit être dans l''intervalle [{0},{1}]
[messages_en.properties]
form01.double2=[double2+double1] must be in [{0},{1}
Hier handelt es sich um Meldungen, die mit {0} und {1} parametrisiert sind. Daher müssen für diese Meldung zwei Werte angegeben werden. Genau das übernimmt der dritte Parameter der Methode [Errors.rejectValue].
- Der vierte Parameter ist eine Standardmeldung für den Fehler;
Kommen wir zurück zur Aktion [/js02]:
@RequestMapping(value = "/js02", method = RequestMethod.POST, produces = "text/html; charset=UTF-8")
public String js02(@Valid Form01 formulaire, BindingResult result, RedirectAttributes redirectAttributes, Locale locale, Model model) {
Form01Validator validator = new Form01Validator(10, 13);
validator.validate(formulaire, result);
if (result.hasErrors()) {
StringBuffer buffer = new StringBuffer();
for (ObjectError error : result.getAllErrors()) {
buffer.append(String.format("[name=%s,code=%s,message=%s]", error.getObjectName(), error.getCode(), error.getDefaultMessage()));
}
setModel(formulaire, model, locale, buffer.toString());
return "vue-01";
} else {
redirectAttributes.addFlashAttribute("form01", formulaire);
return "redirect:/js01.html";
}
}
- Zeile 4: Der Validator [Form01Validator] wird mit folgenden Parametern ausgeführt:
- Parameter 1: das zu validierende Objekt,
- Parameter 2: die Fehlerliste dieses Objekts. Dabei handelt es sich um das Objekt [BindingResult result], das als Parameter an die Aktion übergeben wurde. Wenn die Validierung fehlschlägt, weist dieses Objekt einen weiteren Fehler auf;
- Zeile 5: Es wird geprüft, ob Validierungsfehler vorliegen;
- Zeilen 7–10: Die Fehlerliste wird durchlaufen, um für jeden Fehler Folgendes zu speichern:
- den Namen des validierten Objekts,
- seinen Fehlercode,
- seine Standardfehlermeldung;
- Zeile 10: Mit diesen Informationen wird das Modell der Ansicht [vue-01.xml] erstellt. Diesmal gibt es eine Meldung, die verkettete und gekürzte Version der verschiedenen Fehlermeldungen;
- Zeilen 12–15: Sind alle übermittelten Werte gültig, wird der Client zur Aktion [/js01] weitergeleitet, wobei die übermittelten Werte als Flash-Attribut gesetzt werden;
6.2.4. Die Ansicht
Die Ansicht [vue-01.xml] ist komplex. Wir werden hier nur einen kleinen Ausschnitt davon vorstellen:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring 4 MVC</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="/css/form01.css" />
<script type="text/javascript" src="/js/jquery/jquery-1.10.2.min.js"></script>
...
</head>
<body>
<!-- Titel -->
<h3>
<span th:text="#{form01.title}"></span>
<span th:text="${locale}"></span>
</h3>
<!-- Menü -->
<p>
...
</p>
<!-- Formular -->
<form action="/someURL" th:action="@{/js02.html}" method="post" th:object="${form01}" name="form" id="form">
<table>
<thead>
<tr>
<th class="col1" th:text="#{form01.col1}">Contrainte</th>
<th class="col2" th:text="#{form01.col2}">Saisie</th>
<th class="col3" th:text="#{form01.col3}">Validation client</th>
<th class="col4" th:text="#{form01.col4}">Validation serveur</th>
</tr>
</thead>
<tbody>
<!-- erforderlich -->
<tr>
<td class="col1">required</td>
<td class="col2">
<input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('strNotEmpty')}" th:errors="*{strNotEmpty}" class="error">Donnée erronée</span>
</td>
</tr>
...
</tbody>
</table>
<p>
<!-- Bestätigungsschaltfläche -->
<input type="submit" th:value="#{form01.valider}" value="Valider" onclick="javascript:postForm01()" />
</p>
</form>
<!-- Meldung der serverseitigen Validatoren -->
<br/>
<fieldset class="fieldset">
<legend>
<span th:text="#{server.error.message}"></span>
</legend>
<span th:text="${message}" class="error"></span>
</fieldset>
</body>
</html>
Diese Seite verwendet eine Reihe von Meldungen, die in den externen Meldungsdateien zu finden sind:
[messages_fr.properties]
form01.title=Formulaire - Validations côté client - locale=
form01.col1=Contrainte
form01.col2=Saisie
form01.col3=Validation client
form01.col4=Validation serveur
form01.valider=Valider
server.error.message=Erreurs détectées par les validateurs côté serveur
[messages_en.properties]
form01.title=Form - Client side validation - locale=
form01.col1=Constraint
form01.col2=Input
form01.col3=Client validation
form01.col4=Server validation
form01.valider=Validate
server.error.message=Errors detected by the validators on the server side
Kehren wir zum Code der Seite zurück:
- Zeile 8: eine Vielzahl von Importen von JavaScript-Bibliotheken, die wir hier ignorieren können;
- Zeile 14: Zeigt die vom Server in die Vorlage eingefügte Lokalisierung an;
- Zeile 59: Zeigt die vom Server in die Vorlage eingefügte Meldung an;
Der Code in den Zeilen 33–44 ist neu. Schauen wir ihn uns an:
<!-- erforderlich -->
<tr>
<td class="col1">required</td>
<td class="col2">
<input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('strNotEmpty')}" th:errors="*{strNotEmpty}" class="error">Donnée erronée</span>
</td>
</tr>
Am einfachsten ist es vielleicht, sich den Code HTML anzusehen, der von diesem Thymeleaf-Segment generiert wird:
<!-- erforderlich -->
<tr>
<td class="col1">required</td>
<td class="col2">
<input type="text" data-val="true" data-val-required="Le champ est obligatoire" id="strNotEmpty" name="strNotEmpty" value="" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
</td>
<td class="col4">
</td>
</tr>
Wir werden auf der Client-Seite eine Validierungsbibliothek namens [jquery.validate] verwenden. Alle Attribute [data-x] sind für diese bestimmt. Wenn die clientseitige Validierung deaktiviert ist, werden diese Attribute nicht verwendet. Daher ist es vorerst nicht notwendig, sie zu verstehen. Wir können uns einfach auf die folgende Thymeleaf-Zeile konzentrieren:
<input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />
die die folgende Zeile HTML generiert:
<input type="text" data-val="true" data-val-required="Le champ est obligatoire" id="strNotEmpty" name="strNotEmpty" value="" />
Hier gibt es eine Schwierigkeit bei der Generierung des Attributs [data-val-required="Le champ est obligatoire"]. Der dem Attribut zugeordnete Wert stammt nämlich aus den externen Meldungsdateien. Man ist daher gezwungen, einen Thymeleaf-Ausdruck zu verwenden, um ihn zu erhalten. Es handelt sich um den folgenden Ausdruck: [th:attr="data-val-required=#{NotNull}"]. Dieser Ausdruck wird ausgewertet und sein Wert unverändert in das generierte Tag HTML eingefügt. Es heißt [th:attr], da es zur Generierung von Attributen verwendet wird, die in Thymeleaf nicht vordefiniert sind. Wir sind auf vordefinierte Attribute wie [th:text, th:value, th:class, ...] gestoßen, aber es gibt kein Attribut [th:data-val-required].
6.2.5. Das Stylesheet
Oben finden sich Klassen wie CSS, beispielsweise [class="field-validation-valid"]. Einige dieser Klassen werden von der JavaScript-Validierungsbibliothek verwendet. Sie sind in der folgenden Datei [form01.css] definiert:
![]() |
@CHARSET "UTF-8";
/*Benutzerdefinierte Stile*/
body {
background-image: url("/images/standard.jpg");
}
.col1 {
background: lightblue;
}
.col2 {
background: Cornsilk;
}
.col3 {
background: AliceBlue;
}
.col4 {
background: Lavender;
}
.error {
color: red;
}
.fieldset{
background: Lavender;
}
/* Stile für Validierungshelfer
-----------------------------------------------------------*/
.field-validation-error {
color: #f00;
}
.field-validation-valid {
display: none;
}
.input-validation-error {
border: 1px solid #f00;
background-color: #Gebühr;
}
.validation-summary-errors {
font-weight: bold;
color: #f00;
}
.validation-summary-valid {
display: none;
}
6.3. Clientseitige Validierung
6.3.1. Grundlagen von jQuery und JavaScript
Die clientseitige Validierung erfolgt mit JavaScript. Wir werden dabei das Framework jQuery nutzen, das zahlreiche Funktionen bereitstellt, die die JavaScript-Entwicklung erleichtern. Wir stellen die Grundlagen von jQuery vor, die Sie kennen sollten, um die Skripte in diesem und den folgenden Kapiteln zu verstehen.
Wir erstellen eine statische Datei HTML [JQuery-01.html], die wir in einem Ordner [static / vues] ablegen:
![]() |
Diese Datei hat folgenden Inhalt:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JQuery-01</title>
<script type="text/javascript" src="/js/jquery-1.11.1.min.js"></script>
</head>
<body>
<h3>Rudiments de JQuery</h3>
<div id="element1">
Elément 1
</div>
</body>
</html>
- Zeile 6: Import von jQuery;
- Zeilen 10–12: ein Element der Seite mit der ID [element1]. Wir werden mit diesem Element experimentieren.
Wir müssen die Datei [jquery-1.11.1.min.js] herunterladen. Die neueste Version von jQuery finden wir unter URL und [http://jquery.com/download/]:

Die heruntergeladene Datei wird im Ordner „[static / js]“ abgelegt:
![]() |
Anschließend rufen wir die statische Ansicht [jQuery-01.html] mit Chrome auf: [1-2]:
![]() |
Rufen Sie in Google Chrome die Seite [Ctrl-Maj-I] auf, um die Entwicklertools [3] anzuzeigen. Über die Registerkarte [Console] [4] können Sie JavaScript-Code ausführen. Im Folgenden geben wir JavaScript-Befehle an, die eingegeben werden müssen, und erläutern diese.
JS | Ergebnis |
|
: Gibt die Sammlung aller Elemente mit der ID [element1] zurück, also normalerweise eine Sammlung mit 0 oder 1 Element, da auf einer Seite keine zwei identischen IDs vorkommen können (HTML). | ![]() |
|
: Weist allen Elementen der Sammlung den Text „[blabla]“ zu. Dies hat zur Folge, dass der auf der Seite angezeigte Inhalt geändert wird | ![]() |
|
blendet die Elemente der Sammlung aus. Der Text [blabla] wird nicht mehr angezeigt. | ![]() |
|
: Zeigt die Sammlung wieder an. So können wir sehen, dass das Element mit der ID [element1] das Attribut CSS style='display: none;' besitzt, wodurch das Element ausgeblendet wird. | |
|
: Zeigt die Elemente der Sammlung an. Der Text [blabla] erscheint wieder. Das Attribut CSS style='display: block;' sorgt für diese Anzeige. | ![]() |
|
: Weist allen Elementen der Sammlung ein Attribut zu. Das Attribut lautet hier [style] und sein Wert [color: red]. Der Text [blabla] wird rot dargestellt. | ![]() |
![]() | |
![]() |
Es ist zu beachten, dass sich der Wert „URL“ des Browsers während all dieser Vorgänge nicht verändert hat. Es fand kein Datenaustausch mit dem Webserver statt. Alles geschieht innerhalb des Browsers. Sehen wir uns nun den Quellcode der Seite an:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JQuery-01</title>
<script type="text/javascript" src="/js/jquery-1.11.1.min.js"></script>
</head>
<body>
<h3>Rudiments de JQuery</h3>
<div id="element1">
Elément 1
</div>
</body>
</html>
Dies ist der ursprüngliche Text. Er spiegelt in keiner Weise die Manipulationen wider, die wir an dem Element in den Zeilen 10–12 vorgenommen haben. Es ist wichtig, sich dies beim Debuggen von JavaScript zu merken. Oft ist es daher sinnlos, den Quellcode der angezeigten Seite einzusehen.
Wir wissen nun genug, um die folgenden Skripte jS zu verstehen.
6.3.2. Die Validierungsbibliotheken jS
Wir werden Bibliotheken aus dem jQuery-Ökosystem verwenden. Um jQuery herum gruppieren sich eine Reihe von Projekten, aus denen wiederum Bibliotheken hervorgehen. Wir werden die von Microsoft erstellte und der Stiftung jQuery zur Verfügung gestellte Validierungsbibliothek [jquery.validate.unobstrusive] verwenden. Wir werden sie im Folgenden als Validierungsbibliothek MS oder einfach als Bibliothek MS bezeichnen. Um sie zu erhalten, benötigt man eine Microsoft Visual Studio-Umgebung. Ich habe keine andere Möglichkeit gefunden, sie zu beschaffen. Man kann eine kostenlose Version vom Typ [Visual Studio Community] oder [http://www.visualstudio.com/en-us/news/vs2013-community-vs.aspx] (Dez. 2014) verwenden. Leser, die kein Interesse daran haben, die folgenden Schritte nachzuvollziehen, können diese Bibliothek und die Bibliotheken, auf denen sie basiert, aus den Beispielen auf der Website dieses Dokuments herunterladen.
Man erstellt ein Konsolenprojekt mit Visual Studio [1-4]:
|
![]() |
- in [5], dem Konsolenprojekt;
- in [6-7]: Wir fügen dem Projekt die Pakete [NuGet] hinzu. [NuGet] ist eine Funktion von Visual Studio, mit der Bibliotheken im Format DLL, aber auch jS-Bibliotheken heruntergeladen werden können.
![]() |
- in [9-10]: Führen Sie eine Suche mit dem Stichwort [jQuery] durch;
- in [11-13]: Laden Sie die für die clientseitige Validierung erforderlichen Bibliotheken jS in der angegebenen Reihenfolge herunter;
- Bei [14] laden Sie bitte auch die Bibliothek [Microsoft jQuery Unobtrusive Ajax] herunter, die wir in Kürze verwenden werden;
![]() |
- Suchen Sie in [15-16] nach Paketen mit dem Schlüsselwort [globalize];
- für [17] laden Sie bitte die Bibliothek [jQuery.Validation.Globalize] herunter;
![]() |
Durch diese verschiedenen Downloads wurden eine Reihe von Bibliotheken mit der Bezeichnung jS im Ordner [Scripts] des Projekts [18] installiert. Nicht alle davon sind nützlich. Jede Datei liegt in zwei Exemplaren vor:
- [js]: die lesbare Version der Bibliothek;
- [min.js]: die unlesbare, sogenannte „minifizierte“ Version der Bibliothek. Sie ist nicht wirklich unlesbar. Es handelt sich um Text. Aber sie ist nicht verständlich. Dies ist die Version, die in der Produktion verwendet werden sollte, da diese Datei kleiner ist als die entsprechende Version [js] und somit die Geschwindigkeit des Datenaustauschs zwischen Client und Server verbessert;
Die Versionen [min.map] sind nicht unbedingt erforderlich. Im Ordner [cultures] können nur die von der Anwendung verwalteten Kulturen beibehalten werden.
Mit dem Windows-Explorer kopiert man diese Dateien in den Ordner „[static / js / jquery]“ des Projekts „[springmvc-validation-client]“ und behält nur die nützlichen Dateien „[20]“ bei:
![]() |
In [21] werden nur zwei Sprachversionen beibehalten:
- [fr-FR]: Französisch (Frankreich);
- [en-US]: das Englische aus USA;
6.3.3. Import der Validierungsbibliotheken jS
Um genutzt werden zu können, müssen diese Bibliotheken über die Ansicht [vue-01.xml] importiert werden:
<head>
<title>Spring 4 MVC</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="/css/form01.css" />
<script type="text/javascript" src="/js/jquery/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="/js/jquery/jquery.validate.min.js"></script>
<script type="text/javascript" src="/js/jquery/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript" src="/js/jquery/globalize/globalize.js"></script>
<script type="text/javascript" src="/js/jquery/globalize/cultures/globalize.culture.fr-FR.js"></script>
<script type="text/javascript" src="/js/jquery/globalize/cultures/globalize.culture.en-US.js"></script>
<script type="text/javascript" src="/js/client-validation.js"></script>
<script type="text/javascript" src="/js/local.js"></script>
<script th:inline="javascript">
/*<![CDATA[*/
var culture = [[${locale}]];
Globalize.culture(culture);
/*]]>*/
</script>
</head>
- Zeile 11: Import einer Datei jS, die wir bisher noch nicht erwähnt haben;
- Zeilen 13–18: ein von Thymelaf interpretiertes Skript jS. Es verwaltet die clientseitige Locale;
6.3.4. Verwaltung der Ländereinstellung auf der Client-Seite
Die clientseitige Lokalisierung erfolgt durch das folgende Skript jS:
<script th:inline="javascript">
/*<![CDATA[*/
var culture = [[${locale}]];
Globalize.culture(culture);
/*]]>*/
</script>
- Zeilen 3–4: Code jS, in dem der Thymeleaf-Ausdruck [[${locale}]] vorkommt. Beachten Sie die besondere Syntax dieses Ausdrucks. Dies liegt daran, dass er in JavaScript geschrieben ist. Der Ausdruck [[${locale}]] wird durch den Wert des Schlüssels [locale] aus dem View-Modell ersetzt;
Das Ergebnis im aus diesen Zeilen generierten Datenfluss HTML lautet wie folgt:
<script>
/*<![CDATA[*/
var culture = 'en-US';
Globalize.culture(culture);
/*]]>*/
</script>
Die Zeilen 3–4 legen die clientseitige Kultur fest. Es werden nur zwei verwaltet: [fr-FR] und [en-US]. Aus diesem Grund haben wir nur zwei Kulturdateien importiert:
<script type="text/javascript" src="/js/jquery/globalize/cultures/globalize.culture.fr-FR.js"></script>
<script type="text/javascript" src="/js/jquery/globalize/cultures/globalize.culture.en-US.js"></script>
Die auf der Client-Seite zu verwendende Kultur wird auf der Server-Seite festgelegt. Kehren wir zum Code auf der Server-Seite zurück:
@RequestMapping(value = "/js01", method = RequestMethod.GET, produces = "text/html; charset=UTF-8")
public String js01(Form01 formulaire, Locale locale, Model model) {
setModel(formulaire, model, locale, null);
return "vue-01";
}
// Vorbereitung der Vorlage für die Ansicht „vue-01“
private void setModel(Form01 formulaire, Model model, Locale locale, String message) {
// Es werden nur die Sprachvarianten fr-FR und en-US unterstützt
String language = locale.getLanguage();
String country = null;
if (language.equals("fr")) {
country = "FR";
formulaire.setLang("fr_FR");
}
if (language.equals("en")) {
country = "US";
formulaire.setLang("en_US");
}
model.addAttribute("locale", String.format("%s-%s", language, country));
...
}
- Zeile 20: Die Locale [fr-FR] oder [en-US] wird in die Vorlage der Ansicht [vue-01.xml] (Zeile 4) eingefügt. Hier ist eine Quelle für Komplikationen zu beachten. Während eine französische Locale auf der Client-Seite als [fr-FR] bezeichnet wird, lautet sie auf der Server-Seite [fr_FR]. Aus diesem Grund wird sie in den Zeilen 14 und 18 in dieser Form im Objekt [Form01 formulaire] gespeichert, das die übermittelten Werte empfängt;
Es ist folgender wichtiger Punkt zu beachten: Das Skript
<script>
/*<![CDATA[*/
var culture = 'en-US';
Globalize.culture(culture);
/*]]>*/
</script>
ändert die Kultur des Clients anhand der vom Server übermittelten Ländereinstellung. Dadurch werden die auf der Seite angezeigten Meldungen nicht internationalisiert. Es ändert lediglich die Art und Weise, wie bestimmte Informationen interpretiert werden, die von der Kultur eines Landes abhängen. Mit der Kultur [fr_FR] ist die tatsächliche Zahl [12,78] gültig, während sie mit der Kultur [en-US] ungültig ist. Man muss daher [12.78] schreiben. Ebenso ist das Datum [12/01/2014] in der Kultur [fr-FR] gültig, während man in der Kultur [en-US] [01/12/2014] schreiben muss. Die Dateien im Ordner [jquery / globalize] behandeln diese Art von Problemen:
![]() |
Die Internationalisierung von Fehlermeldungen wird ausschließlich serverseitig verwaltet. Wir werden sehen, dass die Seite HTML / jS Fehlermeldungen enthält, die der vom Server verwalteten Ländereinstellung entsprechen: auf Französisch für die Locale [fr_FR] und auf Englisch für die Locale [en_US].
6.3.5. Die Meldungsdateien
Die Ansicht [vue-01.xml] verwendet die folgenden internationalisierten Meldungen:
![]() |
[messages_fr.properties]
NotNull=Le champ est obligatoire
NotEmpty=La donnée ne peut être vide
NotBlank=La donnée ne peut être vide
typeMismatch=Format invalide
Future.form01.dateInFuture=La date doit être postérieure ou égale à celle d''aujourd'hui
Past.form01.dateInPast=La date doit être antérieure ou égale à celle d''aujourd'hui
Min.form01.intMin10=La valeur doit être supérieure ou égale à 10
Max.form01.intMax100=La valeur doit être inférieure ou égale à 100
Size.form01.strBetween4and6=La chaîne doit avoir entre 4 et 6 caractères
Length.form01.str4=La chaîne doit avoir quatre caractères exactement
Email.form01.email=Adresse mail invalide
URL.form01.url=URL invalide
Range.form01.int1014=La valeur doit être dans l''intervalle [10,14]
AssertTrue=Seule la valeur True est acceptée
AssertFalse=Seule la valeur False est acceptée
Pattern.form01.hhmmss=Tapez l''heure sous la forme hh:mm:ss
form01.hhmmss.pattern=^\\d{2}:\\d{2}:\\d{2}$
DateInvalide.form01=Date invalide
form01.str4.pattern=^.{4,4}$
form01.int1014.max=14
form01.int1014.min=10
form01.strBetween4and6.pattern=^.{4,6}$
form01.intMax100.value=100
form01.intMin10.value=10
form01.double1.min=2.3
form01.double1.max=3.4
Range.form01.double1=La valeur doit être dans l'intervalle [2,3-3,4]
form01.title=Formulaire - Validations côté client - locale=
form01.col1=Contrainte
form01.col2=Saisie
form01.col3=Validation client
form01.col4=Validation serveur
form01.valider=Valider
form01.double2=[double2+double1] doit être dans l''intervalle [{0},{1}]
form01.double3=[double3+double1] doit être dans l''intervalle [{0},{1}]
locale.fr=Français
locale.en=English
client.validation.true=Activer la validation client
client.validation.false=Inhiber la validation client
DecimalMin.form01.double1=Le nombre doit être supérieur ou égal à 2,3
DecimalMax.form01.double1=Le nombre doit être inférieur ou égal à 3,4
server.error.message=Erreurs détectées par les validateurs côté serveur
[messages_en.properties]
NotNull=Field is required
NotEmpty=Field can''t be empty
NotBlank=Field can''t be empty
typeMismatch=Invalid format
Future.form01.dateInFuture=Date must be greater or equal to today''s date
Past.form01.dateInPast=Date must be lower or equal today''s date
Min.form01.intMin10=Value must be higher or equal to 10
Max.form01.intMax100=Value must be lower or equal to 100
Size.form01.strBetween4and6=String must have between 4 and 6 characters
Length.form01.str4=String must be exactly 4 characters long
Email.form01.email=Invalid mail address
URL.form01.url=Invalid URL
Range.form01.int1014=Value must be in [10,14]
AssertTrue=Only value True is allowed
AssertFalse=Only value False is allowed
Pattern.form01.hhmmss=Time must follow the format hh:mm:ss
form01.hhmmss.pattern=^\\d{2}:\\d{2}:\\d{2}$
DateInvalide.form01=Invalid Date
form01.str4.pattern=^.{4,4}$
form01.int1014.max=14
form01.int1014.min=10
form01.strBetween4and6.pattern=^.{4,6}$
form01.intMax100.value=100
form01.intMin10.value=10
form01.double1.min=2.3
form01.double1.max=3.4
Range.form01.double1=Value must be in [2.3,3.4]
form01.title=Form - Client side validation - locale=
form01.col1=Constraint
form01.col2=Input
form01.col3=Client validation
form01.col4=Server validation
form01.valider=Validate
form01.double2=[double2+double1] must be in [{0},{1}]
form01.double3=[double3+double1] must be in [{0},{1}]
locale.fr=Français
locale.en=English
client.validation.true=Activate client validation
client.validation.false=Inhibate client validation
DecimalMin.form01.double1=Value must be greater or equal to 2.3
DecimalMax.form01.double1=Value must be lower or equal to 3.4
server.error.message=Errors detected by the validators on the server side
Die Datei „[messages.properties]“ ist eine Kopie der Datei mit den englischen Meldungen. Letztendlich werden alle Lokalisierungen, die sich von „[fr]“ unterscheiden, englische Meldungen verwenden. Zur Erinnerung: Die Datei [messages_fr.properties] wird für alle Lokalisierungen von [fr_XX] verwendet, wie beispielsweise [fr_CA] oder [fr_FR].
Die Ansicht [vue-01.xml] verwendet die Schlüssel dieser Meldungen. Wenn der Leser den diesen Schlüsseln zugeordneten Wert erfahren möchte, wird er gebeten, zu diesem Absatz zurückzukehren, um ihn zu erfahren.
6.3.6. Änderung der Ländereinstellung
Die Ansicht [vue-01.xml] enthält vier Links:
<body>
<!-- Titel -->
<h3>
<span th:text="#{form01.title}"></span>
<span th:text="${locale}"></span>
</h3>
<!-- Menü -->
<p>
<a id="locale_fr" href="javascript:setLocale('fr_FR')">
<span th:text="#{locale.fr}"></span>
</a>
<a id="locale_en" href="javascript:setLocale('en_US')">
<span style="margin-left:30px" th:text="#{locale.en}"></span>
</a>
<a id="clientValidationTrue" href="javascript:setClientValidation(true)">
<span style="margin-left:30px" th:text="#{client.validation.true}"></span>
</a>
<a id="clientValidationFalse" href="javascript:setClientValidation(false)">
<span style="margin-left:30px" th:text="#{client.validation.false}"></span>
</a>
</p>
<!-- Formular -->
<form action="/someURL" th:action="@{/js02.html}" method="post" th:object="${form01}" name="form" id="form">
...
von denen einige unten dargestellt sind: [1]:
![]() |
Sehen wir uns die beiden Links an, mit denen die Sprache auf Französisch oder Englisch umgestellt werden kann:
<a id="locale_fr" href="javascript:setLocale('fr_FR')">
<span th:text="#{locale.fr}"></span>
</a>
<a id="locale_en" href="javascript:setLocale('en_US')">
<span style="margin-left:30px" th:text="#{locale.en}"></span>
</a>
Ein Klick auf diese Links löst die Ausführung eines Skripts jS aus, das in der Datei [local.js] [2] enthalten ist. In beiden Fällen wird eine Funktion namens jS [setLocale] aufgerufen:
// Ländereinstellung
function setLocale(locale) {
// Die Lokalisierung wird aktualisiert
lang.val(locale);
// Das Formular wird übermittelt – dies löst die Validatoren auf der Client-Seite nicht aus – deshalb wurde die clientseitige Validierung nicht deaktiviert
document.form.submit();
}
Zum Verständnis von Zeile 4 ist eine Vorbemerkung erforderlich. Die Ansicht [vue-01.xml] enthält ein verstecktes Feld mit dem Namen [lang]:
<input type="hidden" th:field="*{lang}" th:value="*{lang}" value="true" />
das einem Feld [lang] in [Form01] entspricht:
// Lokale
private String lang;
Versteckte Felder sind praktisch, wenn man die übermittelten Werte ergänzen möchte. Mit JavaScript kann man ihnen einen Wert zuweisen, und dieser Wert wird wie eine normale Benutzereingabe übermittelt. Der von Thymeleaf generierte Code HTML lautet wie folgt:
<input type="hidden" value="en_US" id="lang" name="lang" />
Der Wert des Parameters [value] entspricht dem Wert des Feldes [Form01.lang] zum Zeitpunkt der Generierung von HTML. Wichtig ist dabei die Kennung jS des Knotens [id="lang"]. Diese Kennung wird von der folgenden Funktion [] verwendet:
// globale Variablen
var lang;
// Dokument bereit
$(document).ready(function() {
// globale Referenzen
lang = $("#lang");
});
// Lokale
function setLocale(locale) {
// Die lokale Variable wird aktualisiert
lang.val(locale);
// Das Formular wird übermittelt – aus unbekannten Gründen werden die Validatoren auf der Client-Seite nicht ausgelöst
// deshalb wurde die Validierung nicht deaktiviert
document.form.submit();
}
- Zeilen 5–8: Die Funktion jS [$(document).ready(f)] wird ausgeführt, sobald der Browser das gesamte vom Server gesendete Dokument geladen hat. Ihr Parameter ist eine Funktion. Die Funktion jS [$(document).ready(f)] wird verwendet, um die Umgebung jS des geladenen Dokuments zu initialisieren;
- Zeile 7: Der Ausdruck [$("#lang")] ist ein Ausdruck jQuery. Sein Wert ist eine Referenz auf den Knoten DOM des Attributs [id='lang'];
- Zeile 2: Variablen, die außerhalb einer Funktion deklariert werden, sind für alle Funktionen global gültig. Das bedeutet in diesem Fall, dass die in [$(document).ready()] initialisierte Variable [lang] auch in der Funktion [setLocale] in Zeile 11 bekannt ist;
- Zeile 13: Ändert das Attribut [value] des durch [lang] identifizierten Knotens. Wenn lang den Wert [xx_XX] hat, dann lautet das Tag HTML des Knotens:
<input type="hidden" value="xx_XX" id="lang" name="lang" />
Mit JavaScript lässt sich der Wert der Elemente von DOM (Document Object Model) ändern.
- Zeile 16: [document] bezeichnet das DOM. [document.form] bezeichnet das erste in diesem Dokument gefundene Formular. Ein HTML-Dokument kann mehrere <form>-Tags und somit mehrere Formulare enthalten. Hier haben wir nur eines. [document.form.submit] sendet dieses Formular so ab, als hätte der Benutzer auf eine Schaltfläche mit dem Attribut [type='submit'] geklickt. An welche Aktion werden die Werte des Formulars gesendet? Um dies herauszufinden, muss man sich das Tag [form] des Formulars in [vue-01.xml] ansehen:
<!-- Formular -->
<form action="/someURL" th:action="@{/js02.html}" method="post" th:object="${form01}" name="form" id="form">
Die Aktion, die die übermittelten Werte empfängt, ist diejenige, die durch das Attribut [th:action] bezeichnet wird. Es handelt sich also um die Aktion [/js02.html]. Es sei daran erinnert, dass in diesem Namen das Suffix [.html] entfernt wird und letztendlich die Aktion [/js02] ausgeführt wird. Wichtig zu verstehen ist, dass der neue Wert [xx_XX] des Knotens [lang] in der Form [lang=xx_XX] übermittelt wird. Wir haben unsere Anwendung jedoch so konfiguriert, dass sie den Parameter [lang] abfängt und als Änderung der Ländereinstellung interpretiert. Auf der Serverseite wird die Ländereinstellung daher zu [xx_XX]. Sehen wir uns die Aktion [/js02] an, die ausgeführt wird:
@RequestMapping(value = "/js02", method = RequestMethod.POST, produces = "text/html; charset=UTF-8")
public String js02(@Valid Form01 formulaire, BindingResult result, RedirectAttributes redirectAttributes, Locale locale, Model model) {
Form01Validator validator = new Form01Validator(10, 13);
validator.validate(formulaire, result);
if (result.hasErrors()) {
StringBuffer buffer = new StringBuffer();
for (ObjectError error : result.getAllErrors()) {
buffer.append(String.format("[name=%s,code=%s,message=%s]", error.getObjectName(), error.getCode(),
error.getDefaultMessage()));
}
setModel(formulaire, model, locale, buffer.toString());
return "vue-01";
} else {
redirectAttributes.addFlashAttribute("form01", formulaire);
return "redirect:/js01.html";
}
}
// Vorbereitung der Vorlage für die Ansicht „vue-01“
private void setModel(Form01 formulaire, Model model, Locale locale, String message) {
// Es werden nur die Sprachvarianten fr-FR und en-US unterstützt
String language = locale.getLanguage();
String country = null;
if (language.equals("fr")) {
country = "FR";
formulaire.setLang("fr_FR");
}
if (language.equals("en")) {
country = "US";
formulaire.setLang("en_US");
}
model.addAttribute("locale", String.format("%s-%s", language, country));
...
}
- Zeile 2: Die Aktion [/js02] erhält die neue Locale [xx_XX], die im Parameter [Locale locale] gekapselt ist:
- Zeilen 5–12: Sind einige der übermittelten Werte ungültig, wird die Ansicht [vue-01.xml] mit Fehlermeldungen angezeigt, die die neue Lokale [xx_XX] verwenden. Außerdem sorgt Zeile 11 dafür, dass die Variable [locale=xx-XX] in die Vorlage eingefügt wird. Auf der Client-Seite wird dieser Wert verwendet, um die clientseitige Locale zu aktualisieren. Wir haben diesen Vorgang bereits beschrieben;
- Zeilen 14–15: Sind alle übermittelten Werte gültig, erfolgt eine Weiterleitung zur folgenden Aktion [/js01]:
@RequestMapping(value = "/js01", method = RequestMethod.GET, produces = "text/html; charset=UTF-8")
public String js01(Form01 formulaire, Locale locale, Model model) {
setModel(formulaire, model, locale, null);
return "vue-01";
}
- In Zeile 2 wird die neue Locale [xx_XX] eingefügt;
- Zeile 3: Die Methode [setModel] setzt daraufhin die Kultur des Clients auf [xx-XX];
Betrachten wir nun den Einfluss der Locale in der Ansicht [vue-01.xml]. Bislang haben wir diese noch nicht vollständig dargestellt, da sie mehr als 300 Zeilen umfasst. Der Großteil der Zeilen besteht jedoch aus der Wiederholung einer Sequenz, die der folgenden ähnelt:
<!-- erforderlich -->
<tr>
<td class="col1">required</td>
<td class="col2">
<input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('strNotEmpty')}" th:errors="*{strNotEmpty}" class="error">Donnée erronée</span>
</td>
</tr>
Dieser Code zeigt das folgende Fragment „[1]“ an:
![]() |
Die Fehlermeldung [2] stammt aus dem Attribut [th:attr="data-val-required=#{NotNull}"] in Zeile 5. [#{NotNull}] ist eine lokalisierte Meldung. Je nach serverseitiger Ländereinstellung generiert Zeile 5 das Tag:
<input type="text" data-val="true" data-val-required="Field is required" id="strNotEmpty" name="strNotEmpty" />
oder das Tag:
<input type="text" data-val="true" data-val-required="Le champ est obligatoire" id="strNotEmpty" name="strNotEmpty" />
Die Attribute von [data-x] werden von der Validierungsbibliothek jS verarbeitet.
Zusammenfassend lässt sich festhalten, dass die beiden Links zum Ändern der Ländereinstellung:
- eine Überprüfung der eingegebenen Werte durch POST auslösen;
- die Ländereinstellung sowohl serverseitig als auch clientseitig ändern;
- eine Seite HTML generieren, die die für die Validierungsbibliothek jS bestimmten Fehlermeldungen enthält, und dass diese Meldungen in der Sprache der gewählten Ländereinstellung vorliegen;
6.3.7. Der POST der eingegebenen Werte
Betrachten wir die Schaltfläche [Valider], die die in der Ansicht [vue-01.xml] eingegebenen Werte übermittelt. Ihr Code HTML lautet wie folgt:
<!-- Bestätigungsschaltfläche -->
<input type="submit" value="Valider" onclick="javascript:postForm01()" />
Wenn JavaScript im Browser aktiviert ist, löst ein Klick auf die Schaltfläche die Ausführung der Methode [postForm01] aus. Wenn diese Funktion den booleschen Wert [False] zurückgibt, wird submit nicht ausgeführt. Gibt sie einen anderen Wert zurück, wird sie ausgeführt. Diese Funktion befindet sich in der Datei [local.js]:
![]() |
Sie wird von der Ansicht [vue-01.xml] über die folgende Zeile 6 importiert:
<head>
<title>Spring 4 MVC</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="/css/form01.css" />
...
<script type="text/javascript" src="/js/local.js"></script>
</head>
In dieser Datei findet sich der folgende Code:
// globale Variablen
var formulaire;
var clientValidation;
var double1;
var double2;
var double3;
...
$(document).ready(function() {
// globale Referenzen
formulaire = $("#form");
clientValidation = $("#clientValidation");
double1 = $("#double1");
double2 = $("#double2");
double3 = $("#double3");
...
});
....
// Formular-Post
function postForm01() {
...
}
- Zeilen 8–16: Die Funktion jS [$(document).ready(f)] wird ausgeführt, sobald der Browser das gesamte vom Server gesendete Dokument geladen hat. Ihr Parameter ist eine Funktion. Die Funktion jS [$(document).ready(f)] wird verwendet, um die Umgebung jS des geladenen Dokuments zu initialisieren;
- Zeilen 10–14: Um diese Zeilen zu verstehen, muss man sowohl den Thymeleaf-Code als auch den generierten HTML-Code betrachten;
Der betreffende Thymeleaf-Code lautet wie folgt:
<form action="/someURL" th:action="@{/js02.html}" method="post" th:object="${form01}" name="form" id="form">
...
<input type="text" th:field="*{double1}" th:value="*{double1}" ... />
...
<input type="text" th:field="*{double2}" th:value="*{double2}" />
...
<input type="text" th:field="*{double3}" th:value="*{double3}" ... />
...
<input type="hidden" th:field="*{clientValidation}" th:value="*{clientValidation}" value="true" />
der den folgenden HTML-Code generiert:
<form action="/js02.html" method="post" name="form" id="form">
...
<input type="text" id="double1" name="double1" .../>
....
<input type="text" value="" id="double2" name="double2" />
...
<input value="" id="double3" name="double3" .../>
...
<input type="hidden" value="false" id="clientValidation" name="clientValidation" />
Jedes Attribut [th:field='x'] generiert zwei Attribute: HTML, [name='x'] und [id='x']. Das Attribut [name] ist der Name der übermittelten Werte. Das Vorhandensein der Attribute [name='x'] und [value='y'] für ein Tag HTML <input type='text'> führt dazu, dass die Zeichenfolge x=y in die übermittelten Werte name1=val1&name2=val2&... eingefügt wird. Das Attribut [id='x'] wird hingegen von JavaScript verwendet. Es dient dazu, ein Element des DOM (Document Object Model) zu identifizieren. Das geladene Dokument HTML wird nämlich in einen JavaScript-Baum namens DOM umgewandelt, in dem jeder Knoten durch sein Attribut [id] gekennzeichnet ist.
Kehren wir zum Code der Funktion [$(document).ready()] zurück:
// globale Variablen
var formulaire;
var clientValidation;
var double1;
var double2;
var double3;
...
$(document).ready(function() {
// globale Referenzen
formulaire = $("#form");
clientValidation = $("#clientValidation");
double1 = $("#double1");
double2 = $("#double2");
double3 = $("#double3");
...
});
....
// Formular-Post
function postForm01() {
...
}
- Zeile 10: Der Ausdruck [$("#form")] ist ein Ausdruck jQuery. Sein Wert ist eine Referenz auf den Knoten DOM mit dem Attribut [id='form '];
- Zeilen 10–14: Es werden die Verweise auf fünf Knoten von DOM abgerufen;
- Zeilen 2–6: Variablen, die außerhalb einer Funktion deklariert werden, sind für alle Funktionen global gültig. Das bedeutet in diesem Fall, dass die in [$(document).ready()] initialisierten Variablen [formulaire, clientValidation , double1, double2, double3] auch in der Funktion [postForm01] in Zeile 19 verfügbar sind;
Betrachten wir nun die Funktion [postForm01]:
// Formularabsendung
function postForm01() {
// clientseitige Validierung
var validationActive = clientValidation.val() === "true";
if (validationActive) {
// Fehler werden vom Server gelöscht
clearServerErrors();
// Formularvalidierung
if (!formulaire.validate().form()) {
// kein Absenden
return false;
}
}
// Werte im angelsächsischen Format
var value1 = double1.val().replace(",", ".");
double1.val(value1);
var value2 = double2.val().replace(",", ".");
double2.val(value2);
var value3 = double3.val().replace(",", ".");
double3.val(value3);
// Das Absenden wird zugelassen
return true;
}
Zur Erinnerung: Diese Funktion jS wird vor der Funktion [submit] des Formulars ausgeführt. Wenn sie den booleschen Wert [false] (Zeile 11) zurückgibt, findet das Absenden nicht statt. Wenn sie etwas anderes zurückgibt (Zeile 22), findet es statt.
- Der wichtige Code befindet sich in den Zeilen 4–12;
- Zeile 4: Der Wert des versteckten Felds [clientValidation] wird abgerufen. Dieser Wert ist „true“, wenn die clientseitige Validierung aktiviert werden soll, andernfalls „false“;
- Zeile 6: Im Falle einer clientseitigen Validierung werden die möglicherweise vorhandenen Fehlermeldungen des Servers gelöscht, da der Benutzer gerade die Ländereinstellung geändert hat;
- Zeile 9: Zur Erinnerung: Die Variable [formulaire] repräsentiert den Knoten des Tags HTML <form>, also das Formular. Dieses enthält Validatoren jS, die wir noch nicht vorgestellt haben und die Gegenstand der folgenden Absätze sein werden. Der Ausdruck [formulaire.validate().form()] erzwingt die Ausführung aller im Formular vorhandenen Validatoren jS. Sein Wert ist [true], wenn alle geprüften Werte gültig sind, andernfalls [false];
- Zeile 11: Der Wert [false] wird zurückgegeben, wenn mindestens einer der geprüften Werte ungültig ist. Dies verhindert, dass der [submit] vom Formular an den Server gesendet wird;
- Zeilen 15–20: Die Kennungen [double1, double2, double3] stehen für die drei tatsächlichen Zahlen aus dem Formular. Je nach Kultur ist der eingegebene Wert unterschiedlich. Bei der Kultur „[fr-FR]“ wird „[10,37]“ eingegeben, während bei der Kultur „[en-US]“ „[10.37]“ eingegeben wird. Das gilt für die Eingabe. Bei der Kultur „[fr-FR]“ sieht der für „[double1]“ gesendete Wert beispielsweise wie „[double1=10,37]“ aus. Auf der Serverseite wird der Wert [10,37] abgelehnt, da dort [10.37] erwartet wird, das Standardformat für reelle Zahlen in Java. Daher wird in den Zeilen 15–20 im eingegebenen Wert für diese Zahlen das Komma durch einen Punkt ersetzt;
- Zeile 15: Der Ausdruck [double1.val()] gibt die für den Knoten [double1] eingegebene Zeichenkette zurück. Der Ausdruck [double1.val().replace(",", ".")] ersetzt in dieser Zeichenkette die Kommas durch Punkte. Das Ergebnis ist die Zeichenkette [value1];
- Zeile 16: Die Anweisung [double1.val(value1)] weist dem Knoten [double1] diesen Wert [value1] zu.
Technisch gesehen gilt: Wenn der Benutzer „[10,37]“ anstelle des tatsächlichen Werts „[double1]“ eingegeben hat, hat der Knoten [double1] nach den vorangegangenen Anweisungen den Wert [10.37], und der Wert, der übermittelt wird, ist [param1=val1&double1=10.37¶m2=val2] – ein Wert, der vom Server akzeptiert wird;
- Zeile 22: Wir geben den Wert [true] zurück, damit der Knoten [submit] des Formulars ausgeführt wird;
Es ist zu beachten, dass die Funktion jS [postForm01]:
- alle Validatoren jS des Formulars ausführt, wenn die clientseitige Validierung aktiviert ist, und die Übermittlung von [submit] des Formulars an den Server verhindert, wenn einer der eingegebenen Werte als ungültig deklariert wurde;
- lässt die Ausführung von [submit] zu, entweder weil die clientseitige Validierung nicht aktiviert ist oder weil sie aktiviert ist und alle eingegebenen Werte gültig sind;
Bleibt noch die Anweisung in Zeile [3]:
// Serverfehler werden gelöscht
clearServerErrors();
Die Funktion [clearServerErrors] dient dazu, die Meldungen in Spalte 4 der Ansicht [vue-01.xml] zu löschen:
![]() |
Im obigen Screenshot wurde auf den Link [English] geklickt. Wir haben gesehen, dass dies zu einem POST der eingegebenen Werte führte, ohne dass die Validatoren jS ausgelöst wurden. Nach der Rückgabe des POST werden in der Spalte [Server Validation] eventuelle Fehlermeldungen angezeigt. Klickt man nun auf die Schaltfläche [Validate] [2] bei aktivierten Validatoren jS [3], dann füllt sich die Spalte [Client Validation] [4] mit Meldungen. Wenn nichts unternommen wird, bleiben die Einträge in der Spalte [Server Validation] erhalten, was zu Verwirrung führt, da bei Fehlern, die von den Validatoren jS erkannt werden, der Server nicht angesprochen wird. Um dies zu vermeiden, wird die Spalte [Server Validation] in der Funktion [postForm01] gelöscht. Diese Aufgabe übernimmt die Funktion []:
function clearServerErrors() {
// Fehlermeldungen vom Server werden gelöscht
$(".error").each(function(index) {
$(this).text("");
});
}
Eine Besonderheit der Fehlermeldungen ist, dass sie alle die Klasse [error] haben. Zum Beispiel für die erste Zeile der Tabelle in [vue-01.html]:
<span th:if="${#fields.hasErrors('strNotEmpty')}" th:errors="*{strNotEmpty}" class="error">Donnée erronée</span>
Und das sind die einzigen Knoten des DOM, die diese Klasse haben. Wir verwenden diese Eigenschaft in der Funktion [clearServerErrors]:
function clearServerErrors() {
// Fehlermeldungen vom Server werden gelöscht
$(".error").each(function(index) {
$(this).text("");
});
}
- Zeile 3: Der Ausdruck [$(".error")] liefert die Sammlung der Knoten des DOM, die die Klasse [error] haben;
- Zeile 3: Der Ausdruck [$(".error").each(function(index){f}] führt für jeden Knoten der Sammlung die Funktion [f] aus. Er erhält einen Parameter [index], der hier nicht verwendet wird und die Nummer des Knotens in der Sammlung angibt;
- Zeile 4: Der Ausdruck [$(this)] bezeichnet den aktuellen Knoten in der Iteration. Dieser ist ein Tag HTML <span>. Der Ausdruck [$(this).text("")] weist dem vom Tag <span> angezeigten Text die leere Zeichenkette zu;
Wir werden nun verschiedene Validatoren jS betrachten.
6.3.8. Validator [required]
Betrachten wir das erste Element des Formulars:
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich -->
<tr>
<td class="col1">required</td>
<td class="col2">
<input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('strNotEmpty')}" th:errors="*{strNotEmpty}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [strNotEmpty] des Formulars [Form01]:
@NotNull
@NotBlank
private String strNotEmpty;
Die Einschränkungen [1-2] legen fest, dass das Feld [strNotEmpty] eine existierende Zeichenfolge [NotNull] sein muss, die nicht leer ist und nicht ausschließlich aus Leerzeichen [NotBlank] besteht. Wir möchten diese Einschränkung auf der Client-Seite mit JavaScript nachbilden.
Betrachten wir die Zeilen 5 und 8. Zeile 11 stellt kein Problem dar. Sie zeigt die Fehlermeldung zum Feld [strNotEmpty] an. Beginnen wir mit Zeile 5:
<input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />
Ausgehend von diesem Code generiert Thymeleaf das folgende Tag:
<input type="text" data-val="true" data-val-required="Field is required" id="strNotEmpty" name="strNotEmpty" value="x" />
- Das Attribut [data-val='true'] wird von den Validierungsbibliotheken jQuery verwendet. Sein Vorhandensein zeigt an, dass der Wert des Knotens validiert wird;
- Das Attribut [data-val-X='msg'] liefert zwei Informationen. [X] ist der Name des Validators, [msg] ist die Fehlermeldung, die mit einem ungültigen Wert des Knotens verbunden ist, auf den der Validator angewendet wird. Dies ist lediglich eine Information. Es führt nicht zur Anzeige der Fehlermeldung;
- [required] ist ein Validator, der von der Validierungsbibliothek [jquery.validate.unobstrusive] von Microsoft erkannt wird. Er muss nicht definiert werden. Dies wird im weiteren Verlauf nicht immer der Fall sein;
- Die Tags [data-x] werden von HTML5 ignoriert. Sie sind nur dann nützlich, wenn JavaScript vorhanden ist, um sie zu verwerten;
Betrachten wir nun Zeile 8:
<span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
Sie dient dazu, die Fehlermeldung des Validators [required] anzuzeigen. Liegt ein Fehler vor, ersetzt die Validierungsbibliothek jS die Zeile HTML der Tabelle dynamisch durch den folgenden Code:
<tr>
<td class="col1">required</td>
<td class="col2">
<input type="text" data-val="true" data-val-required="Le champ est obligatoire" id="strNotEmpty" name="strNotEmpty" value="" aria-required="true" aria-invalid="true" aria-describedby="strNotEmpty-error" class="input-validation-error">
</td>
<td class="col3">
<span class="field-validation-error" data-valmsg-for="strNotEmpty" data-valmsg-replace="true">
<span id="strNotEmpty-error" class="">Le champ est obligatoire</span>
</span>
</td>
<td class="col4">
<span class="error"></span>
</td>
</tr>
</tr>
- Zeile 4: Die Klasse des Knotens [strNotEmpty] hat sich geändert. Sie lautet nun [input-validation-error], wodurch das fehlerhafte Feld rot markiert wird;
- Zeile 7: Die Klasse des Knotens [span] hat sich geändert. Sie lautet nun [field-validation-error], wodurch der Text des Knotens [span] rot angezeigt wird;
- Zeile 8: Das [span], das zuvor leer war, enthält nun den Text [Le champ est obligatoire]. Dieser Text stammt aus dem Tag [data-val-required="Le champ est obligatoire"] in Zeile 4;
- Zeile 7: Um die Fehlermeldung des Knotens [strNotEmpty] aus Zeile 4 anzuzeigen, müssen in Zeile 7 die Attribute [data-valmsg-for="strNotEmpty"] und [data-valmsg-replace="true"] verwendet werden;
6.3.9. Validator [assertfalse]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich, assertfalse -->
<tr>
<td class="col1">required, assertfalse</td>
<td class="col2">
<input type="radio" th:field="*{assertFalse}" value="true" data-val="true"
th:attr="data-val-required=#{NotNull},data-val-assertfalse=#{AssertFalse}" />
<label th:for="${#ids.prev('assertFalse')}">true</label>
<input type="radio" th:field="*{assertFalse}" value="false" data-val="true"
th:attr="data-val-required=#{NotNull},data-val-assertfalse=#{AssertFalse}" />
<label th:for="${#ids.prev('assertFalse')}">false</label>
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="assertFalse" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('assertFalse')}" th:errors="*{assertFalse}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [assertFalse] des Formulars [Form01]:
@NotNull
@AssertFalse
private Boolean assertFalse;
Diese Einschränkung soll auf der Client-Seite mit JavaScript nachgebildet werden. Die Zeilen 12–17 sind mittlerweile Standard:
- Zeilen 12–14: Zeigen im Falle eines Fehlers im Feld [assertFalse] entweder die Meldung an, die durch das Attribut [data-val-assertfalse] in Zeile 6 übermittelt wird, oder diejenige, die durch das Attribut [data-val-required] in derselben Zeile übermittelt wird. Es sei daran erinnert, dass diese Meldungen lokalisiert sind, d. h. in der zuvor vom Benutzer gewählten Sprache oder auf Französisch, falls er keine Auswahl getroffen hat;
- Zeilen 5–10: Zeigen die Optionsfelder mit JavaScript-Validatoren an, die ausgelöst werden, sobald der Benutzer auf eines davon klickt.
Beide Schaltflächen sind auf die gleiche Weise aufgebaut. Wir betrachten nun die erste:
<input type="radio" th:field="*{assertFalse}" value="true" data-val="true" th:attr="data-val-required=#{NotNull},data-val-assertfalse=#{AssertFalse}" />
Nach der Verarbeitung durch Thymeleaf sieht diese Zeile wie folgt aus:
<input type="radio" value="true" data-val="true" data-val-required="Le champ est obligatoire" data-val-assertfalse="Seule la valeur False est acceptée" id="assertFalse1" name="assertFalse" />
Wir haben Validatoren mit den Namen [data-val="true"]. Es sind zwei. Ein Validator namens [required] [data-val-required="Le champ est obligatoire"] und ein weiterer namens [assertfalse] [data-val-assertfalse="Seule la valeur False est acceptée"]. Zur Erinnerung: Der Wert des Attributs [data-val-X] ist die Fehlermeldung des Validators X.
Wir haben den Validator [required] bereits kennengelernt. Neu ist hier, dass man einem eingegebenen Wert mehrere Validatoren zuordnen kann. Während der Validator [required] in der Validierungsbibliothek MS (Microsoft) bekannt ist, ist dies beim Validator [assertFalse] nicht der Fall. Wir werden daher lernen, wie man einen neuen Validator erstellt. Wir werden mehrere davon erstellen, die in einer Datei namens [client-validation.js] abgelegt werden:
![]() |
Diese Datei wird, wie die anderen auch, von der Ansicht [vue-01.xml] importiert (Zeile 6 unten):
<head>
<title>Spring 4 MVC</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="/css/form01.css" />
...
<script type="text/javascript" src="/js/client-validation.js"></script>
...
</head>
Das Hinzufügen des Validators [assertfalse] beschränkt sich auf die Erstellung der beiden folgenden Funktionen jS:
// -------------- assertfalse
$.validator.addMethod("assertfalse", function(value, element, param) {
return value === "false";
});
$.validator.unobtrusive.adapters.add("assertfalse", [], function(options) {
options.rules["assertfalse"] = options.params;
options.messages["assertfalse"] = options.message.replace("''", "'");
});
Ganz ehrlich gesagt bin ich kein JavaScript-Experte – eine Sprache, die mir nach wie vor ein Rätsel ist. Die Grundlagen sind einfach, aber die darauf aufbauenden Bibliotheken sind oft sehr komplex. Beim Schreiben der obigen Codezeilen habe ich mich von Beispielen aus dem Internet inspirieren lassen. Der Link [http://jsfiddle.net/LDDrk/] hat mir den Weg gewiesen. Falls er noch existiert, sei dem Leser empfohlen, ihn durchzusehen, da er umfassend ist und ein funktionierendes Beispiel enthält. Er zeigt, wie man einen neuen Validator erstellt, und hat es mir ermöglicht, alle Validatoren in diesem Kapitel zu erstellen. Kommen wir zurück zum Code:
- Zeilen 2–4: Definieren den neuen Validator. Die Funktion [$.validator.addMethod] erwartet als ersten Parameter den Namen des Validators und als zweiten Parameter eine Funktion, die diesen definiert;
- Zeile 2: Die Funktion hat drei Parameter:
- [value]: der zu validierende Wert. Die Funktion muss [true] zurückgeben, wenn der Wert gültig ist, andernfalls [false],
- [element]: das Element HTML, zu dem der zu validierende Wert gehört,
- [param]: ein Objekt, das die Werte enthält, die den Parametern eines Validators zugeordnet sind. Dieses Konzept haben wir noch nicht eingeführt. Hier hat der Validator [assertFalse] keine Parameter. Man kann ohne zusätzliche Informationen feststellen, ob der Wert [value] gültig ist. Anders wäre es, wenn man überprüfen müsste, ob der Wert [value] eine reelle Zahl im Intervall [min, max] ist. In diesem Fall müssten wir [min] und [max] kennen. Diese beiden Werte werden als Parameter des Validators bezeichnet;
- Zeilen 6–9: eine für die Validierungsbibliothek MS erforderliche Funktion. Die Funktion [$.validator.unobtrusive.adapters.add] erwartet als ersten Parameter den Namen des Validators, als zweiten Parameter das Array mit den Parametern des Validators und als dritten Parameter eine Funktion;
- der Validator [assertFalse] hat keine Parameter. Deshalb ist der zweite Parameter ein leeres Array;
- Die Funktion hat nur einen Parameter, ein Objekt [options], das Informationen über das zu validierende Element enthält und für das zwei neue Eigenschaften [rules] und [messages] definiert werden müssen;
- Zeile 7: Hier werden die Regeln [rules] für den Validator [assertFalse] definiert. Diese Regeln sind die Parameter des Validators [assertFalse], dieselben wie die des Parameters [param] in Zeile 2. Diese Parameter sind in [options.params] zu finden;
- Zeile 8: Sie definieren die Fehlermeldung des Validators [assertFalse]. Dieser ist in [options.message] zu finden. Bei den Fehlermeldungen gibt es folgende Schwierigkeit: In den Meldungsdateien findet man folgende Meldung:
Range.form01.int1014=La valeur doit être dans l''intervalle [10,14]
Das doppelte Apostroph ist für Thymeleaf erforderlich. Thymeleaf interpretiert es als einfaches Apostroph. Wenn man ein einfaches Apostroph verwendet, wird es von Thymeleaf nicht angezeigt. Nun dienen diese Meldungen auch als Fehlermeldungen für die Validierungsbibliothek MS. Das JavaScript zeigt jedoch beide Apostrophe an. In Zeile 8 ersetzen wir daher das doppelte Apostroph in der Fehlermeldung durch ein einfaches.
Um ein wenig zu sehen, was passiert, können wir jS-Log-Code hinzufügen:
// Protokolle
var logs = {
assertfalse : true
}
// -------------- assertfalse
$.validator.addMethod("assertfalse", function(value, element, param) {
// Protokolle
if (logs.assertfalse) {
console.log(jSON.stringify({
"[assertfalse] value" : value
}));
console.log("[assertfalse] element");
console.log(element);
console.log(jSON.stringify({
"[assertfalse] param" : param
}));
}
// Gültigkeitsprüfung
return value === "false";
});
$.validator.unobtrusive.adapters.add("assertfalse", [], function(options) {
// Protokolle
if (logs.assertfalse) {
console.log(jSON.stringify({
"[assertfalse] options.params" : options.params
}));
console.log(jSON.stringify({
"[assertfalse] options.message" : options.message
}));
console.log(jSON.stringify({
"[assertfalse] options.messages" : options.messages
}));
}
// Code
options.rules["assertfalse"] = options.params;
options.messages["assertfalse"] = options.message.replace("''", "'");
});
Dieser Code nutzt die Bibliothek jSON JSON3 [http://bestiejs.github.io/json3/]. Wenn wir die Protokollierung aktivieren (Zeile 3), erhalten wir folgende Ausgaben in der Konsole:
Beim ersten Laden der Seite werden folgende Protokolleinträge angezeigt:
Die Funktion jS [$.validator.unobtrusive.adapters.add] wurde ausgeführt. Dabei erfahren wir Folgendes:
- [options.params] ist ein leeres Objekt, da der Validator [assertFalse] keine Parameter hat;
- [options.message] ist die Fehlermeldung, die wir für den Validator [assertFalse] im Attribut [data-val-assertFalse] erstellt haben;
- [options.messages] ist ein Objekt, das die übrigen Fehlermeldungen des validierten Elements enthält. Hier finden wir die Fehlermeldung, die wir in das Attribut [data-val-required] eingefügt haben;
Geben wir nun einen falschen Wert in das Feld [assertFalse] ein und führen wir die Validierung durch:
Daraufhin erhalten wir die folgenden Protokolle:
![]() |
Darin ist Folgendes zu erkennen:
- Der getestete Wert ist [true] (Zeile 118);
- das getestete Element HTML ist das Optionsfeld mit der ID [assertFalse1] (Zeile 122);
- der Validator [assertFalse] hat keinen Parameter (Zeile 123);
Das war’s. Was lässt sich daraus schließen?
Für einen X-Validator jS müssen wir Folgendes definieren:
- im zu validierenden Tag HTML das Attribut [data-val-X='msg'], das sowohl den Validator X als auch dessen Fehlermeldung definiert;
- zwei Funktionen jS, die in die Datei [client-validation.js] aufgenommen werden müssen:
- [$.validator.addMethod("X", function(value, element, param)],
- [$.validator.unobtrusive.adapters.add("X", [param1, param2], function(options)];
Im weiteren Verlauf werden wir auf dem aufbauen, was für diesen ersten Validator bereits erstellt wurde, und lediglich die Neuerungen vorstellen.
6.3.10. Validator [asserttrue]
Dieser Validator ist natürlich analog zum Validator [assertFalse].
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich, asserttrue -->
<tr>
<td class="col1">asserttrue</td>
<td class="col2">
<select th:field="*{assertTrue}" data-val="true" th:attr="data-val-asserttrue=#{AssertTrue}">
<option value="true">True</option>
<option value="false">False</option>
</select>
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="assertTrue" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('assertTrue')}" th:errors="*{assertTrue}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [assertTrue] des Formulars [Form01]:
@NotNull
@AssertTrue
private Boolean assertTrue;
In den Zeilen 1–16 gibt es keine Neuerungen. Sie verwenden einen Validator [asserrtrue], der in der Datei [client-validation.js] definiert werden muss:
// -------------- asserttrue
$.validator.addMethod("asserttrue", function(value, element, param) {
return value === "true";
});
$.validator.unobtrusive.adapters.add("asserttrue", [], function(options) {
options.rules["asserttrue"] = options.params;
options.messages["asserttrue"] = options.message.replace("''", "'");
});
6.3.11. Validatoren [date] und [past]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich, Datum, Vergangenheit -->
<tr>
<td class="col1">required, date, past</td>
<td class="col2">
<input type="date" th:field="*{dateInPast}" th:value="*{dateInPast}" data-val="true"
th:attr="data-val-required=#{NotNull},data-val-date=#{DateInvalide.form01},data-val-past=#{Past.form01.dateInPast}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="dateInPast" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('dateInPast')}" th:errors="*{dateInPast}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [dateInPast] des Formulars [Form01]:
@NotNull
@Past
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dateInPast;
Die Zeile mit den Datumsvalidatoren lautet wie folgt:
<input type="date" th:field="*{dateInPast}" th:value="*{dateInPast}" data-val="true" th:attr="data-val-required=#{NotNull},data-val-date=#{DateInvalide.form01},data-val-past=#{Past.form01.dateInPast}" />
Darin finden sich drei Validatoren [data-val-X]: „required“, „date“ und „past“. Wir müssen in [client-validation.js] die Funktionen definieren, die diesen beiden neuen Validatoren zugeordnet sind:
logs.date = true;
// -------------- Datum
$.validator.addMethod("date", function(value, element, param) {
// Gültigkeit
var valide = Globalize.parseDate(value, "yyyy-MM-dd") != null;
// Protokolle
if (logs.date) {
console.log(jSON.stringify({
"[date] value" : value,
"[date] valide" : valide
}));
}
// Ergebnis
return valide;
});
$.validator.unobtrusive.adapters.add("date", [], function(options) {
options.rules["date"] = options.params;
options.messages["date"] = options.message.replace("''", "'");
});
und
logs.past = true;
// -------------- Vergangenheit
$.validator.addMethod("past", function(value, element, param) {
// Gültigkeit
var valide = value <= new Date().toISOString().substring(0, 10);
// Protokolle
if (logs.past) {
console.log(jSON.stringify({
"[past] value" : value,
"[past] valide" : valide
}));
}
// Ergebnis
return valide;
});
$.validator.unobtrusive.adapters.add("past", [], function(options) {
options.rules["past"] = options.params;
options.messages["past"] = options.message.replace("''", "'");
});
Bevor wir den Code erläutern, schauen wir uns die Protokolle an, wenn ein Datum eingegeben wird, das nach dem heutigen liegt:
Als Erstes fällt auf, dass das zu validierende Datum als Zeichenkette im Format [aaaa-mm-jj] eingeht. Dies erklärt die folgenden Zeilen:
var valide = Globalize.parseDate(value, "yyyy-MM-dd") != null;
Die Bibliothek [globalize.js] stellt die oben genannte Funktion [Globalize.parseDate] bereit. Der erste Parameter ist das Datum als Zeichenkette und der zweite sein Format. Das Ergebnis ist ein Zeiger null, wenn das Datum ungültig ist, andernfalls das resultierende Datum.
Die Gültigkeit des Validators [past] wird durch den folgenden Code überprüft:
var valide = value <= new Date().toISOString().substring(0, 10);
Hier ist die Auswertung des Ausdrucks [new Date().toISOString().substring(0, 10)] in einer Konsole:
![]() |
Die Zeichenkette [value] muss alphabetisch vor der Zeichenkette [new Date().toISOString().substring(0, 10)] stehen, um gültig zu sein.
Es ist zu beachten, dass die verwendete Chrome-Version das Datum im Format [yyyy-mm-dd] angibt. Bei einem Browser, bei dem dies nicht der Fall ist, müsste der Benutzer ausdrücklich darauf hingewiesen werden, dieses Eingabeformat zu verwenden.
6.3.12. Validator [future]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich, Datum, Zukunft -->
<tr>
<td class="col1">required, date, future</td>
<td class="col2">
<input type="date" th:field="*{dateInFuture}" th:value="*{dateInFuture}" data-val="true" th:attr="data-val-required=#{NotNull},data-val-date=#{DateInvalide.form01},data-val-future=#{Future.form01.dateInFuture}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="dateInFuture" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('dateInFuture')}" th:errors="*{dateInFuture}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [dateInFuture] des Formulars [Form01]:
@NotNull
@Future
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dateInFuture;
- in Zeile 5 erscheint ein neuer Validator [data-val-future];
Dieser Validator ist natürlich dem Validator [past] sehr ähnlich. Die beiden Funktionen, die in [client-validation.js] hinzugefügt werden müssen, lauten wie folgt:
// -------------- zukünftig
$.validator.addMethod("future", function(value, element, param) {
var now = new Date().toISOString().substring(0, 10);
return value > now;
});
$.validator.unobtrusive.adapters.add("future", [], function(options) {
options.rules["future"] = options.params;
options.messages["future"] = options.message.replace("''", "'");
});
6.3.13. Validatoren [int] und [max]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich, int, max(100) -->
<tr>
<td class="col1">required, int, max(100)</td>
<td class="col2">
<input type="text" th:field="*{intMax100}" th:value="*{intMax100}" data-val="true" th:attr="data-val-required=#{NotNull},data-val-int=#{typeMismatch},data-val-max=#{Max.form01.intMax100},data-val-max-value=#{form01.intMax100.value}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="intMax100" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('intMax100')}" th:errors="*{intMax100}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [intMax100] des Formulars [Form01]:
@NotNull
@Max(value = 100)
private Integer intMax100;
In Zeile 5 gibt es zwei neue Validatoren: [int] und [max]. Letzterer hat einen Parameter: den Maximalwert. Sehen wir uns den durch Zeile 5 generierten Code HTML an:
<!-- erforderlich, int, max(100) -->
<tr>
<td class="col1">required, int, max(100)</td>
<td class="col2">
<input type="text" data-val="true" data-val-int="Format invalide" data-val-max-value="100" data-val-required="Le champ est obligatoire" data-val-max="La valeur doit être inférieure ou égale à 100" value="" id="intMax100" name="intMax100" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="intMax100" data-valmsg-replace="true"></span>
</td>
<td class="col4">
</td>
</tr>
Zur Erinnerung: Die Bedeutung der verschiedenen Attribute von [data-X] lautet wie folgt:
- [data-val="true"] gibt an, dass dem Element HTML Validatoren zugeordnet sind;
- [data-val-required] führt den Validator [required] mit seiner Meldung ein;
- [data-val-int] führt den Validator [int] mit seiner Meldung ein;
- [data-val-max] führt den Validator [max] mit seiner Meldung ein;
- [data-val-max-value="100"] führt einen Parameter namens [value] für den Validator [max] ein. [100] ist der Wert dieses Parameters. Dies ist das erste Mal, dass wir auf den Begriff der Validatorparameter stoßen.
Die Datei [client-validation.js] wird um den folgenden Validator [int] erweitert:
logs.int = true;
// -------------- int
$.validator.addMethod("int", function(value, element, param) {
// Gültigkeit
valide = /^\s*[-\+]?\s*\d+\s*$/.test(value);
// Protokolle
if (logs.int) {
console.log(jSON.stringify({
"[int] value" : value,
"[int] valide" : valide,
}));
}
// Ergebnis
return valide;
});
$.validator.unobtrusive.adapters.add("int", [], function(options) {
options.rules["int"] = options.params;
options.messages["int"] = options.message.replace("''", "'");
});
- Zeile 5: Es wird ein regulärer Ausdruck verwendet, um zu überprüfen, ob die Zeichenfolge [value] tatsächlich eine ganze Zahl darstellt. Diese kann vorzeichenbehaftet sein;
Hier sind einige Beispiele für Protokolleinträge:
Der Validator [max] wird wie folgt in [client-validation.js] hinzugefügt
// -------------- max in Verbindung mit [int] oder [number] zu verwenden
logs.max = true;
$.validator.addMethod("max", function(value, element, param) {
// Protokolle
if (logs.max) {
console.log(jSON.stringify({
"[max] value" : value,
"[max] param" : param
}));
}
// Gültigkeit
var val = Globalize.parseFloat(value);
if (isNaN(val)) {
// Protokolle
if (logs.max) {
console.log(jSON.stringify({
"[max] valide" : true
}));
}
// Ergebnis
return true;
}
var max = Globalize.parseFloat(param.value);
var valide = val <= max;
// Protokolle
if (logs.max) {
console.log(jSON.stringify({
"[max] valide" : valide
}));
}
// Ergebnis
return valide;
});
$.validator.unobtrusive.adapters.add("max", [ "value" ], function(options) {
options.rules["max"] = options.params;
options.messages["max"] = options.message.replace("''", "'");
});
Wir werden uns nun den Fall des Parameters [value] des Validators [max] befassen, der durch das Attribut [data-val-max-value="100"] eingeführt wurde.
- In Zeile 35 wird der Parameter [value] in den zweiten Parameter der Funktion [$.validator.unobtrusive.adapters.add] integriert;
- Zeile 3: Das Objekt [param] ist nun nicht mehr leer, sondern enthält {"value":100};
Um den Code in den Zeilen 3–33 zu verstehen, muss man wissen, dass bei mehreren Validatoren für dasselbe Element HTML:
- die Ausführungsreihenfolge der Validatoren nicht bekannt ist;
- die Ausführung der Validatoren wird beendet, sobald ein Validator das Element als ungültig deklariert. In diesem Fall wird die Fehlermeldung dieses Validators dem ungültigen Element zugeordnet;
Betrachten wir den Code:
- Zeile 12: Es wird überprüft, ob eine Zahl vorliegt. Wenn der Validator [int] vor dem Validator [max] ausgeführt wurde, ist dies zwangsläufig der Fall, da ein ungültiger Wert die Ausführung der Validatoren beendet;
- Zeilen 13–22: Wenn kein Zahlenwert vorliegt, bedeutet dies, dass der Validator [int] noch nicht ausgeführt wurde. Man gibt dann an, dass der geprüfte Wert gültig ist, damit der Validator [int] seine Arbeit verrichten und das Element mit einer eigenen Fehlermeldung als ungültig deklarieren kann;
- Zeilen 23–24: Berechnung der Gültigkeit von [value];
Hier sind einige Protokolleinträge:
Eingegebener Wert | Protokolle |
| |
| |
|
6.3.14. Validator [min]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich, int, min(10) -->
<tr>
<td class="col1">required, int, min(10)</td>
<td class="col2">
<input type="text" th:field="*{intMin10}" th:value="*{intMin10}" data-val="true" th:attr="data-val-required=#{NotNull},data-val-int=#{typeMismatch},data-val-min=#{Min.form01.intMin10},data-val-min-value=#{form01.intMin10.value}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="intMin10" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('intMin10')}" th:errors="*{intMin10}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [intMin10] des Formulars [Form01]:
@NotNull
@Min(value = 10)
private Integer intMin10;
Zeile 5 fügt einen neuen Validator [min] [data-val-int=#{typeMismatch}] mit einem Parameter [value] [data-val-min-value=#{form01.intMin10.value}"] ein. Hier handelt es sich um einen ähnlichen Fall wie beim Validator [max]. In [client-validation.js] wird der folgende Code hinzugefügt:
logs.min = true;
//-------------- min muss in Verbindung mit [int] oder [number] verwendet werden
$.validator.addMethod("min", function(value, element, param) {
// Protokolle
if (logs.min) {
console.log(jSON.stringify({
"[min] value" : value,
"[min] param" : param
}));
}
// Gültigkeit
var val = Globalize.parseFloat(value);
if (isNaN(val)) {
// Protokolle
if (logs.min) {
console.log(jSON.stringify({
"[min] valide" : true
}));
}
// Ergebnis
return true;
}
var min = Globalize.parseFloat(param.value);
var valide = val >= min;
// Protokolle
if (logs.min) {
console.log(jSON.stringify({
"[min] valide" : valide
}));
}
// Ergebnis
return valide;
});
$.validator.unobtrusive.adapters.add("min", [ "value" ], function(options) {
options.rules["min"] = options.params;
options.messages["min"] = options.message.replace("''", "'");
});
Hier sind einige Ausführungsprotokolle:
Eingegebener Wert | Protokolle |
| |
| |
|
6.3.15. Validator [regex]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich, regulärer Ausdruck -->
<tr>
<td class="col1">required, regex</td>
<td class="col2">
<input type="text" th:field="*{strBetween4and6}" th:value="*{strBetween4and6}" data-val="true" th:attr="data-val-required=#{NotNull},data-val-regex=#{Size.form01.strBetween4and6}, data-val-regex-pattern=#{form01.strBetween4and6.pattern}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="strBetween4and6" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('strBetween4and6')}" th:errors="*{strBetween4and6}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [strBetween4and6] des Formulars [Form01]:
@NotNull
@Size(min = 4, max = 6)
private String strBetween4and6;
Zeile 5 generiert das folgende HTML:
<input type="text" data-val="true" data-val-required="Le champ est obligatoire" data-val-regex="La chaîne doit avoir entre 4 et 6 caractères" data-val-regex-pattern="^.{4,6}$" value="" id="strBetween4and6" name="strBetween4and6" />
Dieses Tag führt den Validator [regex] [data-val-regex="La chaîne doit avoir entre 4 et 6 caractères"] mit seinem Parameter [pattern] [data-val-regex-pattern="^.{4,6}$"] ein. Der Parameter [pattern] ist der reguläre Ausdruck, anhand dessen der zu validierende Wert überprüft werden muss. Hier überprüft der reguläre Ausdruck, ob die Zeichenfolge zwischen 4 und 6 beliebige Zeichen enthält. Der Validator [regex] ist in der Validierungsbibliothek MS vordefiniert. Es muss daher nichts in die Datei [client-validation.js] hinzugefügt werden.
6.3.16. Validator [email]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich, E-Mail -->
<tr>
<td class="col1">required, email</td>
<td class="col2">
<input type="text" th:field="*{email}" th:value="*{email}" data-val="true" th:attr="data-val-required=#{NotNull},data-val-email=#{Email.form01.email}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="email" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [email] des Formulars [Form01]:
@NotNull
@Email
@NotBlank
private String email;
Zeile 5 erzeugt die folgende Zeile HTML:
<input type="text" data-val="true" data-val-required="Le champ est obligatoire" data-val-email="Adresse mail invalide" value="" id="email" name="email" />
Dieses Tag führt den Validator [email] [data-val-email="Adresse mail invalide"] ein. Der Validator [email] ist in der Validierungsbibliothek MS vordefiniert. Es muss daher nichts in die Datei [client-validation.js] hinzugefügt werden.
6.3.17. Validator [range]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich, int, Bereich (10,14) -->
<tr>
<td class="col1">required, int, range (10,14)</td>
<td class="col2">
<input type="text" th:field="*{int1014}" th:value="*{int1014}" data-val="true" th:attr="data-val-required=#{NotNull},data-val-int=#{typeMismatch}, data-val-range=#{Range.form01.int1014},data-val-range-max=#{form01.int1014.max},data-val-range-min=#{form01.int1014.min}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="int1014" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('int1014')}" th:errors="*{int1014}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [int1014] des Formulars [Form01]:
@Range(min = 10, max = 14)
@NotNull
private Integer int1014;
Zeile 5 generiert die folgende Zeile HTML:
<input type="text" data-val="true" data-val-range-max="14" data-val-range="La valeur doit être dans l''intervalle [10,14]" data-val-int="Format invalide" data-val-required="Le champ est obligatoire" data-val-range-min="10" value="" id="int1014" name="int1014" />
Dieses Tag führt einen neuen Validator [range] [data-val-range="La valeur doit être dans l''intervalle [10,14]"], der zwei Parameter [min] [data-val-range-min="10"] und [max] [data-val-range-max="14"] hat.
In der Datei [client-validation.js] definieren wir den Validator [range] wie folgt:
// -------------- Bereich muss in Verbindung mit [int] oder [number] verwendet werden
logs.range=true
$.validator.addMethod("range", function(value, element, param) {
// Protokolle
if (logs.range) {
console.log(jSON.stringify({
"[range] value" : value,
"[range] param" : param
}));
}
// Gültigkeit
var val = Globalize.parseFloat(value);
if (isNaN(val)) {
// Protokolle
if (logs.min) {
console.log(jSON.stringify({
"[range] valide" : true
}));
}
// abgeschlossen
return true;
}
var min = Globalize.parseFloat(param.min);
var max = Globalize.parseFloat(param.max);
var valide = val >= min && val <= max;
// Protokolle
if (logs.range) {
console.log(jSON.stringify({
"[range] valide" : valide
}));
}
// abgeschlossen
return valide;
});
$.validator.unobtrusive.adapters.add("range", [ "min", "max" ], function(options) {
options.rules["range"] = options.params;
options.messages["range"] = options.message.replace("''", "'");
});
Er ist den bereits behandelten Validatoren [min] und [max] sehr ähnlich.
Hier sind einige Beispiele für Protokolle:
Eingegebener Wert | Protokolle |
| |
| |
|
6.3.18. Validator [number]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- double1: erforderlich, Zahl, Bereich (2,3; 3,4) -->
<tr>
<td class="col1">double1 : required, number, range (2.3,3.4)</td>
<td class="col2">
<input type="text" th:field="*{double1}" th:value="*{double1}" data-val="true"
th:attr="data-val-required=#{NotNull},data-val-number=#{typeMismatch},data-val-range=#{Range.form01.double1},data-val-range-max=#{form01.double1.max},data-val-range-min=#{form01.double1.min}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="double1" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('double1')}" th:errors="*{double1}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [double1] des Formulars [Form01]:
@NotNull
@DecimalMax(value = "3.4")
@DecimalMin(value = "2.3")
private Double double1;
Zeile 5 erzeugt die folgende Zeile HTML:
<input type="text" data-val="true" data-val-number="Format invalide" data-val-range-max="3.4" data-val-range="La valeur doit être dans l'intervalle [2,3-3,4]" data-val-required="Le champ est obligatoire" data-val-range-min="2.3" value="" id="double1" name="double1" />
Das Tag fügt einen neuen Validator [number] mit dem Attribut [data-val-number="Format invalide"] ein. Dieser Validator ist in der Datei [client-validation.js] wie folgt definiert:
// -------------- Zahl
logs.number = true;
$.validator.addMethod("number", function(value, element, param) {
var valide = !isNaN(Globalize.parseFloat(value));
// Protokolle
if (logs.number) {
console.log(jSON.stringify({
"[number] value" : value,
"[number] valide" : valide
}));
}
// Ergebnis
return valide;
});
$.validator.unobtrusive.adapters.add("number", [], function(options) {
options.rules["number"] = options.params;
options.messages["number"] = options.message.replace("''", "'");
});
Hier sind einige Beispiele für Protokolleinträge:
Eingegebener Wert | Protokolle |
Es ist bekannt, dass reelle Zahlen kulturspezifisch sind. Oben befinden wir uns in der Kultur [fr-FR]. Wenn man [2.5] (angelsächsische Schreibweise) eingibt, wird die Zahl akzeptiert. Das liegt an [Globalize.parseFloat], das beide Schreibweisen akzeptiert:
Wechseln wir ins Englische und geben wir [+2,5] und [+2.5] ein. Die Protokolleinträge lauten wie folgt:
Eingegebener Wert | Protokolle |
Es gibt ein Problem mit [2,5]. Es wurde als gültiger Wert gemeldet, obwohl eigentlich [2.5] geschrieben werden müsste. Schuld daran ist [Globalize.parseFloat]:
Im obigen Beispiel ignoriert [Globalize.parseFloat] das Komma und interpretiert die Zahl als 25. In der Kultur von [en-US] kann eine reelle Zahl einen Dezimalpunkt und Kommas enthalten, die manchmal zur Trennung von Tausendern verwendet werden.
Man kann dies wie folgt verbessern:
// -------------- Zahl
logs.number = true;
$.validator.addMethod("number", function(value, element, param) {
// Es werden ausschließlich die Kulturen [fr-FR] und [en-US] verwaltet
var pattern_fr_FR = /^\s*[-+]?[0-9]*\,?[0-9]+\s*$/;
var pattern_en_US = /^\s*[-+]?[0-9]*\.?[0-9]+\s*$/;
var culture = Globalize.culture().name;
// Gültigkeitsprüfung
var valide;
if (culture === "fr-FR") {
valide = pattern_fr_FR.test(value);
} else if (culture === "en-US") {
valide = pattern_en_US.test(value);
} else {
valide = !isNaN(Globalize.parseFloat(value));
}
// Protokolle
if (logs.number) {
console.log(jSON.stringify({
"[number] value" : value,
"[number] culture" : culture,
"[number] valide" : valide
}));
}
// Ergebnis
return valide;
});
- Zeile 5: Der reguläre Ausdruck für eine reelle Zahl in der Kultur [fr-FR];
- Zeile 6: Der reguläre Ausdruck für eine reelle Zahl in der Kultur „[en-US]“;
- Zeile 7: der Name der aktuellen Kultur. In unserem Beispiel ist dies eine der beiden oben genannten Kulturen;
- Zeilen 9–16: die Gültigkeitsprüfung des eingegebenen Werts;
- Zeile 15: Es wurde der Fall berücksichtigt, dass die Kultur weder [fr-FR] noch [en-US] ist;
Die Protokolle zeigen nun Folgendes an:
Kultur [fr-FR]
Eingegebener Wert | Protokolle |
| |
| |
| |
|
Kultur [en-US]
Eingegebener Wert | Protokolle |
| |
| |
|
6.3.19. Validator [custom3]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- double3: erforderlich, Zahl, custom3 -->
<tr>
<td class="col1">double3 : required, number, custom3</td>
<td class="col2">
<input type="text" th:field="*{double3}" th:value="*{double3}" data-val="true" th:attr="data-val-required=#{NotNull},data-val-number=#{typeMismatch},data-val-custom3=${custom3.message},data-val-custom3-field=${custom3.otherFieldName},data-val-custom3-max=${custom3.max},data-val-custom3-min=${custom3.min}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="double3" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('double3')}" th:errors="*{double3}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [double3] des Formulars [Form01]:
@NotNull
private Double double3;
Hier soll ein Validator untersucht werden, der nicht mehr einen eingegebenen Wert, sondern eine Beziehung zwischen zwei eingegebenen Werten validiert. In diesem Fall soll [double1+double3] im Intervall von [10,13] liegen.
Zeile 5 erzeugt die folgende Zeile HTML:
<input type="text" data-val="true" data-val-custom3-min="10.0" data-val-number="Invalid format"
data-val-custom3="[double3+double1] must be in [10,13]" data-val-custom3-max="13.0" data-val-custom3-field="double1" data-val-required="Field is required" value="" id="double3" name="double3" />
Diese Zeile führt den neuen Validator [custom3] ein, der durch das Attribut [data-val-custom3="[double3+double1] must be in [10,13]"] deklariert wird. Dieser Validator hat folgende Parameter:
- [field], deklariert durch das Attribut [data-val-custom3-field="double1"]. Dieser Parameter bezeichnet das Feld, dessen Wert in die Gültigkeitsprüfung von [double3] einfließt;
- [min], deklariert durch das Attribut [data-val-custom3-min="10.0"]. Dieser Parameter ist der Mindestwert des Intervalls [min, max], in dem sich [double1+double3] befinden muss;
- [max] wird durch das Attribut [data-val-custom3-max="13.0"] definiert. Dieser Parameter ist der Maximalwert des Intervalls [min, max], in dem sich [double1+double3] befinden muss;
Dieser Validator wird in [client-validation.js] wie folgt verwaltet:
// -------------- custom3 in Verbindung mit [number]
logs.custom3 = true;
$.validator.addMethod("custom3", function(value1, element, param) {
// zweiter Wert
var value2 = $("#" + param.field).val();
// Protokolle
if (logs.custom3) {
console.log(jSON.stringify({
"[custom3] value1" : value1,
"[custom3] param" : param,
"[custom3] value2" : value2
}))
}
// erster Wert
var valeur1 = Globalize.parseFloat(value1);
if (isNaN(valeur1)) {
// man überlässt die Arbeit dem Validator [number]
if (logs.custom3) {
console.log(jSON.stringify({
"[custom3] valide" : true
}))
}
return true;
}
// zweiter Wert
var valeur2 = Globalize.parseFloat(value2);
if (isNaN(valeur2)) {
// Die Gültigkeitsprüfung kann nicht durchgeführt werden
if (logs.custom3) {
console.log(jSON.stringify({
"[custom3] valide" : false
}))
}
return false;
}
// Gültigkeitsprüfung
var min = Globalize.parseFloat(param.min);
var max = Globalize.parseFloat(param.max);
var somme = valeur1 + valeur2;
var valide = somme >= min && somme <= max;
// Protokolle
if (logs.custom3) {
console.log(jSON.stringify({
"[custom3] valide" : valide
}))
}
// Ergebnis
return valide;
});
$.validator.unobtrusive.adapters.add("custom3", [ "field", "max", "min" ], function(options) {
options.rules["custom3"] = options.params;
options.messages["custom3"] = options.message.replace("''", "'");
});
Hier sind einige Beispiele für Protokolle:
Eingegebene Werte [double1,double3] | Protokolle |
| |
| |
| |
|
6.3.20. Validator [url]
![]() |
Die Zeile [1] wird durch die folgende Sequenz der Ansicht [vue-01.xml] generiert:
<!-- erforderlich, URL -->
<tr>
<td class="col1">required, url</td>
<td class="col2">
<input type="text" th:field="*{url}" th:value="*{url}" data-val="true" th:attr="data-val-required=#{NotNull},data-val-url=#{URL.form01.url}" />
</td>
<td class="col3">
<span class="field-validation-valid" data-valmsg-for="url" data-valmsg-replace="true"></span>
</td>
<td class="col4">
<span th:if="${#fields.hasErrors('url')}" th:errors="*{url}" class="error">Donnée erronée</span>
</td>
</tr>
Diese Zeilen beziehen sich auf das Feld [url] des Formulars [Form01]:
@URL
@NotBlank
private String url;
Zeile 5 generiert die folgende Zeile HTML:
<input type="text" data-val="true" data-val-url="Invalid URL" data-val-required="Field is required" value="" id="url" name="url" />
Sie fügt den Validator [url] mit dem Attribut [data-val-url] ein. Dieser Validator ist in der Validierungsbibliothek jQuery vordefiniert. In [client-validation.js] muss nichts hinzugefügt werden.
6.3.21. Aktivierung/Deaktivierung der clientseitigen Validierung
Solange die clientseitige Validierung aktiv ist, wird die serverseitige Validierung nie sichtbar, da die übermittelten Werte nur dann beim Server ankommen, wenn sie clientseitig als gültig deklariert wurden. Um die serverseitige Validierung in Aktion zu sehen, muss die clientseitige Validierung deaktiviert werden. Die Ansicht [vue-01.xml] bietet zwei Links zur Verwaltung dieser Aktivierung/Deaktivierung:
<a id="clientValidationTrue" href="javascript:setClientValidation(true)">
<span style="margin-left:30px" th:text="#{client.validation.true}"></span>
</a>
<a id="clientValidationFalse" href="javascript:setClientValidation(false)">
<span style="margin-left:30px" th:text="#{client.validation.false}"></span>
</a>
Diese beiden Links sind nicht gleichzeitig sichtbar:
![]() | ![]() |
Die Übersetzung HTML dieser Links lautet wie folgt:
<a id="clientValidationTrue" href="javascript:setClientValidation(true)">
<span style="margin-left:30px">Activer la validation client</span>
</a>
<a id="clientValidationFalse" href="javascript:setClientValidation(false)">
<span style="margin-left:30px">Inhiber la validation client</span>
</a>
Das Skript jS [setClientValidation] ist in der Datei [local.js] definiert (siehe oben). In der Funktion [$(document).ready] dieser Datei werden die Validierungslinks ausgewertet:
// Dokument bereit
$(document).ready(function() {
// globale Verweise
...
activateValidationTrue = $("#clientValidationTrue");
activateValidationFalse = $("#clientValidationFalse");
clientValidation = $("#clientValidation");
...
// Validierungslinks
// clientValidation ist ein vom Server gesetztes verstecktes Feld
var validate = clientValidation.val();
setClientValidation2(validate === "true");
});
- Zeile 5: ein Verweis auf den Link zur Aktivierung der clientseitigen Validierung;
- Zeile 6: ein Verweis auf den Link zur Deaktivierung der clientseitigen Validierung;
- Zeile 7: ein Verweis auf ein verstecktes Feld des Formulars, das den letzten Status der Aktivierung in Form eines booleschen Werts speichert [true : validation client activée, false : validation client désactivée]. Dieses Feld befindet sich in der Ansicht [vue-01.xml] in folgender Form:
<input type="hidden" th:field="*{clientValidation}" th:value="*{clientValidation}" value="true" />
und entspricht dem Feld [clientValidation] des Formulars [Form01]:
// Client-Validierung
private boolean clientValidation = true;
- Zeile 11: Der Wert des versteckten Feldes wird abgerufen;
- Zeile 12: Die folgende Funktion [setClientValidation2] wird aufgerufen:
function setClientValidation2(activate) {
// Links
if (activate) {
// Die clientseitige Validierung ist aktiv
activateValidationTrue.hide();
activateValidationFalse.show();
// Die Validatoren des Formulars werden analysiert
$.validator.unobtrusive.parse(formulaire);
} else {
// Die clientseitige Validierung ist deaktiviert
activateValidationFalse.hide();
activateValidationTrue.show();
// Die Validatoren des Formulars werden deaktiviert
formulaire.data('validator', null);
}
}
- Zeile 1: Der Parameter [activate] hat den Wert [true], wenn die clientseitige Validierung aktiviert werden soll, andernfalls „false“;
- Zeilen 5–6: Der Link zum Deaktivieren wird angezeigt, der Link zum Aktivieren ist ausgeblendet;
- Zeile 8: Damit die clientseitige Validierung funktioniert, muss das Dokument nach Validatoren mit der Kennung [data-val-X] durchsucht werden. Der Parameter der Funktion [$.validator.unobtrusive.parse] ist die Kennung jS des zu durchsuchenden Formulars;
- Zeilen 11–12: Der Aktivierungslink wird angezeigt, der Deaktivierungslink ist ausgeblendet;
- Zeile 14: Die Validatoren des Formulars werden deaktiviert. Von nun an ist es so, als gäbe es keine Validatoren jS im Formular;
Wozu dient diese Funktion [setClientValidation2]? Sie dient zur Verwaltung der POST. Da das Feld [clientValidation] ein verstecktes Feld ist, wird es übermittelt und kehrt mit dem vom Server zurückgesendeten Formular zurück. Sein Wert wird dann verwendet, um die clientseitige Validierung wieder so einzustellen, wie sie vor dem POST war. Tatsächlich gibt es zwischen den Anfragen keinen jS-Speicher. Daher muss der Server in der neuen Ansicht die Informationen übermitteln, mit denen die jS dieser Ansicht initialisiert werden kann. Dies geschieht üblicherweise in der Funktion [$(document).ready].
Kehren wir nun zur Funktion [setClientValidation] zurück, die den Klick auf die Links zum Aktivieren bzw. Deaktivieren der clientseitigen Validierung verarbeitet:
// Clientseitige Validierung
function setClientValidation(activate) {
// Die Aktivierung/Deaktivierung der clientseitigen Validierung wird verwaltet
setClientValidation2(activate);
// Die Auswahl des Benutzers wird im versteckten Feld gespeichert
clientValidation.val(activate ? "true" : "false");
// zusätzliche Anpassungen
if (activate) {
// Die clientseitige Validierung ist aktiv
// Alle Fehlermeldungen vom Server werden gelöscht
clearServerErrors();
// Das Formular wird validiert
formulaire.validate().form();
} else {
// Die clientseitige Validierung ist deaktiviert
// Alle Fehlermeldungen des Clients werden gelöscht
clearClientErrors();
}
}
- Zeile 4: Hier wird die Funktion [setClientValidation2] verwendet, die wir gerade betrachtet haben;
- Zeile 6: Die Auswahl des Benutzers wird im versteckten Feld gespeichert, um sie bei der Rückkehr aus der nächsten Funktion POST wieder abzurufen;
- Zeile 11: Wenn die clientseitige Validierung aktiv ist, werden die Fehlermeldungen aus der Spalte [serveur] der Ansicht gelöscht. Die Funktion [clearServerErrors] haben wir in Abschnitt 6.3.7 beschrieben;
- Zeile 13: Die Validatoren jS werden ausgeführt, um eventuelle Fehlermeldungen in der Spalte [client] der Ansicht anzuzeigen;
- Zeile 17: Ist die Client-Validierung deaktiviert, werden die Fehlermeldungen aus der Spalte [client] der Ansicht gelöscht. Sehen wir uns in der Chrome-Entwicklerkonsole den Code HTML eines fehlerhaften Elements an:
<td class="col2">
<input type="text" data-val="true" data-val-int="Format invalide" data-val-max-value="100" data-val-required="Le champ est obligatoire" data-val-max="La valeur doit être inférieure ou égale à 100" value="" id="intMax100" name="intMax100" aria-required="true" class="input-validation-error" aria-describedby="intMax100-error">
</td>
<td class="col3">
<span class="field-validation-error" data-valmsg-for="intMax100" data-valmsg-replace="true">
<span id="intMax100-error" class="">Le champ est obligatoire</span>
</span>
</td>
- In Zeile 2 sehen wir, dass das fehlerhafte Element in Spalte 2 der Tabelle den Stil [class="input-validation-error"] hat;
- In Zeile 5 sieht man, dass die Fehlermeldung in Spalte 3 der Tabelle den Typ [class="field-validation-error"] hat;
Das gilt für alle fehlerhaften Elemente. Diese beiden Informationen werden in der folgenden Funktion [clearClientErrors] verwendet:
// Client-Fehler löschen
function clearClientErrors() {
// Client-Fehlermeldungen werden gelöscht
$(".field-validation-error").each(function(index) {
$(this).text("");
});
// Die Klasse CSS der fehlerhaften Eingaben wird geändert
$(".input-validation-error").each(function(index) {
$(this).removeClass("input-validation-error");
});
}
- Zeilen 4–6: Es werden alle Elemente von DOM gesucht, die die Klasse [field-validation-error] haben, und der von ihnen angezeigte Text wird gelöscht. Auf diese Weise werden die Fehlermeldungen gelöscht;
- Zeilen 8–10: Es werden alle Elemente des DOM gesucht, die die Klasse [input-validation-error] haben, und diese Klasse wird ihnen entfernt. Dadurch erhält das fehlerhafte Element, das rot markiert war, wieder sein ursprüngliches Aussehen;


















































