Anleitung: Thymeleaf + Spring. Teil 3

Erster Teil
Zweiter Teil

7 Überprüfungs- und Fehlermeldungen


Die meisten unserer Formulare sollten Validierungsmeldungen anzeigen, um den Benutzer über von ihm gemachte Fehler zu informieren.

Thymeleaf bietet hierfür mehrere Tools an: verschiedene Funktionen im # fields- Objekt, th: error- und th: errorclass-Attribute .

7.1 Feldfehler


Mal sehen, wie wir eine bestimmte CSS-Klasse für ein Feld festlegen können, wenn es einen Fehler enthält:

<input type="text" th:field="*{datePlanted}" th:class="${#fields.hasErrors('datePlanted')}? fieldError" /> 

Wie Sie sehen können, empfängt die Funktion # fields.hasErrors (...) einen Feldausdruck als Parameter ( datePlanted ) und gibt einen booleschen Wert zurück, der angibt, ob für dieses Feld Validierungsfehler vorliegen .

Wir können auch alle Fehler für dieses Feld abrufen und wiederholen:

 <ul> <li th:each="err : ${#fields.errors('datePlanted')}" th:text="${err}" /> </ul> 

Anstatt zu iterieren, könnten wir auch th: Errors verwenden , ein spezielles Attribut, das eine Liste mit allen Fehlern für den angegebenen Selektor erstellt, getrennt durch <br />:

 <input type="text" th:field="*{datePlanted}" /> <p th:if="${#fields.hasErrors('datePlanted')}" th:errors="*{datePlanted}">Incorrect date</p> 

Fehlerbasiertes CSS-Styling: th: errorclass

Das obige Beispiel, in dem die CSS-Klasse für das Eingabeformular festgelegt wurde, wenn in diesem Feld Fehler auftreten, ist so häufig, dass Thymeleaf ein spezielles Attribut für die genaue Ausführung bietet: th: errorclass .

Wird auf das Formularfeld-Tag (Eingabe, Auswahl, Textbereich ...) angewendet, liest es den Namen des zu überprüfenden Felds aus einem vorhandenen Namen oder den Attributen th: field im selben Tag und fügt dem Tag die angegebene CSS-Klasse hinzu, falls es sich um ein solches Feld handelt hat verwandte Fehler:

 <input type="text" th:field="*{datePlanted}" class="small" th:errorclass="fieldError" /> 

Wenn datePlanted Fehler enthält , sieht es folgendermaßen aus:

 <input type="text" id="datePlanted" name="datePlanted" value="2013-01-01" class="small fieldError" /> 

7.2 Alle Fehler


Aber was ist, wenn wir alle Fehler im Formular anzeigen möchten? Wir müssen nur die Methoden # fields.hasErrors (...) und # fields.errors (...) mit den Konstanten ' * ' oder ' all ' (die äquivalent sind) anfordern :

 <ul th:if="${#fields.hasErrors('*')}"> <li th:each="err : ${#fields.errors('*')}" th:text="${err}">Input is incorrect</li> </ul> 

Wie in den obigen Beispielen könnten wir alle Fehler erhalten und sie durchlaufen ...

 <ul> <li th:each="err : ${#fields.errors('*')}" th:text="${err}" /> </ul> 

... und erstellen Sie auch eine gemeinsame Liste <br />:

 <p th:if="${#fields.hasErrors('all')}" th:errors="*{all}">Incorrect date</p> 

Beachten Sie schließlich, dass # fields.hasErrors ('*') # fields.hasAnyErrors () und # fields.errors ('*') # fields.allErrors () entspricht . Verwenden Sie die von Ihnen bevorzugte Syntax:

 <div th:if="${#fields.hasAnyErrors()}"> <p th:each="err : ${#fields.allErrors()}" th:text="${err}">...</p> </div> 

7.3 Globale Fehler


