📜 ⬆️ ⬇️

Guide: Thymeleaf + Spring. Part 3

First part
The second part of

7 Check and error messages


Most of our forms should display validation messages to inform the user about errors he made.

Thymeleaf offers several tools for this: several functions in the #fields object, attributes to th: errors and th: errorclass .

7.1 Field errors


Let's see how we can set a specific CSS class for a field if it contains an error:

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

As you can see, the # fields.hasErrors (...) function takes a field expression as a parameter ( datePlanted ) and returns a Boolean value indicating whether there are any validation errors for this field.

We can also get all the errors for this field and repeat them:

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

Instead of iterating, we could also use th: errors , a specialized attribute that creates a list with all the errors for the specified selector, separated by <br />:

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

Error-based CSS style simplification: th: errorclass

The example we saw above, setting the CSS class for an input form, if there are errors in this field, is so common that Thymeleaf offers a special attribute for precise execution: th: errorclass .

Applied to a form field tag (input, select, textarea ...), it will read the name of the field to be checked from any existing name or th: field attributes in the same tag, and then add the specified CSS class to the tag, if such a field has any related errors:

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

If there are errors in datePlanted , it will look like this:

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

7.2 All errors


And what if we want to show all the errors in the form? We just need to request the methods # fields.hasErrors (...) and # fields.errors (...) with the constants ' * ' or ' all ' (which are equivalent):

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

As in the examples above, we could get all the errors and iterate on them ...

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

... and also create a divided list:

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

Finally, notice that # fields.hasErrors ('*') is equivalent to # fields.hasAnyErrors () , and # fields.errors ('*') is equivalent to # fields.allErrors () . Use the syntax you prefer:

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

7.3 Global Errors


There is a third type of error in the Spring form: global errors. These are errors that are not related to any particular fields in the form, but still exist.
Thymeleaf offers a global constant to access these errors:

 <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 also equivalent helper methods # fields.hasGlobalErrors () and # fields.globalErrors () :

7.4 Mapping errors out of form


Form validation errors can also be displayed out of the form using variables ( $ {...} ) instead of expression expressions ( * {...} ) and the prefix of the name of the component that supports the form:

 <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 Objects


Thymeleaf offers the ability to retrieve form error information in the form of beans (instead of simple strings) with the attributes fieldName (String), message (String), and global (boolean).

These errors can be obtained using the service method # 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 This is still a prototype!


Our application is ready. But let's take another look at the .html page we created ...

One of the most enjoyable consequences of working with Thymeleaf is that after all these features that we added to our HTML, we can still use this HTML as a prototype (we say it is the Natural Template ). Let's open seedstartermng.html right in our browser, without launching our application:

image

Here it is! This is not a working application, this is not real data ... but this is a completely correct prototype composed of beautifully displayed HTML code.

9 The Conversion Service


9.1 Configuration


As explained earlier, Thymeleaf can use the Transformation Service registered in the context of the application. Our application configuration class, expanding Spring’s own WebMvcConfigurerAdapter , will automatically register a conversion service that we can customize by adding the necessary formatting tools. Let's see what it looks like:

 @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 Double bracket syntax


The conversion service can be easily applied to convert / format any object into a string. This is done using the expression in double brackets:


So, for example, given the Integer-to-String converter, which adds commas as the thousands separator, this is:

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

... should lead to:

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

9.3 Forms Use


We saw earlier that each th: field attribute will always apply a translation service, so this is:

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

... is actually equivalent to:

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

Note that according to the Spring requirement, this is the only scenario in which the mapping service is used in expressions using the single-bracket syntax.

9.4 #conversions transform object


The #conversions utility object allows you to manually start the conversion service where needed:

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

The syntax for this service object is:


10 Drawing Fragments of the Template Fragments Template (AJAX etc)


Thymeleaf offers the ability to visualize only part of a template as a result of its execution: a fragment .

This can be a useful component engine tool. For example, it can be used on controllers that run on AJAX calls, which can return page markup fragments that have already been loaded into the browser (to update the selection, turn on / off buttons ...).

Fragmentary rendering can be achieved using Thymeleaf fragment specifications: objects that implement the org.thymeleaf.fragment.IFragmentSpec interface.

