0% found this document useful (0 votes)
14 views34 pages

Tutorial: Thymeleaf + Spring

This tutorial provides a comprehensive guide on integrating Thymeleaf with the Spring Framework, particularly Spring MVC, detailing the necessary libraries and configurations required. It covers the use of the SpringStandard dialect, view resolvers, and template engines, along with code examples for setting up Thymeleaf in a Spring application. Additionally, it introduces a practical application example, the Spring Thyme Seed Starter Manager, to illustrate the integration process.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views34 pages

Tutorial: Thymeleaf + Spring

This tutorial provides a comprehensive guide on integrating Thymeleaf with the Spring Framework, particularly Spring MVC, detailing the necessary libraries and configurations required. It covers the use of the SpringStandard dialect, view resolvers, and template engines, along with code examples for setting up Thymeleaf in a Spring application. Additionally, it introduces a practical application example, the Spring Thyme Seed Starter Manager, to illustrate the integration process.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 34

01/08/2020 Tutorial: Thymeleaf + Spring

Thymeleaf

Tutorial: Thymeleaf + Spring


Document version: 20181029 - October 29, 2018
3.0.11.RELEASE
Project site:https://www.thymeleaf.org

The provided link is not translatable text. 1/34


01/08/2020 Tutorial: Thymeleaf + Spring

Preface
This tutorial explains how Thymeleaf can be integrated with the Spring Framework, especially (but not only) Spring
MVC.

Note that Thymeleaf has integrations for versions 3.x and 4.x of the Spring Framework, provided by two libraries.
separate callsthymeleaf-spring3ethymeleaf-spring4These libraries are packaged in.jarfiles
separated (thymeleaf-spring3-{version}.jare thymeleaf-spring4-{version}.jar) and need to be added to your
class path to use Thymeleaf Spring integrations in your application.

The code samples and the application example in this tutorial use Spring 4.x and its Thymeleaf integrations.
correspondents, but the content of this text is also valid for Spring 3.x. If your application uses Spring 3.x, all of the
what you need to do is replace theorg.thymeleaf.spring4packageorg.thymeleaf.spring3in the code examples.

Unable to access external content. 2/34


01/08/2020 Tutorial: Thymeleaf + Spring

1 Integrating Thymeleaf with Spring


Thymeleaf offers a set of Spring integrations that allow it to be used as a complete substitute for JSP in
Spring MVC applications.

These integrations allow:

Make the methods mapped in theControllerobjects of Spring MVC are forwarded to the models
managed by Thymeleaf, just like you do with JSPs.
Use Spring Expression Language (Spring EL) instead of OGNL in your templates.
Create forms in your models fully integrated with your form backup beans and links.
results, including the use of property editors, conversion services, and validation error handling.
Display internationalization messages from message files managed by Spring (through the
MessageSourceusual objects.
Solve your models using the resource resolution mechanisms of Spring.

Note that in order to fully understand this tutorial, you must first have read the tutorial 'Using Thymeleaf'.
Explain the Standard Dialect in depth.

Unable to access the provided URL. 3/34


01/08/2020 Tutorial: Thymeleaf + Spring

The SpringStandard dialect


To achieve easier and better integration, Thymeleaf provides a dialect that specifically implements all the
necessary resources for it to work correctly with Spring.

This specific dialect is based on the Thymeleaf Standard Dialect and is implemented in a class called
org.thymeleaf.spring4.dialect.SpringStandardDialect, which actually extends from
org.thymeleaf.standard.StandardDialect.

In addition to all the features already present in the Standard Dialect - and therefore inherited - the SpringStandard Dialect presents the
specific resources:

Use Spring Expression Language (Spring EL or SpEL) as a variable expression language, instead of OGNL.
Consequently, all expressions${...} e*{...} will be evaluated by the expressions language mechanism
from Spring. Note also that support for the Spring EL compiler is available (Spring 4.2.4+).
Access any beans in the application context using SpringEL syntax:${@myBean.doSomething()}
New attributes for form processing:th:field, th:errorsanderrorclass, in addition to a new
implementation of theth:objectwhich allows it to be used for form command selection.
An object and method of expression#themes.code(...), which is equivalent tospring:themecustom JSP tag.
An object and method of expression#mvc.uri(...), which is equivalent tospring:mvcUrl(...)custom JSP function
(only on Spring 4.1+).

Note that most of the time you should not use this dialect directly in aTemplate Enginenormal object like
part of its configuration. Unless you have very specific Spring integration needs, you should be a
creation of an instance of a new model engine class that executes all the necessary configuration steps
automaticallyorg.thymeleaf.spring4.SpringTemplateEngine.

An example of bean configuration:

@Bean
publicSpringResourceTemplateResolver templateResolver(){
SpringResourceTemplateResolver automatically integrates with Spring's own
resource resolution infrastructure, which is highly recommended.
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
// HTML is the default value, added here for the sake of clarity.
templateResolver.setTemplateMode(TemplateMode.HTML);
// Template cache is true by default. Set to false if you want
// templates to be automatically updated when modified.
templateResolver.setCacheable(true);
returntemplateResolver;
}

@Bean
publicSpringTemplateEngine templateEngine()
// SpringTemplateEngine automatically applies SpringStandardDialect and
Enables Spring's own MessageSource message resolution mechanisms.
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
Enabling the SpringEL compiler with Spring 4.2.4 or newer can
// speed up execution in most scenarios, but might be incompatible
with specific cases when expressions in one template are reused
// across different data types, so this flag is "false" by default
// for safer backwards compatibility.
templateEngine.setEnableSpringELCompiler(true);
returntemplateEngine;
}