In der Spring-Form gibt es einen dritten Fehlertyp: globale Fehler. Hierbei handelt es sich um Fehler, die keinem bestimmten Feld im Formular zugeordnet sind, aber dennoch vorhanden sind.
Thymeleaf bietet eine globale Konstante für den Zugriff auf diese Fehler:

 <ul th:if="${#fields.hasErrors('global')}"> <li th:each="err : ${#fields.errors('global')}" th:text="${err}">Input is incorrect</li> </ul> 

 <p th:if="${#fields.hasErrors('global')}" th:errors="*{global}">Incorrect date</p> 

... a sowie die entsprechenden Hilfsmethoden # fields.hasGlobalErrors () und # fields.globalErrors () :

7.4 Fehler außerhalb von Formularen anzeigen


Fehler bei der Formularüberprüfung können auch außerhalb von Formularen mithilfe von Variablen ( $ {...} ) anstelle von ausgewählten Ausdrücken ( * {...} ) und einem Präfix für den Namen der Komponente angezeigt werden, die das Formular unterstützt:

 <div th:errors="${myForm}">...</div> <div th:errors="${myForm.date}">...</div> <div th:errors="${myForm.*}">...</div> <div th:if="${#fields.hasErrors('${myForm}')}">...</div> <div th:if="${#fields.hasErrors('${myForm.date}')}">...</div> <div th:if="${#fields.hasErrors('${myForm.*}')}">...</div> <form th:object="${myForm}"> ... </form> 

7.5 Rich-Error-Objekte


Thymeleaf bietet die Möglichkeit, Informationen über Formularfehler in Form von Bean-Komponenten (anstelle von einfachen Zeichenfolgen) mit den Attributen fieldName (String), message (String) und global (boolean) abzurufen .

Diese Fehler können mit der Dienstprogrammmethode # fields.detailedErrors () behoben werden:

 <ul> <li th:each="e : ${#fields.detailedErrors()}" th:class="${e.global}? globalerr : fielderr"> <span th:text="${e.global}? '*' : ${e.fieldName}">The field name</span> | <span th:text="${e.message}">The error message</span> </li> </ul> 

8 Dies ist immer noch ein Prototyp!


Unsere Bewerbung ist fertig. Aber schauen wir uns noch einmal die von uns erstellte HTML-Seite an ...

Eine der erfreulichsten Konsequenzen der Arbeit mit Thymeleaf ist, dass wir nach all diesen Funktionen, die wir unserem HTML hinzugefügt haben, diesen HTML-Code weiterhin als Prototyp verwenden können (wir sagen, dass dies eine natürliche Vorlage ist ). Lassen Sie uns seedstartermng.html direkt in unserem Browser öffnen, ohne unsere Anwendung zu starten:

Bild

Hier ist es! Dies ist keine funktionierende Anwendung, es sind keine echten Daten ... aber es ist ein völlig korrekter Prototyp, der aus perfekt angezeigtem HTML-Code besteht.

9 Der Konvertierungsdienst


9.1 Konfiguration


Wie bereits erläutert, kann Thymeleaf den im Anwendungskontext registrierten Transformationsdienst verwenden. Unsere Anwendungskonfigurationsklasse, die den nativen Spring WebMvcConfigurerAdapter-Helfer erweitert , registriert automatisch einen Konvertierungsdienst, den wir durch Hinzufügen der erforderlichen Formatierungswerkzeuge konfigurieren können. Mal sehen, wie es wieder aussieht:

 @Override public void addFormatters(final FormatterRegistry registry) { super.addFormatters(registry); registry.addFormatter(varietyFormatter()); registry.addFormatter(dateFormatter()); } @Bean public VarietyFormatter varietyFormatter() { return new VarietyFormatter(); } @Bean public DateFormatter dateFormatter() { return new DateFormatter(); } 

9.2 Syntax in doppelten Klammern


Der Konvertierungsdienst kann einfach angewendet werden, um ein Objekt in eine Zeichenfolge zu konvertieren / zu formatieren. Dies erfolgt mithilfe der Ausdruckssyntax in doppelten Klammern:

  • Für variable Ausdrücke: $ {{...}}
  • So drücken Sie eine Auswahl aus: * {{...}}

Angesichts des Integer-to-String-Konverters, der Kommas als Tausendertrennzeichen hinzufügt, lautet dies beispielsweise:

 <p th:text="${val}">...</p> <p th:text="${{val}}">...</p> 