The most common of these implementations is org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec , which allows you to specify a fragment using a DOM selector in exactly the same way as those used in th: include or th: replace .

10.1 Definition of fragments in a view bean


View beans are beans of the org.thymeleaf.spring4.view.ThymeleafView class declared in the context of the application ( Bean annotation if you are using Java configuration). They allow you to specify fragments as follows:

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

Given the above definition of a bean component, if our controller returns a content-part (the name of the above-mentioned bean component) ...

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

... Thymeleaf will return only the content fragment of the index template - the location of which is likely to be approximately the same as /WEB-INF/templates/index.html , after applying the prefix and suffix. Thus, the result will be completely equivalent to the indication of index :: content :

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

Also note that thanks to Thymeleaf's powerful markup selectors, we can select a fragment in a template without any th: fragment attributes. Let's use the id attribute, for example:

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

10.2 Definition of fragments in the return value of the controller


Instead of declaring view beans , fragments can be defined from the controller using fragment expressions . Just like in th: insert or th: replace attributes.

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

Of course, all the power of DOM selectors is available again, so we can select our snippet based on standard HTML attributes, such as id = "content" :

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

And we can also use parameters such as:

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

11 Advanced integration features


11.1 Integration with RequestDataValueProcessor


Thymeleaf easily integrates with Spring RequestDataValueProcessor interface. This interface allows you to intercept link URLs, form URLs and form field values ​​before they are written to the markup result, and also transparently add hidden form fields that include security features, such as: protection against CSRF (cross-site request forgery) .

The RequestDataValueProcessor implementation can be easily configured in the context of the application. It must implement the org.springframework.web.servlet.support.RequestDataValueProcessor interface and have the requestDataValueProcessor as the name of the bean:

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

... and Thymeleaf will use this as follows:


Note that there are very few scenarios in which you would need to explicitly implement RequestDataValueProcessor in your application. In most cases, this will be automatically used by security libraries that you use transparently, for example, for example, Spring Security's CSRF.

11.1 Building URIs to Controllers


Starting with version 4.1, Spring allows you to create links to annotated controllers directly from the views, without having to know the URIs to which these controllers are mapped.

In Thymeleaf, this can be achieved using the expression # mvc.url (...) , which allows you to specify controller methods in capital letters of the controller class in which they are located, followed by the method name. This is equivalent to the user-defined spring: mvcUrlx (...) function in JSP.

For example, for:

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

The following code will create method references:

 <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> 

You can read about this mechanism at http://docs.spring.io/spring-framework/docs/4.1.2.RELEASE/spring-framework-reference/html/mvc.html#mvc-links-to-controllers- from-views

12 Spring WebFlow Integration


Thymeleaf + Spring integration packages include integration with Spring WebFlow (2.3+).

WebFlow includes some AJAX features for rendering fragments of the rendered page when certain events (transitions) are triggered, and in order for Thymeleaf to track these AJAX requests, we will need to use another ViewResolver implementation configured as:

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

... and then this ViewResolver can be configured in WebFlow ViewFactoryCreator as:

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

From here, you can define Thymeleaf templates in your view-state's:

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

In the above example, bookingDetail is a Thymeleaf template specified in the usual way, understandable to any of the template resolvers configured in TemplateEngine.

12.2 AJAX Fragments in Spring WebFlow


Please note that this only explains how to create AJAX fragments for use with Spring WebFlow. If you are not using WebFlow, creating a Spring MVC controller that responds to an AJAX request and returns a piece of HTML is as simple as creating any other controller that returns a template, with the only exception that you will probably return a snippet like " main :: admin "from your controller method.

WebFlow allows you to define rendering via AJAX with <render> tags, like this:

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

These fragments (in this case hoteldata ) can be a comma-separated list of fragments specified in the markup using the th: fragment :

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

Always remember that these snippets must have an id attribute so that Spring JavaScript libraries running in the browser can replace markup.

<Render> tags can also be specified using DOM selectors:

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

</ view-state>

... and this means that there is no need for th: fragment :

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

As for the code that launches the updateData transition, it looks like this:

 <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/437658/