Or, using the XML-based configuration of Spring:

Unable to access the content of the provided link. 4/34


01/08/2020 Tutorial: Thymeleaf + Spring

SpringResourceTemplateResolver automatically integrates with Spring's own


resource resolution infrastructure, which is highly recommended. -->
<bean id="templateResolver"
class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver"
prefix
suffix
HTML is the default value, added here for the sake of clarity. -->
templateMode
Template cache is true by default. Set to false if you want -->
templates to be automatically updated when modified. -->
cacheable
</bean>

SpringTemplateEngine automatically applies SpringStandardDialect and -->


Enables Spring's own MessageSource message resolution mechanisms. -->
<bean id="templateEngine"
class="org.thymeleaf.spring4.SpringTemplateEngine"
templateResolver
Enabling the SpringEL compiler with Spring 4.2.4 or newer can speed up
execution in most scenarios, but might be incompatible with specific -->
cases when expressions in one template are reused across different data
Types, so this flag is "false" by default for safer backwards -->
<!-- compatibility. -->
enableSpringELCompiler
</bean>

Unable to access the content of the provided URL for translation. 5/34
01/08/2020 Tutorial: Thymeleaf + Spring

3 views and display resolvers

3.1 Displays and resolver displays in Spring MVC

There are two interfaces in Spring MVC that comply with the core of its model system:

org.springframework.web.servlet.View
org.springframework.web.servlet.ViewResolver

Visualize the template pages in our applications and allows us to modify and predict their behavior by defining them.
like beans. The views are responsible for rendering the actual HTML interface, usually by executing some
model mechanism like Thymeleaf.

ViewResolvers are the objects responsible for obtaining View objects for a specific operation and location.
Normally, controllers ask the ViewResolvers to forward to a view with a specific name (a
String returned by the controller method) and then all view resolvers in the application are executed
in an ordered chain until one of them can resolve this display, in which case a View object is returned and the control
be passed to the HTML rendering.

Note that not all pages in our applications need to be defined as Views, but only those whose
We desire behavior that is not standard or configured in a specific way (for example, connecting
some special beans to it). If a ViewResolver is requested for a view that does not have a bean.
correspondent, which is the common case, a new View object will be created ad hoc and returned.

A typical configuration for a JSP + JSTL ViewResolver in a Spring MVC application from the past was like this:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
viewClass
prefix
suffix
order
viewNames
</bean>

A quick look at its properties is enough to know how it was configured:

viewClassestablishes the class of display instances. This is necessary for a JSP resolver, but will not be
necessary when we are working with Thymeleaf.
prefixandsuffixwork similarly to the attributes with the same names in the TemplateResolver objects
Thymeleaf.
orderestablishes the order in which the ViewResolver will be consulted in the chain.
viewNamesallows the definition (with wildcard characters) of the display names that will be resolved by this
ViewResolver.

3.2 Displays and resolver displays in Thymeleaf

Thymeleaf offers implementations for the two interfaces mentioned above:

org.thymeleaf.spring4.view.ThymeleafView
org.thymeleaf.spring4.view.ThymeleafViewResolver

These two classes will be responsible for processing the Thymeleaf models as a result of the execution of the
controllers.

Unable to access the content of the provided URL. 6/34


01/08/2020 Tutorial: Thymeleaf + Spring
The configuration of the Thymeleaf View Resolver is very similar to that of JSP:

@Bean
publicThymeleafViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
// NOTE 'order' and 'viewNames' are optional
viewResolver.setOrder(1);
viewResolver.setViewNames(new String[] {".html", ".xhtml"});
returnviewResolver;
}

... Or in XML:

<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
templateEngine
NOTE 'order' and 'viewNames' are optional
1
viewNames
</bean>

ThetemplateEnginethe parameter is, obviously, theSpringTemplateEngineobject that we defined in the previous chapter. The others
twoorderandviewNames) are optional and have the same meaning as in the JSP ViewResolver we saw earlier.

Note that we don't needprefixorsuffixparameters, because they are already specified in the Model Resolver
(which in turn is passed to the Model Mechanism).

And if we wanted to define aViewCome and add some static variables? Easy, just define a prototype of bean to
he:

@Bean
@Scope("prototype")
publicThymeleafView mainView() {
ThymeleafView view = new ThymeleafView("main"); // templateName = 'main'
view.setStaticVariables(
The ACME Fruit Company
returnview;
}

By doing this, you will be able to run specifically this display bean, selecting it by the name of the bean.mainView,
in this case)

Unable to access or translate URLs. 7/34


01/08/2020 Tutorial: Thymeleaf + Spring

Thyme seed initiation manager of 4 springs


The source code of the examples shown in this and in future chapters of this guide can be found in the repository of
on GitHubSpring Thyme Seed Starter Manager.

4.1 The Concept

At Thymeleaf, we are big fans of thyme, and every spring we prepare our seed starter kits with good soil and
Our favorite seeds, we placed under the Spanish sun and patiently wait for our new plants to grow.

But this year we got tired of sticking labels on the beginner containers to know which seed was in each cell of
container, so we decided to prepare an application using Spring MVC and Thymeleaf to help us catalog our
beginners: The Spring Thyme SeedStarter Manager.

First page of the STSM

In a similar way to the Good Thymes Virtual Grocery app that we developed in the tutorial Using Thymeleaf, the STSM
will allow us to exemplify the most important aspects of integrating Thymeleaf as a templating engine for the
Spring MVC.

The provided link does not contain translatable text. 8/34