... sollte führen zu:

 <p>1234567890</p> <p>1,234,567,890</p> 

9.3 Verwendung in Formularen


Wir haben bereits gesehen, dass jedes th: field- Attribut immer einen Transformationsdienst anwendet.

 <input type="text" th:field="*{datePlanted}" /> 

... eigentlich gleichbedeutend mit:

 <input type="text" th:field="*{{datePlanted}}" /> 

Beachten Sie, dass dies gemäß der Spring-Anforderung das einzige Szenario ist, in dem der Transformationsdienst auf Ausdrücke mit einklammeriger Syntax angewendet wird.

9.4 Konvertierungsobjekt #conversions


Mit dem Konvertierungsdienstprogramm #conversions können Sie den Konvertierungsdienst bei Bedarf manuell starten:

 <p th:text="${'Val: ' + #conversions.convert(val,'String')}">...</p> 

Die Syntax für dieses Serviceobjekt lautet:

  • # convertions.convert (Object, Class) : Konvertiert das Objekt in die angegebene Klasse
  • # convertions.convert (Object, String) : wie oben, jedoch mit der Zielklasse als String (beachten Sie, dass das Paket java.lang. möglicherweise weggelassen wird)

10 Rendern von Fragmenten der Vorlage Vorlagenfragmente (AJAX usw.)


Thymeleaf bietet die Möglichkeit, nur einen Teil der Vorlage als Ergebnis ihrer Ausführung zu rendern: Fragment .

Dies kann ein nützliches Komponentenwerkzeug sein. Beispielsweise kann es auf Controllern verwendet werden, die bei AJAX- Aufrufen ausgeführt werden und Fragmente des Layouts einer Seite zurückgeben können, die bereits in den Browser geladen wurde (um die Auswahl zu aktualisieren, schalten Sie die Schaltflächen ein / aus ...).

Fragmentiertes Rendern kann mithilfe von Thymeleaf-Snippet-Spezifikationen erreicht werden: Objekte, die die Schnittstelle org.thymeleaf.fragment.IFragmentSpec implementieren.

Die häufigste dieser Implementierungen ist org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec , mit der Sie ein Fragment mithilfe des DOM-Selektors angeben können, genau wie in th: include oder th: replace .

10.1 Fragmente in einer View Bean definieren


