Première partieDeuxième partie7 Messages de vérification et d'erreur
La plupart de nos formulaires doivent afficher des messages de validation pour informer l'utilisateur des erreurs qu'il a commises.
Thymeleaf propose plusieurs outils pour cela: plusieurs fonctions dans l'objet
#fields , les
attributs th: errors et
th: errorclass .
7.1 Erreurs de champ
Voyons comment définir une classe CSS spécifique pour un champ s'il contient une erreur:
<input type="text" th:field="*{datePlanted}" th:class="${#fields.hasErrors('datePlanted')}? fieldError" />
Comme vous pouvez le voir, la fonction
# fields.hasErrors (...) reçoit une expression de champ en tant que paramètre (
datePlanted ) et renvoie une valeur booléenne indiquant s'il existe des erreurs de validation pour ce champ.
Nous pouvons également obtenir toutes les erreurs pour ce champ et les répéter:
<ul> <li th:each="err : ${#fields.errors('datePlanted')}" th:text="${err}" /> </ul>
Au lieu d'itérer, nous pourrions également utiliser
th: errors , un attribut spécialisé qui crée une liste avec toutes les erreurs pour le sélecteur spécifié, séparées par <br />:
<input type="text" th:field="*{datePlanted}" /> <p th:if="${#fields.hasErrors('datePlanted')}" th:errors="*{datePlanted}">Incorrect date</p>
Style CSS basé sur les erreurs:
th: errorclassL'exemple que nous avons vu ci-dessus, définissant la classe CSS pour le formulaire d'entrée, s'il y a des erreurs dans ce champ, est si commun que Thymeleaf offre un attribut spécial pour une exécution précise:
th: errorclass .
Appliqué à la balise de champ de formulaire (entrée, sélection, zone de texte ...), il lira le nom du champ à vérifier à partir de tout
nom existant ou d'attributs
th: field dans la même balise, puis ajoutera la classe CSS spécifiée à la balise, si un tel champ a des erreurs liées:
<input type="text" th:field="*{datePlanted}" class="small" th:errorclass="fieldError" />
S'il y a des erreurs dans
datePlanted , cela ressemblera à ceci:
<input type="text" id="datePlanted" name="datePlanted" value="2013-01-01" class="small fieldError" />
7.2 Toutes les erreurs
Mais que se passe-t-il si nous voulons afficher toutes les erreurs dans le formulaire? Il suffit de demander les
méthodes # fields.hasErrors (...) et
# fields.errors (...) avec les constantes '
* ' ou '
all ' (qui sont équivalentes):
<ul th:if="${#fields.hasErrors('*')}"> <li th:each="err : ${#fields.errors('*')}" th:text="${err}">Input is incorrect</li> </ul>
Comme dans les exemples ci-dessus, nous pourrions obtenir toutes les erreurs et les parcourir ...
<ul> <li th:each="err : ${#fields.errors('*')}" th:text="${err}" /> </ul>
... et créez également une liste partagée <br />:
<p th:if="${#fields.hasErrors('all')}" th:errors="*{all}">Incorrect date</p>
Enfin, notez que
# fields.hasErrors ('*') est équivalent à
# fields.hasAnyErrors () et
# fields.errors ('*') est équivalent à
# fields.allErrors () . Utilisez la syntaxe que vous préférez:
<div th:if="${#fields.hasAnyErrors()}"> <p th:each="err : ${#fields.allErrors()}" th:text="${err}">...</p> </div>
7.3 Erreurs globales
Il existe un troisième type d'erreur dans le formulaire Spring: les erreurs globales. Ce sont des erreurs qui ne sont associées à aucun champ spécifique du formulaire, mais qui existent toujours.
Thymeleaf propose une constante
globale pour accéder à ces erreurs:
<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 ainsi que les méthodes d'assistance équivalentes
# fields.hasGlobalErrors () et
# fields.globalErrors () :
7.4 Affichage des erreurs en dehors des formulaires
Les erreurs de validation de formulaire peuvent également être affichées en dehors des formulaires à l'aide de variables (
$ {...} ) au lieu d'expressions de sélection (
* {...} ) et d'un préfixe pour le nom du composant qui prend en charge le formulaire:
<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 Objets d'erreur riches
Thymeleaf offre la possibilité de recevoir des informations sur les erreurs de formulaire sous la forme de composants de bean (au lieu de simples chaînes) avec les attributs
fieldName (String),
message (String) et
global (boolean).
Ces erreurs peuvent être obtenues à l'aide de la méthode utilitaire
# fields.detailedErrors () :
<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 Ceci est encore un prototype!
Notre candidature est prête. Mais revenons à la page .html que nous avons créée ...
L'une des conséquences les plus agréables de travailler avec Thymeleaf est qu'après toutes ces fonctions que nous avons ajoutées à notre HTML, nous pouvons toujours utiliser ce HTML comme prototype (nous disons qu'il s'agit d'un
modèle naturel ). Ouvrons
seedstartermng.html directement dans notre navigateur sans lancer notre application:

Ça y est! Ce n'est pas une application qui fonctionne, ce ne sont pas de vraies données ... mais c'est un prototype complètement correct composé de code HTML parfaitement affiché.
9 Le service de conversion
9.1 Configuration
Comme expliqué précédemment, Thymeleaf peut utiliser le service de transformation enregistré dans le contexte de l'application. Notre classe de configuration d'application, développant l'
assistant natif Spring
WebMvcConfigurerAdapter , enregistrera automatiquement un service de conversion que nous pouvons configurer en ajoutant les outils de formatage nécessaires. Voyons à quoi ça ressemble à nouveau:
@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 Syntaxe du double crochet
Le service de conversion peut être facilement appliqué pour convertir / formater n'importe quel objet en chaîne. Cela se fait en utilisant la syntaxe d'expression à double parenthèse:
- Pour les expressions variables: $ {{...}}
- Pour exprimer un choix: * {{... ...}}
Ainsi, par exemple, étant donné le convertisseur Integer-to-String, qui ajoute des virgules comme séparateur de milliers, voici:
<p th:text="${val}">...</p> <p th:text="${{val}}">...</p>
... devrait se traduire par:
<p>1234567890</p> <p>1,234,567,890</p>
9.3 Utilisation dans les formulaires
Nous avons vu précédemment que chaque attribut
th: field appliquera toujours un service de transformation, donc ceci:
<input type="text" th:field="*{datePlanted}" />
... en fait équivalent à:
<input type="text" th:field="*{{datePlanted}}" />
Notez que selon l'exigence de Spring, c'est le seul scénario dans lequel le service de transformation est appliqué aux expressions utilisant la syntaxe à parenthèse simple.
9.4 Objet de conversion #conversions
L'objet utilitaire de conversion #conversions vous permet de démarrer manuellement le service de conversion si nécessaire:
<p th:text="${'Val: ' + #conversions.convert(val,'String')}">...</p>
La syntaxe de cet objet de service est:
- # conversions.convert (Object, Class) : convertit l'objet en la classe spécifiée
- # conversions.convert (Object, String) : le même que ci-dessus, mais avec la classe cible String (notez que le package java.lang. peut être omis)
10 Rendu de fragments du modèle Fragments de modèle (AJAX, etc.)
Thymeleaf offre la possibilité de ne rendre qu'une partie du modèle à la suite de son exécution:
fragment .
Cela peut être un outil de composantenement utile. Par exemple, il peut être utilisé sur des contrôleurs qui s'exécutent sur
des appels
AJAX qui peuvent renvoyer des fragments de la mise en page d'une page déjà chargée dans le navigateur (pour mettre à jour la sélection, activer / désactiver les boutons ...).
Le rendu fragmenté peut être réalisé à l'aide des spécifications d'
extraits de Thymeleaf: objets qui implémentent l'interface
org.thymeleaf.fragment.IFragmentSpec .
La plus courante de ces implémentations est
org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec , qui vous permet de spécifier un fragment à l'aide du sélecteur DOM de la même manière que ceux utilisés dans
th: include ou
th: replace .
10.1 Définition de fragments dans un bean de vue
Les beans vue sont les beans de la classe
org.thymeleaf.spring4.view.ThymeleafView déclarée dans le contexte de l'application (annotation
Bean si vous utilisez la configuration Java). Ils vous permettent de spécifier des fragments comme suit:
@Bean(name="content-part") @Scope("prototype") public ThymeleafView someViewBean() { ThymeleafView view = new ThymeleafView("index");
Étant donné la définition ci-dessus d'un bean, si notre contrôleur retourne une
partie de contenu (le nom du bean ci-dessus) ...
@RequestMapping("/showContentPart") public String showContentPart() { ... return "content-part"; }
... Thymeleaf ne renverra que le fragment de
contenu du modèle d'index - dont l'emplacement est susceptible d'être approximativement le même que
/WEB-INF/templates/index.html , après avoir appliqué le préfixe et le suffixe. Ainsi, le résultat sera complètement équivalent à spécifier
index :: content :
<!DOCTYPE html> <html> ... <body> ... <div th:fragment="content"> Only this div will be rendered! </div> ... </body> </html>
Notez également que grâce aux puissants sélecteurs de disposition Thymeleaf, nous pouvons sélectionner un fragment dans un modèle sans aucun attribut
th: fragment . Utilisons l'attribut
id , par exemple:
@Bean(name="content-part") @Scope("prototype") public ThymeleafView someViewBean() { ThymeleafView view = new ThymeleafView("index");
10.2 Définition de fragments dans la valeur de retour du contrôleur
Au lieu de déclarer des
beans de vue , des fragments peuvent être définis à partir du contrôleur à l'aide de la syntaxe des
expressions de fragment . Tout comme dans les attributs
th: insert ou
th: replace .
@RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: content"; }
Bien sûr, la pleine puissance des sélecteurs DOM est à nouveau disponible, nous pouvons donc sélectionner notre fragment en fonction d'attributs HTML standard, tels que
id = "content" :
@RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: #content"; }
Et nous pouvons également utiliser des paramètres tels que:
@RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: #content ('myvalue')"; }
11 Fonctions d'intégration avancées
11.1 Intégration avec RequestDataValueProcessor
Thymeleaf s'intègre de manière transparente à l'interface Spring
RequestDataValueProcessor . Cette interface vous permet d'intercepter des URL de lien, des URL de formulaire et des valeurs de champ de formulaire avant de les écrire dans le résultat du balisage, ainsi que d'ajouter de manière transparente des champs de formulaire masqués qui incluent des fonctionnalités de sécurité, telles que: protection contre CSRF (contrefaçon de demande intersite) .
L'implémentation de
RequestDataValueProcessor peut être facilement configurée dans le contexte de l'application. Il doit implémenter l'interface
org.springframework.web.servlet.support.RequestDataValueProcessor et avoir
requestDataValueProcessor comme nom de bean:
@Bean public RequestDataValueProcessor requestDataValueProcessor() { return new MyRequestDataValueProcessor(); }
... et Thymeleaf l'utilisera comme suit:
- th: href et th: src appellent RequestDataValueProcessor.processUrl (...) avant de rendre l'URL
- th: action appelle RequestDataValueProcessor.processAction (...) avant de rendre l'attribut action du formulaire, et en plus il détecte quand cet attribut est appliqué à la balise <form>, qui en tout cas devrait être le seul endroit, et dans ce cas appelle RequestDataValueProcessor.getExtraHiddenFields (... ) et ajoute les champs masqués retournés juste avant la balise de fermeture </form>
- th: value appelle RequestDataValueProcessor.processFormFieldValue (...) pour dessiner la valeur à laquelle il se réfère, sauf si th: field est dans la même balise (dans ce cas, th: field fera attention)
- th: field appelle RequestDataValueProcessor.processFormFieldValue (...) pour dessiner la valeur du champ auquel il s'applique (ou le corps de la balise s'il s'agit de <textarea>)
Notez qu'il existe très peu de scénarios dans lesquels vous auriez besoin d'implémenter explicitement RequestDataValueProcessor dans votre application. Dans la plupart des cas, il sera automatiquement utilisé par les bibliothèques de sécurité que vous utilisez de manière transparente, par exemple le CSRF de Spring Security.11.1 Création d'URI pour les contrôleurs
À partir de la version 4.1,
Spring offre la possibilité de créer des liens vers des contrôleurs annotés directement à partir des vues, sans avoir besoin de connaître les URI auxquels ces contrôleurs sont mappés.
Dans Thymeleaf, cela peut être réalisé en utilisant l'expression
# mvc.url (...) , qui vous permet de définir les méthodes de contrôleur en lettres majuscules de la classe de contrôleur dans laquelle elles se trouvent, suivi du nom de la méthode. Cela équivaut à la fonction
Spring: mvcUrlx (...) personnalisée dans JSP.
Par exemple, pour:
public class ExampleController { @RequestMapping("/data") public String getData(Model model) { ... return "template" } @RequestMapping("/data") public String getDataParam(@RequestParam String type) { ... return "template" } }
Le code suivant créera des références de méthode:
<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>
Vous pouvez lire sur ce mécanisme à
http://docs.spring.io/spring-framework/docs/4.1.2.RELEASE/spring-framework-reference/html/mvc.html#mvc-links-to-controllers- vues12 Intégration Spring WebFlow
Les packages d'intégration Thymeleaf + Spring incluent une intégration avec Spring WebFlow (2.3+).
WebFlow inclut certaines fonctionnalités AJAX pour le rendu de fragments de la page affichée lorsque certains événements (transitions) sont déclenchés, et pour que Thymeleaf puisse suivre ces demandes AJAX, nous devrons utiliser une autre implémentation de ViewResolver configurée comme suit:
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring4.view.AjaxThymeleafViewResolver"> <property name="viewClass" value="org.thymeleaf.spring4.view.FlowAjaxThymeleafView" /> <property name="templateEngine" ref="templateEngine" /> </bean>
... puis ce
ViewResolver peut être configuré dans
WebFlow ViewFactoryCreator comme:
<bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator"> <property name="viewResolvers" ref="thymeleafViewResolver"/> </bean>
De là, vous pouvez définir des modèles Thymeleaf dans votre état d'affichage:
<view-state id="detail" view="bookingDetail"> ... </view-state>
Dans l'exemple ci-dessus,
bookingDetail est un modèle Thymeleaf spécifié de la manière habituelle, compréhensible par n'importe lequel des résolveurs de modèle configurés dans TemplateEngine.
12.2 Extraits AJAX dans Spring WebFlow
Notez que cela explique uniquement comment créer des fragments AJAX à utiliser avec Spring WebFlow. Si vous n'utilisez pas WebFlow, la création d'un contrôleur Spring MVC qui répond à une demande AJAX et renvoie un morceau de HTML est aussi simple que la création de tout autre contrôleur qui renvoie un modèle, à la seule exception que vous êtes susceptible de renvoyer un fragment comme " main " :: admin "à partir de la méthode de votre contrôleur.WebFlow vous permet de définir le rendu via AJAX avec des balises <render>, par exemple comme ceci:
<view-state id="detail" view="bookingDetail"> <transition on="updateData"> <render fragments="hoteldata"/> </transition> </view-state>
Ces fragments (dans ce cas,
hoteldata ) peuvent être une liste séparée par des virgules de fragments indiqués dans le balisage avec
th: fragment :
<div id="data" th:fragment="hoteldata"> This is a content to be changed </div>
N'oubliez pas que ces extraits doivent avoir un attribut
id pour que les bibliothèques Spring Spring exécutées dans le navigateur puissent remplacer le balisage.
Vous pouvez également spécifier des balises <render> à l'aide des sélecteurs DOM:
<view-state id = "detail" view = "bookingDetail">
/>
</view-state>
... et cela signifie qu'il n'y a pas besoin de
th: fragment :
<div id="data"> This is a content to be changed </div>
Quant au code qui déclenche la transition
updateData , il ressemble à ceci:
<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>