01/08/2020 Tutorial: Thymeleaf + Spring

4.2 Business Layer

We will need a very simple business layer for our application. First of all, let's take a look at
our model entities:

STSM Model

Some very simple service classes will provide the necessary business methods. Like:

Service
public class SeedStarterService {

@Autowired
privateSeedStarterRepository seedstarterRepository;

public List<SeedStarter> findAll() {


return this.seedstarterRepository.findAll();
}

public void add(final SeedStarter seedStarter) {


this.seedstarterRepository.add(seedStarter);
}

@Service
public classVarietyService {

@Autowired
privateVarietyRepository varietyRepository;

public List<Variety> findAll() {


return this.varietyRepository.findAll();
}

publicVariety findById(final Integer id) {


return this.varietyRepository.findById(id);
}

}
Invalid input. Please provide text to translate. 9/34
01/08/2020 Tutorial: Thymeleaf + Spring

4.3 Spring MVC Configuration

Next, we need to define the Spring MVC configuration for the application, which will include not only the standard artifacts
from Spring MVC, such as resource handling or annotation scanning, but also the creation of Template instances
Engine and the View Resolver.

@Configuration
Enable Web MVC
Component Scan
public class SpringWebConfig
extends WebMvcConfigurerAdapter implements ApplicationContextAware {

privateApplicationContext applicationContext;

publicSpringWebConfig() {
super();
}

public void setApplicationContext(final ApplicationContext applicationContext)


throwsBeansException {
this.applicationContext = applicationContext;
}

/* ******************************************************************* */
GENERAL CONFIGURATION ARTIFACTS */
Static Resources, i18n Messages, Formatters (Conversion Service) */
/* ******************************************************************* */

@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
registry.addResourceHandler("/images/**").addResourceLocations("/images/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
}

@Bean
publicResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("Messages");
returnmessageSource;
}

@Override
public void addFormatters(final FormatterRegistry registry) {
super.addFormatters(registry);
registry.addFormatter(varietyFormatter());
registry.addFormatter(dateFormatter());
}

@Bean
publicVarietyFormatter varietyFormatter() {
return newVarietyFormatter();
}

@Bean
publicDateFormatter dateFormatter() {
return newDateFormatter();
}

The provided text is a URL and cannot be translated directly. 10/34


01/08/2020 Tutorial: Thymeleaf + Spring

/* **************************************************************** */
/* THYMELEAF-SPECIFIC ARTIFACTS */
/* TemplateResolver <- TemplateEngine <- ViewResolver */
/* **************************************************************** */

@Bean
publicSpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver automatically integrates with Spring's own
resource resolution infrastructure, which is highly recommended.
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
HTML is the default value, added here for the sake of clarity.
templateResolver.setTemplateMode(TemplateMode.HTML);
// Template cache is true by default. Set to false if you want
// templates to be automatically updated when modified.
templateResolver.setCacheable(true);
returntemplateResolver;
}

@Bean
public SpringTemplateEngine templateEngine() {
// SpringTemplateEngine automatically applies SpringStandardDialect and
// enables Spring's own MessageSource message resolution mechanisms.
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
Enabling the SpringEL compiler with Spring 4.2.4 or newer can
// Speed up execution in most scenarios, but might be incompatible
with specific cases when expressions in one template are reused
across different data types, so this flag is "false" by default
// for safer backwards compatibility.
templateEngine.setEnableSpringELCompiler(true);
returntemplateEngine;
}

@Bean
publicThymeleafViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
returnviewResolver;
}

4.4 The Controller

Obviously, we will also need a controller for our application. Since the STSM will contain only one page of
web with a list of beginners and a form to add new ones, we will write only one controller class for
all server interactions:

@Controller
public class SeedStarterMngController {

@Autowired
privateVarietyService varietyService;

@Autowired
privateSeedStarterService seedStarterService;

...

}
https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html 11/34
01/08/2020 Tutorial: Thymeleaf + Spring

Now let's see what we can add to this controller class.

Model attributes

First, we will add some model attributes that we will need on the page:

allTypes
public List<Type> populateTypes() {
returnArrays.asList(Type.ALL);
}

@ModelAttribute("allFeatures")
public List<Feature> populateFeatures() {
returnArrays.asList(Feature.ALL);
}

@ModelAttribute("allVarieties")
publicList<Variety> populateVarieties() {
return this.varietyService.findAll();
}

@ModelAttribute("allSeedStarters")
publicList<SeedStarter> populateSeedStarters() {
return this.seedStarterService.findAll();
}

Mapped methods

And now the most important part of a controller, the mapped methods: one to show the form page and another
to process the addition of newSeedStarterobjects.

/, /seedstartermng
public String showSeedstarters(final SeedStarter seedStarter) {
seedStarter.setDatePlanted(Calendar.getInstance().getTime());
seedstartermng
}

@RequestMapping(value="/seedstartermng", params={"save"})
publicString saveSeedstarter(
finalSeedStarter seedStarter, finalBindingResult bindingResult, finalModelMap model) {
if(bindingResult.hasErrors()) {
return "seedstartermng";
}
this.seedStarterService.add(seedStarter);
model.clear();
return"redirect:/seedstartermng";
}

4.5 Configuring a conversion service

To allow easy formattingDateand also of theVarietyobjects in our visualization layer, we configured our
app for aConversionServicethe Spring object would be created and initialized (as per
WebMvcConfigurerAdapterWe extend) with some formatting objects that we will need. See again:

@Override
public void addFormatters(final FormatterRegistry registry) {
super.addFormatters(registry);
registry.addFormatter(varietyFormatter());
Unable to access the specified URL. Please provide the text to be translated. 12/34
01/08/2020 Tutorial: Thymeleaf + Spring
registry.addFormatter(dateFormatter());
}

@Bean
publicVarietyFormatter varietyFormatter() {
return newVarietyFormatter();
}

@Bean
publicDateFormatter dateFormatter() {
return newDateFormatter();
}

Spring Formatters are implementations of theorg.springframework.format.Formatterinterface. For more information


about how the Spring conversion infrastructure works, consult the documents atspring.io.

Let's take a look atDateFormatter, which formats dates according to a format string present in the
date.formatmessage key of ourMessages.properties:

public class DateFormatter implements Formatter<Date> {

@Autowired
privateMessageSource messageSource;

publicDateFormatter() {
super();
}

publicDate parse(finalString text, finalLocale locale) throws ParseException {


finalSimpleDateFormat dateFormat = createDateFormat(locale);
returndateFormat.parse(text);
}

public String print(final Date object, final Locale locale) {}


finalSimpleDateFormat dateFormat = createDateFormat(locale);
returndateFormat.format(object);
}

private SimpleDateFormat createDateFormat(final Locale locale) {


this.messageSource.getMessage("date.format", null, locale);
finalSimpleDateFormat dateFormat = newSimpleDateFormat(format);
dateFormat.setLenient(false);
return date format
}

OVarietyFormatterautomatically convert between ourVarietyentities and the way we want to use them in
our forms (basically, by theidfield values:

public class VarietyFormatter implements Formatter<Variety> {}

@Autowired
privateVarietyService varietyService;

publicVarietyFormatter() {
super();
}

publicVariety parse(finalString text, finalLocale locale) throws ParseException {


finalInteger varietyId = Integer.valueOf(text);
return this.varietyService.findById(varietyId);
}

Unable to access the content of the provided URL. 13/34


01/08/2020 Tutorial: Thymeleaf + Spring

publicString print(finalVariety object, finalLocale locale) {


return(object != null ? object.getId().toString() : "");
}

We will learn more about how these formatters affect the way our data is displayed later.

Unable to access content from the provided URL. 14/34


August 1, 2020 Tutorial: Thymeleaf + Spring

5 Listing initial seed data


The first thing that our/WEB-INF/templates/seedstartermng.htmlthe page will show a list of beginners from
seeds currently stored. For this, we will need some externalized messages and also some
evaluation of expression in the model's attributes. Like this:

<div class="seedstarterlist" th:unless="${#lists.isEmpty(allSeedStarters)}">

List of Seed Starters

<table>
<thead>
<tr>
Date Planted
Covered
Type
Features
Rows

<trth:each="sb : ${allSeedStarters}">
13/01/2011
yes
Wireframe

#messages.arrayMsg(
#strings.arrayPrepend(sb.features,'seedstarter.feature.')),
Electric Heating, Turf
<td>
<table>

<trth:each="row,rowStat : ${sb.rows}">
1
Thyme Thyme
12
</tr>

</tr>

There's much to see here. Let's take a look at each fragment separately.

First of all, this section will only be shown if there is any initial match. We achieve this with a th: unless
attribute and the#lists.isEmpty(...)function.

<div class="seedstarterlist" th:unless="${#lists.isEmpty(allSeedStarters)}">

Note that all utility objects such as#listsare available in Spring EL expressions in the same way as
they were in the OGNL expressions in the standard Dialect.

The next thing to see is many internationalized (externalized) texts, such as:

List of Seed Starters

<table>
<thead>

Unable to access the content of the provided URL. 15/34


01/08/2020 Tutorial: Thymeleaf + Spring
<tr>
Date Planted
Covered
Type
Features
Rows
...

As it is a Spring MVC application, we have already defined aMessageSourcebean


in our Spring configuration (the
MessageSourceobjects are the standard way to manage externalized texts in Spring MVC:

@Bean
publicResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("Messages");
returnmessageSource;
}

... And thisbasenameproperty indicates that we will have files such asMessages_es.properties orMessages_en.propertiesin
our class path. Let's take a look at the Spanish version:

Seedbed List

dd/MM/yyyy
yes
no

Planting date
Covered
Type
Features
Rows

Wood
Plastic

Specific substrate for seed starters


Fertilizer
pH Corrector

In the first column of the table list, we will show the date when the seed initiator was prepared. But we
we will show formatted in the way we define in ourDateFormatterTo do this, we will use bracket syntax
duplo${{...}} ), which will automatically apply the Spring Conversion Service, including theDateFormatterwhat we register
in the configuration.

13/01/2011

Next, we show whether the seed starter container is covered or not, transforming the value of the property.
boolean of the covered bean in an internationalized "yes" or "no" with a literal replacement expression:

yes

Now we have to show the type of seed starter container. Type is a Java enumeration with two values (
WOODePLASTIC), and that is why we define two properties in ourMessagesfile called
seedstarter.type.WOODandPLASTIC.

But, to obtain the internationalized names of the types, we will need to add theseedstarter.type.prexo to the value of
enumeration through an expression, the result of which we will use as the message key:

Wireframe

The provided link does not contain translatable text. 16/34


01/08/2020 Tutorial: Thymeleaf + Spring
The most difficult part of this listing is the resources column. In it, we want to display all the resources of our container - which
come in the form of a matrix ofFeatureenumerations -, separated by commas. Like "Electric heating, grass".

Note that this is particularly difficult because these enum values also need to be externalized, such as zemos
with Types. The flow is then:

1. Attach the corresponding attachment price to all elements of thefeaturesmatrix.