View Beans sind Beans der Klasse org.thymeleaf.spring4.view.ThymeleafView , die im Anwendungskontext deklariert sind ( Bean- Annotation, wenn Sie die Java-Konfiguration verwenden). Mit ihnen können Sie Fragmente wie folgt angeben:

 @Bean(name="content-part") @Scope("prototype") public ThymeleafView someViewBean() { ThymeleafView view = new ThymeleafView("index"); // templateName = 'index' view.setMarkupSelector("content"); return view; } 

In Anbetracht der obigen Definition einer Bean, wenn unser Controller einen Inhaltsteil (den Namen der obigen Bean) zurückgibt ...

 @RequestMapping("/showContentPart") public String showContentPart() { ... return "content-part"; } 

... Thymeleaf gibt nach dem Anwenden des Präfixes und des Suffixes nur ein Fragment des Inhalts der Indexvorlage zurück , dessen Speicherort wahrscheinlich ungefähr mit /WEB-INF/templates/index.html übereinstimmt . Somit entspricht das Ergebnis vollständig der Angabe von index :: content :

 <!DOCTYPE html> <html> ... <body> ... <div th:fragment="content"> Only this div will be rendered! </div> ... </body> </html> 

Beachten Sie auch, dass wir dank der leistungsstarken Thymeleaf-Layout-Selektoren ein Fragment in einer Vorlage ohne th: fragment- Attribute auswählen können. Verwenden wir zum Beispiel das Attribut id :

 @Bean(name="content-part") @Scope("prototype") public ThymeleafView someViewBean() { ThymeleafView view = new ThymeleafView("index"); // templateName = 'index' view.setMarkupSelector("#content"); return view; } 

10.2 Fragmente im Rückgabewert der Steuerung definieren


Anstatt View Beans zu deklarieren, können Fragmente vom Controller mithilfe der Syntax für Fragmentausdrücke definiert werden. Genau wie in den Attributen th: insert oder th: replace .

 @RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: content"; } 

Natürlich ist die volle Leistung der DOM-Selektoren wieder verfügbar, sodass wir unser Fragment basierend auf Standard-HTML-Attributen wie id = "content" auswählen können:

 @RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: #content"; } 

Und wir können auch Parameter verwenden wie:

 @RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: #content ('myvalue')"; } 

11 Erweiterte Integrationsfunktionen


11.1 Integration mit RequestDataValueProcessor


Thymeleaf lässt sich nahtlos in die Spring RequestDataValueProcessor- Schnittstelle integrieren. Über diese Schnittstelle können Sie Link-URLs, Formular-URLs und Formularfeldwerte abfangen, bevor Sie sie in das Markup-Ergebnis schreiben. Außerdem können Sie versteckte Formularfelder mit Sicherheitsfunktionen transparent hinzufügen, z. B.: Schutz vor CSRF (Cross-Site Request Forgery) .

Die Implementierung von RequestDataValueProcessor kann einfach im Anwendungskontext konfiguriert werden. Es sollte die Schnittstelle org.springframework.web.servlet.support.RequestDataValueProcessor implementieren und requestDataValueProcessor als Bean-Namen haben:

 @Bean public RequestDataValueProcessor requestDataValueProcessor() { return new MyRequestDataValueProcessor(); } 

... und Thymeleaf wird es wie folgt verwenden:

  • th: href und th: src rufen RequestDataValueProcessor.processUrl (...) auf, bevor die URL gerendert wird
  • th: action ruft RequestDataValueProcessor.processAction (...) auf, bevor das Aktionsattribut des Formulars gerendert wird, und erkennt außerdem, wann dieses Attribut auf das <form> -Tag angewendet wird, das in jedem Fall der einzige Ort sein sollte, und ruft in diesem Fall RequestDataValueProcessor.getExtraHiddenFields (...) auf ) und fügt die zurückgegebenen ausgeblendeten Felder unmittelbar vor dem schließenden </ form> -Tag hinzu
  • th: value ruft RequestDataValueProcessor.processFormFieldValue (...) auf, um den Wert zu zeichnen, auf den es verweist, es sei denn, th: field befindet sich im selben Tag (in diesem Fall kümmert sich th: field darum).
  • th: field ruft RequestDataValueProcessor.processFormFieldValue (...) auf, um den Wert des Feldes zu zeichnen, auf das es angewendet wird ( oder den Hauptteil des Tags, wenn es <textarea> ist).

Beachten Sie, dass es nur sehr wenige Szenarien gibt, in denen Sie RequestDataValueProcessor explizit in Ihrer Anwendung implementieren müssten . In den meisten Fällen wird es automatisch von Sicherheitsbibliotheken verwendet, die Sie transparent verwenden, z. B. CSRF von Spring Security.

11.1 URIs für Controller erstellen


Ab Version 4.1 bietet Spring die Möglichkeit, Links zu mit Anmerkungen versehenen Controllern direkt aus Ansichten zu erstellen, ohne die URIs zu kennen, denen diese Controller zugeordnet sind.

In Thymeleaf kann dies mit dem Ausdruck # mvc.url (...) erreicht werden , mit dem Sie Controller-Methoden in Großbuchstaben der Controller-Klasse festlegen können, in der sie sich befinden, gefolgt vom Methodennamen. Dies entspricht der benutzerdefinierten Funktion spring: mvcUrlx (...) in JSP.

Zum Beispiel für:

 public class ExampleController { @RequestMapping("/data") public String getData(Model model) { ... return "template" } @RequestMapping("/data") public String getDataParam(@RequestParam String type) { ... return "template" } } 

Der folgende Code erstellt Methodenreferenzen:

 <a th:href="${(#mvc.url('EC#getData')).build()}">Get Data Param</a> <a th:href="${(#mvc.url('EC#getDataParam').arg(0,'internal')).build()}">Get Data Param</a> 

Weitere Informationen zu diesem Mechanismus finden Sie unter http://docs.spring.io/spring-framework/docs/4.1.2.RELEASE/spring-framework-reference/html/mvc.html#mvc-links-to-controllers- von Ansichten

12 Spring WebFlow-Integration


Thymeleaf + Spring-Integrationspakete beinhalten die Integration mit Spring WebFlow (2.3+).

WebFlow enthält einige AJAX-Funktionen zum Rendern von Fragmenten der angezeigten Seite, wenn bestimmte Ereignisse (Übergänge) ausgelöst werden. Damit Thymeleaf diese AJAX-Anforderungen verfolgen kann, muss eine andere ViewResolver-Implementierung verwendet werden, die wie folgt konfiguriert ist:

 <bean id="thymeleafViewResolver" class="org.thymeleaf.spring4.view.AjaxThymeleafViewResolver"> <property name="viewClass" value="org.thymeleaf.spring4.view.FlowAjaxThymeleafView" /> <property name="templateEngine" ref="templateEngine" /> </bean> 

... und dann kann dieser ViewResolver in WebFlow ViewFactoryCreator wie folgt konfiguriert werden:

 <bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator"> <property name="viewResolvers" ref="thymeleafViewResolver"/> </bean> 

Von hier aus können Sie Thymeleaf-Vorlagen in Ihrem Ansichtsstatus definieren:

 <view-state id="detail" view="bookingDetail"> ... </view-state> 

Im obigen Beispiel ist bookingDetail eine Thymeleaf-Vorlage, die auf die übliche Weise angegeben wird und für jeden der in TemplateEngine konfigurierten Vorlagen-Resolver verständlich ist.

12.2 AJAX-Snippets in Spring WebFlow


Beachten Sie, dass hier nur erläutert wird, wie AJAX-Fragmente zur Verwendung mit Spring WebFlow erstellt werden. Wenn Sie WebFlow nicht verwenden, ist das Erstellen eines Spring MVC-Controllers, der auf eine AJAX-Anforderung reagiert und ein Stück HTML zurückgibt, so einfach wie das Erstellen eines anderen Controllers, der eine Vorlage zurückgibt, mit der einzigen Ausnahme, dass Sie wahrscheinlich ein Fragment wie " main " zurückgeben :: admin "von Ihrer Controller-Methode.

Mit WebFlow können Sie das Rendern über AJAX mit <render> -Tags definieren, beispielsweise wie folgt:

 <view-state id="detail" view="bookingDetail"> <transition on="updateData"> <render fragments="hoteldata"/> </transition> </view-state> 

Diese Fragmente (in diesem Fall Hoteldata ) können eine durch Kommas getrennte Liste von Fragmenten sein, die im Markup mit th: fragment : angegeben sind.

 <div id="data" th:fragment="hoteldata"> This is a content to be changed </div> 

Denken Sie immer daran, dass diese Snippets ein ID- Attribut haben müssen, damit im Browser ausgeführte Spring JavaScript-Bibliotheken das Markup ersetzen können.

Sie können <render> -Tags auch mit DOM-Selektoren angeben:

<view-state id = "detail" view = "bookingDetail">
/>

</ view-state>

... und das heißt, es besteht keine Notwendigkeit für th: fragment :

 <div id="data"> This is a content to be changed </div> 

Der Code, der den updateData- Übergang auslöst, sieht folgendermaßen aus:

 <script type="text/javascript" th:src="@{/resources/dojo/dojo.js}"></script> <script type="text/javascript" th:src="@{/resources/spring/Spring.js}"></script> <script type="text/javascript" th:src="@{/resources/spring/Spring-Dojo.js}"></script> ... <form id="triggerform" method="post" action=""> <input type="submit" id="doUpdate" name="_eventId_updateData" value="Update now!" /> </form> <script type="text/javascript"> Spring.addDecoration( new Spring.AjaxEventDecoration({formId:'triggerform',elementId:'doUpdate',event:'onclick'})); </script> 

Source: https://habr.com/ru/post/de437658/


All Articles