2. Obtain the externalized messages corresponding to all the keys from step 1.
3. Combine all the messages obtained in step 2, using a comma as a delimiter.

To achieve this, we created the following code:

<tdth:text="${#strings.arrayJoin(
Invalid input
#strings.arrayPrepend(sb.features,'seedstarter.feature.')
Electric Heating, Turf

The last column of our listing will be quite simple. Even if there is a nested table to show the content of
each line in the container:

<td>
<table>
<tbody>
<trth:each="row,rowStat : ${sb.rows}">
1
Thymus Thymi
12
</tr>
</tbody>
</table>
</td>

The provided text is a URL and cannot be translated. 17/34


August 1, 2020 Tutorial: Thymeleaf + Spring

6 Creating a form

6.1 Manipulating the command object

Command object is the name that Spring MVC assigns to form backup beans, that is, to the objects that model
the fields of a form and provide getter and setter methods that will be used by the framework to establish and obtain
the values entered by the user on the client side.

Thymeleaf requires you to specify the command object using ath:objectattribute in your<form>tag

#
...

This is consistent with other uses,th:object,but, in fact, this specific scenario adds some limitations to
correctly integrate into the Spring MVC infrastructure:

The values forth:objectattributes in form tags must be variable expressions (${...}) specifying
just the name of a model attribute, without property navigation. This means that an expression like
${seedStarter}it is valid, but${seedStarter.data}it wouldn't be.
Once inside the<form>tag, no otherth:objectattribute can be specified. This is consistent with the fact that
that HTML forms cannot be nested.

6.2 Entries

Let's see now how to add an entry to our form:

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

As you can see, we are introducing a new attribute here:th:field.This is a very important resource for the
Spring MVC integration because it does all the heavy lifting of binding your input to a property on the backing bean
of the form. You can see it as an equivalent of the path attribute in a tag from the Spring MVC tag library.

Theth:fieldthe attribute behaves differently depending on being attached to a tag<input>, <select>or


(and also depending on the specific type oftag). In this case (input[type=text]), the line of code
above is similar to:

<inputtype="text"id="datePlanted"name="datePlanted"th:value="*{datePlanted}" />

... But, in fact, it's a little more than that, becauseth:fieldwill also apply the spring conversion service
registered, including theDateFormatterwhat we saw earlier (even if the field expression is not among
double brackets). Thanks to this, the date will be displayed correctly formatted.

The values forth:fieldattributes should be selection expressions (*{...}), which makes sense, as they will be evaluated
no backup bean of form and not in the context variables (or model attributes in Spring MVC jargon).

Contrary to what is insideth:objectthese expressions may include property navigation (in fact, any
allowed expression for the path attribute of a<form:input>JSP tag will be allowed here).

It should be noted thatth:fieldalso includes the new types of<input>element introduced by HTML5 as<input
type="datetime" ... />, <input type="color" ... />etc., effectively adding full HTML5 support for
Spring MVC.

The content at the provided URL is not accessible for translation. 18/34
01/08/2020 Tutorial: Thymeleaf + Spring

Now let's see what we can add to this controller class.

Model attributes

First, we will add some model attributes that we will need on the page:

allTypes
public List<Type> populateTypes() {
returnArrays.asList(Type.ALL);
}

@ModelAttribute("allFeatures")
public List<Feature> populateFeatures() {
returnArrays.asList(Feature.ALL);
}

@ModelAttribute("allVarieties")
publicList<Variety> populateVarieties() {
return this.varietyService.findAll();
}

@ModelAttribute("allSeedStarters")
publicList<SeedStarter> populateSeedStarters() {
return this.seedStarterService.findAll();
}

Mapped methods

And now the most important part of a controller, the mapped methods: one to show the form page and another
to process the addition of newSeedStarterobjects.

/, /seedstartermng
public String showSeedstarters(final SeedStarter seedStarter) {
seedStarter.setDatePlanted(Calendar.getInstance().getTime());
seedstartermng
}

@RequestMapping(value="/seedstartermng", params={"save"})
publicString saveSeedstarter(
finalSeedStarter seedStarter, finalBindingResult bindingResult, finalModelMap model) {
if(bindingResult.hasErrors()) {
return "seedstartermng";
}
this.seedStarterService.add(seedStarter);
model.clear();
return"redirect:/seedstartermng";
}

4.5 Configuring a conversion service

To allow easy formattingDateand also of theVarietyobjects in our visualization layer, we configured our
app for aConversionServicethe Spring object would be created and initialized (as per
WebMvcConfigurerAdapterWe extend) with some formatting objects that we will need. See again:

@Override
public void addFormatters(final FormatterRegistry registry) {
super.addFormatters(registry);
registry.addFormatter(varietyFormatter());
Unable to access the specified URL. Please provide the text to be translated. 12/34
01/08/2020 Tutorial: Thymeleaf + Spring

correspondents.

6.4 Option button fields

The radio button fields are specified similarly to non-boolean checkboxes (with multiple
values) - except that they don't have multiple values, of course:

<ul>
<lith:each="ty : ${allTypes}">
<input type="radio" th:field="*{type}" th:value="${ty}" />
Wireframe

6.5 Drop-down selectors / list

The selection fields have two parts: the<select>brand and its<option>nested tags. When creating this type of field,
just a<select>tag needs to include ath:fieldattribute, but theth:valueattributes in the<option>nested tags will be
very important because they will provide the means to know what the currently selected option is (similarly to
non-boolean checkboxes and radio buttons.

Let's recreate the type field as a dropdown:

<select th:field="*{type}">
<option th:each="type : ${allTypes}"
th:value="${type}"
Wireframe

At this point, understanding this piece of code is quite easy. Note how the attribute precedence allows us to define the
th:eachattribute no<option>own tag.

6.6 Dynamic fields

Thanks to the advanced form field binding features in Spring MVC, we can use complex expressions from
Spring EL to bind dynamic form fields to our form backup bean. This will allow us to create
new Row objects in ourSeedStartercome and add the fields of these lines to our form,
user request.

To do this, we will need some new methods mapped in our controller, which will add or remove a
line of ours,SeedStarterdepending on the existence of specific request parameters:

@RequestMapping(value="/seedstartermng", params={"addRow"})
public String addRow(final SeedStarter seedStarter, final BindingResult bindingResult) {
seedStarter.getRows().add(newRow());
seedstartermng
}

@RequestMapping(value="/seedstartermng", params={"removeRow"})
publicString removeRow(
finalSeedStarter seedStarter, finalBindingResult bindingResult,
finalHttpServletRequest req) {
finalInteger rowId = Integer.valueOf(req.getParameter("removeRow"));
seedStarter.getRows().remove(rowId.intValue());
seedstartermng
}

Sorry, I cannot access external links. 20/34


01/08/2020 Tutorial: Thymeleaf + Spring

And now we can add a pivot table to our form:

<table>
<thead>
<tr>
Row
Variety
Seeds per cell
<th>
Add row
</th>

<tbody>
<trth:each="row,rowStat : *{rows}">
1

<select th:field="*{rows[__${rowStat.index}__].variety}">
<option th:each="var : ${allVarieties}"
th:value="${var.id}"
Thymus Thymi
</select>
</td>
<td>
<input type="text" th:field="*{rows[__${rowStat.index}__].seedsPerCell}" />
</td>
<td>
<button type="submit" name="removeRow">
Remove row

Many things to see here, but not much that we shouldn't understand so far... except for one.strangething

<selectth:field="*{rows[__${rowStat.index}__].variety}">

...

If you remember the tutorial 'Using Thymeleaf', this__${...}__syntax is a pre-processing expression, which is
an internal expression that is evaluated before actually evaluating the whole expression. But why this way of specifying the
line index? Wouldn't it be enough with:

<select th:field="*{rows[rowStat.index].variety}">

...

... well, actually no. The problem is that Primavera EL does not evaluate variables within array index braces, so
when executing the above expression, we would get an error telling us thatrows[rowStat.index](instead ofrows[0], rows[1], etc)
it is not a valid position in the row collection. That's why preprocessing is necessary here.

Let's take a look at a fragment of the resulting HTML after pressing 'Add row' a few times:

<tr>
1
<td>

Invalid input. Please provide text for translation. 21/34


01/08/2020 Tutorial: Thymeleaf + Spring
<select id="rows0.variety" name="rows[0].variety">
Thymus vulgaris
Thymus x citriodorus
Thymus herba-barona
Thymus pseudolaginosus
Thymus serpyllum

<td>
<inputid="rows0.seedsPerCell"name="rows[0].seedsPerCell"type="text"value="" />
</td>
<td>
<buttonname="removeRow"type="submit"value="0">Remove row</button>
</td>

<tr>
2
<td>
<select id="rows1.variety" name="rows[1].variety">
<optionselected="selected"value="1">Thymus vulgaris</option>
Thymus x citriodorus
Thymus herba-barona
Thymus pseudolaginosus
Thyme
</select>
</td>
<td>
<inputid="rows1.seedsPerCell"name="rows[1].seedsPerCell"type="text"value="" />
</td>
<td>
<buttonname="removeRow"type="submit"value="1">Remove row</button>
</td>
</tr>
</tbody>

The provided link is a URL and does not contain translatable text. 22/34
01/08/2020 Tutorial: Thymeleaf + Spring

7 Validation and error messages


Most of our forms will need to show validation messages to inform the user about the errors that he / she ...
she committed.

Thymeleaf offers some tools for this: some functions in#fieldsthe objectth:errorsand the
Error Classattributes.

7.1 Field Errors

Let's see how we can define a specific CSS class for a field, if there is an error:

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


th:class="${#fields.hasErrors('datePlanted')}? fieldError" />

As you can see, the#fields.hasErrors(...)function receives the field expression as a parameter (date planted) e
returns a boolean indicating whether there are validation errors for this field.

We can also get all the errors from this field and iterate through them:

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

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

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


Incorrect date

Simplifying the CSS style based on error:th:errorclass

The example we saw above, defining a CSS class for a form input if that field has errors, is so common that
Thymeleaf offers a specific attribute to do exactly that:errorclass.

Applied to a form field tag (input, select, textarea ...), it will read the name of the field to be examined.
based on any existing onenameorth:fieldattributes in the same tag, and then add the specified CSS class
for the tag if such field has any associated errors:

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

IfdatePlantedif there are errors, this will be rendered as:

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

7.2 All errors

And if we want to show all the errors on the form? We just need to check the#fields.hasErrors(...)e
fields.errors(...)methods with the'*'orallconstants (that are equivalent):

Unable to access external content. 23/34


01/08/2020 Tutorial: Thymeleaf + Spring

if="${#fields.hasErrors('*')}"
Input is incorrect

As in the examples above, we can obtain all the errors and iterate through them...

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

...as well as create a<br />separated list:

Incorrect date

Finally, note that#fields.hasErrors('*')is equivalent to#fields.hasAnyErrors()e*é


equivalent to#fields.allErrors()Use the syntax you prefer:

if(${#fields.hasAnyErrors()})
...<p th:each="err : ${#fields.allErrors()}"> <span th:text="${err}"></span></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 associated with any field.
specific in the form, but they still exist.

Thymeleaf offers theglobalconstant to access these errors:

if="${#fields.hasErrors('global')}"
Input is incorrect
</ul>

Incorrect date

...as well as equivalent methods#fields.hasGlobalErrors()e#fields.globalErrors()convenient:

<divth:if="${#fields.hasGlobalErrors()}">
...
translatedText

7.4 Displaying errors outside of forms

Form validation errors can also be displayed outside of forms using variable expressions.
${...}) instead of selection (*{...}and prefixing the backup form bean name:

<divth:errors="${myForm}">...</div>
${myForm.date}
<divth:errors="${myForm.*}">...</div>

...if there are errors in '${myForm}'...


...if there are errors in the ${myForm.date} field...
...

Unable to access external content. 24/34


01/08/2020 Tutorial: Thymeleaf + Spring
<form th:object="${myForm}">
...
</form>

7.5 Advanced error objects

Thymeleaf offers the possibility to obtain form error information in the form of beans (instead of mere strings
), with the attributesfieldName(String)messageeglobalboolean

These errors can be obtained through the#fields.detailedErrors()utility method:

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

Unable to access the content of the provided link. 25/34


01/08/2020 Tutorial: Thymeleaf + Spring

It's still a prototype!


Our application is ready now. But let's take a second look at the.htmlpage we created ...

One of the most pleasant consequences of working with Thymeleaf is that, after all this functionality that
we added to our HTML, we can still use it as a prototype (we say it is a Natural Model). Let's open
seedstartermng.htmldirectly in our browser without running our application:

Natural model STSM

Here it is! It's not a functional app, it's not real data... but it's a perfectly valid prototype, made up of code.
Perfectly displayable HTML.

Unable to access the content of the provided URL for translation. 26/34
August 1, 2020 Tutorial: Thymeleaf + Spring

9 The conversion service

9.1 Configuration

As explained earlier, Thymeleaf can use a Conversion Service registered in the Application Context. Our
application configuration class, extending the itselfWebMvcConfigurerAdapterSpring assistant will register
automatically this conversion service, which we can configure by adding the formatters we need.
Let's see again how it is:

@Override
public void addFormatters(final FormatterRegistry registry) {
super.addFormatters(registry);
registry.addFormatter(varietyFormatter());
registry.addFormatter(dateFormatter());
}

@Bean
publicVarietyFormatter varietyFormatter() {
return newVarietyFormatter();
}

@Bean
publicDateFormatter dateFormatter() {
return newDateFormatter();
}

9.1 Double braces syntax

The Conversion Service can be easily applied to convert/format any object into String. This is done by
the syntax of the expression between braces:

For variable expressions:${{...}}


For selection expressions:*{{...}}

Thus, for example, given an integer-to-string converter that adds commas as thousands separators, this:

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

It must result in:

1234567890
1,234,567,890

9.2 Use in forms

We saw earlier that eachth:fieldattribute will always apply the conversion service, therefore, this:

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

... is really equivalent to:

The provided link does not contain text that can be translated. 27/34
01/08/2020 Tutorial: Thymeleaf + Spring

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

Note that, by the requirement of Spring, this is the only scenario in which the Conversion Service is applied to expressions.
using single-key syntax.

9.3#conversionsutility object

O #conversions The utility expression object allows for the manual execution of the Conversion Service whenever necessary:

Val:

Syntax for this utility object:

#conversions.convert(Object,Class)convert the object into the specified class.


#conversions.convert(Object,String)like the above, but specifying the target class as a String (note
what orjava.lang.package can be omitted.

Unable to access the content of the provided URL. 28/34


01/08/2020 Tutorial: Thymeleaf + Spring

10 rendering model fragments


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

This can be a useful component tool. For example, it can be used in controllers run on
AJAX calls, which can return fragments of markup from a page that is already loaded in the browser (to
update selection buttons, activation/deactivation ...)

Fragment rendering can be achieved using Thymeleaf fragment specifications: objects implementing the
org.thymeleaf.fragment.IFragmentSpecinterface.

The most common of these implementations isorg.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpecthat allows


specify a fragment using a DOM Selector exactly like those used inth:includeorth:replace.

10.1 Specifying fragments in display beans

View beans are beans fromorg.thymeleaf.spring4.view.ThymeleafViewclass declared in the context of the application
Beandeclarations if you are using the Java configuration). They allow the specification of fragments like this:

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

Given the bean definition above, if our controller returnscontent-part(the name of the bean above)...

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

... Thymeleaf will return only thecontentfragment of theindexmodel - what place will probably be something like this/WEB-
INF/templates/index.htmlwhen the price and the taxes are applied. Therefore, the result will be completely
equivalent to specifyingcontent:

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

...
</body>

Also note that, thanks to the power of Thymeleaf markup selectors, we can select a fragment in a
model without needing anyth:fragmentattribute. Let's use theidattribute, for example:

Unable to access the content of the link provided. 29/34


01/08/2020 Tutorial: Thymeleaf + Spring

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

... that will select perfectly:

<!DOCTYPE html>
<html>
...
<body>
...

Only this div will be rendered!

...
</body>

10.2 Specifying fragments in the return values of the controller

Instead of declaring display beans, fragments can be specified within the controllers themselves using the syntax
the expressions of fragment. Just like ininsertorth:replaceattributes:

/showContentPart
public String showContentPart() {
...
index :: content
}

Obviously, again all the power of the DOM Selectors is available, so we can select our fragment.
based on standard HTML attributes, such asid="content":

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

And we can also use parameters, such as:

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

The provided text is a URL and not a translatable text. 30/34


01/08/2020 Tutorial: Thymeleaf + Spring

11 Advanced Integration Resources

11.1 Integration withRequestDataValueProcessor

Thymeleaf integrates perfectly withRequestDataValueProcessorSpring interface. This interface allows


interception of link URLs, form URLs, and form field values before they are recorded in the result
from the marking, in addition to transparently adding hidden form fields that activate security features
such as protection against CSRF (Cross-Site Request Forgery).

An implementation ofRequestDataValueProcessorit can be easily configured in the app context. It needs


implement toorg.springframework.web.servlet.support.RequestDataValueProcessorinterface and have
requestDataValueProcessoras bean name:

@Bean
publicRequestDataValueProcessor requestDataValueProcessor() {
return newMyRequestDataValueProcessor();
}

... And Thymeleaf will use it this way:

th:hrefandth:srcleagueRequestDataValueProcessor.processUrl(...)before rendering the URL.

th:actioncallRequestDataValueProcessor.processAction(...)before rendering theactionform attribute


and, in addition, detects when this attribute is being applied to a<form>tag - which should be the only place - and, in this
case, callRequestDataValueProcessor.getExtraHiddenFields(...)and adds the hidden fields returned
immediately before theclosing tag.

th:valuerequestsRequestDataValueProcessor.processFormFieldValue(...)the rendering of the value to which it refers,


less than there is ath:fieldpresent in the same tag (in this case,th:fieldit will be necessary).

th:fieldrequestRequestDataValueProcessor.processFormFieldValue(...)the rendering of the value of the field to which it is


apply (or the body of the tag, if it is a).

Note that there are very few scenarios in which you would need to explicitly implement.
RequestDataValueProcessorin your app. In most cases, this will be used automatically by the
security libraries that you use transparently, such as the CSRF support of Spring Security.

11.1 Creating URIs for controllers

Since version 4.1, Spring allows the possibility of creating links to annotated controllers directly from the views,
without the need to know the URIs to which these controllers are mapped.

In Thymeleaf, this can be achieved through the#mvc.url(...)expression object method, which allows the
specification of controller methods by the uppercase letters of the controller class in which they are located, followed by
name of the own method. This is equivalent tospring:mvcUrl(...)custom function of JSP.

For example, for:

public class ExampleController {

/data
public String getData(Model model) { ...return "template" }

/data
Unable to access content from the link provided. 31/34
01/08/2020 Tutorial: Thymeleaf + Spring
public String getDataParam(@RequestParam String type) { ...return "template" }

The following code will create a link to it:

Get Data Param


Get Data Param

You can read more 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-of-views

I cannot access external links. 32/34


01/08/2020 Tutorial: Thymeleaf + Spring

12 Integration of Spring WebFlow

12.1 Basic Configuration

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

WebFlow includes some AJAX features to render fragments of the displayed page when specific events occur.
transitions) are triggered and, to allow Thymeleaf to respond to these AJAX requests, we will have to use a
ViewResolverdifferent implementation, configured as follows:

<bean id="thymeleafViewResolver" class="org.thymeleaf.spring4.view.AjaxThymeleafViewResolver">


viewClass
templateEngine
</bean>

... And thisViewResolvercan be configured in your WebFlowViewFactoryCreatoras:

<bean id="mvcViewFactoryCreator"
class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator"
viewResolvers

From here, you can specify Thymeleaf templates in your display states:

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

In the example above,booking detailit is a Thymeleaf model specified in the usual way, understandable by anyone
model resolvers configured inTemplate Engine.

12.2 AJAX Fragments in Spring WebFlow

Note that what is explained here is only the way to create AJAX fragments to be used with Spring.
WebFlow. If you are not using WebFlow, create a Spring MVC controller that responds to a request.
AJAX and return a piece of HTML is as simple as creating any other model return controller, with
the only exception being that you would probably return a fragment likemain :: adminof your method of
control.

WebFlow allows the specification of fragments to be rendered via AJAX withtranslatedTexttags, like this:

detail
<transition on="updateData">
<renderfragments="hoteldata"/>

These fragments (hotel datain this case) can be a comma-separated list of fragments specified in the markup
withth:fragment:

Unable to access the content from the provided URL. 33/34


01/08/2020 Tutorial: Thymeleaf + Spring

<div id="data" th:fragment="hoteldata">


This is a content to be changed

Always remember that the specified fragments must have aidattribute, for the Spring JavaScript libraries in
execution in the browser can replace the markup.

<render>Tags can also be specified using DOM selectors:

detail
updateData
[//div[@id='data']]

</view-state>

... And that means that notth:fragmentit is necessary:

This is a content to be changed

Regarding the code that triggers theupdateDatatransition, it looks like:

<scripttype="text/javascript"th:src="@{/resources/dojo/dojo.js}"></script>
<script type="text/javascript" th:src="@{/resources/spring/Spring.js}"></script>
<scripttype="text/javascript"th:src="@{/resources/spring/Spring-Dojo.js}"></script>

...

<formid="triggerform"method="post"action="">
<inputtype="submit"id="doUpdate"name="_eventId_updateData"value="Update now!" />
</form>

<scripttype="text/javascript">
Spring.addDecoration(
new Spring.AjaxEventDecoration({formId:'triggerform',elementId:'doUpdate',event:'onclick'}));

The provided text is a URL and cannot be translated. 34/34

You might also like