0% found this document useful (0 votes)
19 views94 pages

Tutorial - Using Thymeleaf PDF

[1] The document is a tutorial on how to use Thymeleaf, a Java template engine for creating dynamic web pages; [2] It explains the basic concepts of Thymeleaf such as dialects, template modes, and natural templating; [3] The tutorial also presents an example of a virtual grocery store application to illustrate Thymeleaf's features.
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)
19 views94 pages

Tutorial - Using Thymeleaf PDF

[1] The document is a tutorial on how to use Thymeleaf, a Java template engine for creating dynamic web pages; [2] It explains the basic concepts of Thymeleaf such as dialects, template modes, and natural templating; [3] The tutorial also presents an example of a virtual grocery store application to illustrate Thymeleaf's features.
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/ 94

01/08/2020 Tutorial: Using Thymeleaf

Thymeleaf

Tutorial: Using Thymeleaf


Document version: 20181029 - October 29, 2018
Versão do projeto:3.0.11.RELEASE
Project site:https://www.thymeleaf.org

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 1/94
01/08/2020 Tutorial: Using Thymeleaf

1 Introduction to Thymeleaf

1.1 What is Thymeleaf?

Thymeleaf is a modern server-side Java template engine for web and standalone environments, capable of
process HTML, XML, JavaScript, CSS, and even plain text.

The main goal of Thymeleaf is to provide an elegant and highly sustainable way to create templates. To achieve this
yes, it is based on the concept of Natural Models to inject its logic into the model files in a way that does not
affect the use of the model as a design prototype. This improves the communication of the design and fills the gap between the
design and development teams.

Thymeleaf was also developed from the ground up with Web Standards in mind - especially HTML5 -
allowing the creation of complete validation models, if necessary.

1.2 What kind of models can Thymeleaf process?

Ready to use, Thymeleaf allows processing six types of models, each of which is called a Model Mode:

HTML
XML
TEXT
JAVASCRIPT
CSS
CRU

There are two modes of demarcation modelHTMLe XMLthree modes of text modelTEXT, JAVASCRIPTeCSS) and a
non-operational model modeRAW).

OHTMLModel mode will allow any type of HTML input, including HTML5, HTML 4, and XHTML. No validation or
Good training verification will be carried out and the code/structure of the model will be respected to the fullest extent possible.
exit.

OXMLmodel mode will allow XML input. In this case, it is expected that the code is well-formed - without non-
closed, without uncited attributes, etc. - and the parser will throw exceptions if violations of good formation are found.
Note that no validation (against a DTD or XML schema) will be performed.

TheTEXTModelo mode will allow the use of a special syntax for unmarked nature models. Examples of such
models can be text emails or template documentation. Note that HTML or XML templates can also be included.
to be processed asTEXTin this case, they will not be analyzed as markup and all the tags, DOCTYPE, comment,
etc., will be treated as mere text.

TheJAVASCRIPTthe model mode will allow the processing of JavaScript files in a Thymeleaf application. This means
to be able to use the model data in JavaScript files in the same way as in HTML files, but with integrations
JavaScript specifications, such as specialized escape or natural script.JAVASCRIPTmodel mode is considered a
text mode, therefore, uses the same special syntax as theTEXT model mode.

TheCSSThe
model mode will allow the processing of CSS files involved in a Thymeleaf application. Similar to
JAVASCRIPTway, orCSSmodel mode is also a text mode and uses the special processing syntax of
TEXTmodel mode.

TheRAWmodel mode simply does not process models. It should be used to insert untouched resources (files,
responses from URL etc.) in the models being processed. For example, uncontrolled external resources in
HTML format can be included in application templates, confidently knowing that any Thymeleaf code that
these resources may include will not be executed.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 2/94
01/08/2020 Tutorial: Using Thymeleaf

1.3 Dialects: The Standard Dialect

Thymeleaf is an extremely extensible template engine (in fact, it can be called a framework of
model mechanism) that allows defining and customizing the way your models will be processed with a level of
detail no.

An object that applies some logic to a markup artifact (a tag, some text, a comment, or a mere marker)
if the models are not marked) is called processor, and a set of these processors - in addition to
some extra artifacts - that is what a dialect is normally composed of. Readily, the main library of Thymeleaf
provides a dialect called Standard Dialect, which should be sufficient for most users.

Note that dialects may actually not have processors and be entirely composed of other types of
artifacts, but processors are definitely the most common use case.

This tutorial addresses the standard dialect. Each attribute and syntax feature you will learn in the following pages are
defined by this dialect, even if this is not mentioned explicitly.

Obviously, users can create their own dialects (even extending the standard) if they want to define their own
processing logic while taking advantage of the advanced resources of the library. Thymeleaf can also be
configured to use multiple dialects at once.

The official integration packages thymeleaf-spring3 and thymeleaf-spring4 define a dialect called
"SpringStandard Dialect", which is basically the same as the Standard Dialect, but with slight adaptations for
better utilize some resources of the Spring Framework (for example, using Spring Expression Language or
SpringEL instead of OGNL). So, if you are a Spring MVC user, you are not wasting time, because almost everything
what you learn here will be useful in your Spring applications.

Most of the processors in the Standard Dialect are attribute processors. This allows browsers to display
correctly the HTML template files, even before they are processed, because they simply ignore the attributes
additional. For example, while a JSP using tag libraries can include a code fragment not directly
displayed by a browser as:

<form:inputText name="userName" value="${user.name}" />

… The Thymeleaf Standard Dialect would allow us to achieve the same functionality with:

<inputtype="text"name="userName"value="James Carrot"th:value="${user.name}" />

This will not only be displayed correctly by browsers, but it will also allow us (optionally) to specify a
value attribute ("James Carrot", in this case) that will be displayed when the prototype is statically opened in a
browser and will be replaced by the value resulting from the evaluation${user.name}during the model processing.

This helps your designer and developer work on the same template file and reduce the effort needed to
transforming a static prototype into a working template file. The ability to do this is a feature called
Natural Templating.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 3/94
01/08/2020 Tutorial: Using Thymeleaf

2 The virtual sustenance Good Thymes


The source code of the examples shown in this and the following chapters of this guide can be found in the repository.
GitHub Good Thymes Virtual Grocery.

2.1 A website for a grocery store

To better explain the concepts involved in processing models with Thymeleaf, this tutorial will use a
demo application, which can be downloaded from the project website.

This application is the website of an imaginary virtual grocery store and will provide many scenarios to showcase its many features.
Thymeleaf.

To begin with, we need a simple set of model entities for our application: theProductswhat are
soldCustomersthrough creationOrdersWe will also manageCommentsaboutProducts:

Example application model

Our app will also have a very simple service layer, composed ofServiceobjects containing methods
like:

public class ProductService {

...

public List<Product> findAll() {

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 4/94
01/08/2020 Tutorial: Using Thymeleaf
returnProductRepository.getInstance().findAll();
}

publicProduct findById(Integer id) {


returnProductRepository.getInstance().findById(id);
}

On the web layer, our application will have a filter that delegates execution to the commands enabled for Thymeleaf,
depending on the request URL:

private boolean process(HttpServletRequest request, HttpServletResponse response)


throwsServletException {

try{

This prevents triggering engine executions for resource URLs


if(request.getRequestURI().startsWith("/css") ||
request.getRequestURI().startsWith("/images") ||
request.getRequestURI().startsWith("/favicon") {
returnfalse;
}

/*
Query controller/URL mapping and obtain the controller
that will process the request. If no controller is available,
return false and let other filters/servlets process the request.
*/
IGTVGController controller = this.application.resolveControllerForRequest(request);
if(controller == null) {
return false;
}

/*
Obtain the TemplateEngine instance.
*/
ITemplateEngine templateEngine = this.application.getTemplateEngine();

/*
Write the response headers
*/
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires",0);

/*
Execute the controller and process view template,
writing the results to the response writer.
*/
controller.process(
request, response,this.servletContext, templateEngine);

return true;

}catch(Exception e) {
try{
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}catch(final IOException ignored) {
Just ignore this
}
throw newServletException(e);
}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 5/94
01/08/2020 Tutorial: Using Thymeleaf

This is ourIGTVGControllerinterface:

public interface IGTVGController {

public void process(


HttpServletRequest request, HttpServletResponse response,
ServletContext servletContext, ITemplateEngine templateEngine);

All we need to do now is create implementations of theIGTVGControllerinterface, retrieving data from services and
processing models using theITemplateEngineobject.

No signal, it will look like this:

Home page of the sample application

But first, let's see how this model mechanism is initialized.

2.2 Creating and configuring the Template Engine

The method process (...) in our last contained this line:

ITemplateEngine templateEngine = this.application.getTemplateEngine();

What it means is that the class GTVGApplication is responsible for creating and configuring one of the most important objects in a
Thymeleaf application: theTemplateEngineinstance (implementation of theITemplateEngineinterface).

Ourorg.thymeleaf.TemplateEnginethe object is initialized like this:

public classGTVGApplication {

...
private finalTemplateEngine templateEngine;
...

public GTVGApplication(final ServletContext servletContext) {

super();

ServletContextTemplateResolver templateResolver =
newServletContextTemplateResolver(servletContext);
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 6/94
August 1, 2020 Tutorial: Using Thymeleaf

HTML is the default mode, but we set it anyway for better understanding of code
templateResolver.setTemplateMode(TemplateMode.HTML);
// This will convert "home" to "/WEB-INF/templates/home.html"
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
// Template cache TTL=1h. If not set, entries would be cached until expelled
templateResolver.setCacheTTLMs(Long.valueOf(3600000L));

// Cache is set to true by default. Set to false if you want templates to


be automatically updated when modified.
templateResolver.setCacheable(true);

this.templateEngine = new TemplateEngine();


this.templateEngine.setTemplateResolver(templateResolver);

...

Existem várias maneiras de con gurar um Template Engineobject, but for now these few lines of code help us
they teach enough about the necessary steps.

The model resolver

Let's start with the Model Resolver:

ServletContextTemplateResolver templateResolver =
newServletContextTemplateResolver(servletContext);

Model resolvers are objects that implement an interface of the Thymeleaf API called
org.thymeleaf.templateresolver.ITemplateResolver:

public interface ITemplateResolver {

...

/*
Templates are resolved by their name (or content) and also (optionally) their
owner template in case we are trying to resolve a fragment for another template.
Will return null if the template cannot be handled by this template resolver.
*/
publicTemplateResolution resolveTemplate(
final IEngineConfiguration configuration,
finalString ownerTemplate,finalString template,
finalMap<String, Object> templateResolutionAttributes);
}

These objects are responsible for determining how our models will be accessed and, in this GTVG application, the
org.thymeleaf.templateresolver.ServletContextTemplateResolvermeans to recover our template files such as
resources of Servlet Context: ajavax.servlet.ServletContextobject of the entire application that exists in all the
Java web applications, and this resolves root resources of the web application.

But that is not all we can say about the model resolver, because we can define some parameters of
configuration. First, the model mode:

templateResolver.setTemplateMode(TemplateMode.HTML);

HTML is the standard template modeServletContextTemplateResolver, but it is a good practice to establish it in any
way, so that our code clearly documents what is happening.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 7/94
01/08/2020 Tutorial: Using Thymeleaf

templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");

We will provide the names of the models to the engine to obtain the real names of the
resources to be used.

Using this configuration, the model name "product / list" would correspond to:

servletContext.getResourceAsStream("/WEB-INF/templates/product/list.html")

Optionally, the amount of time that an analyzed model can live in the cache is configured in the Resolver.
Models through the property cacheTTLMs:

templateResolver.setCacheTTLMs(3600000L);

A model can still be evicted from the cache before the TTL is reached if the maximum cache size is reached and the
oldest entry currently in cache.

The behavior and sizes of the cache can be defined by the user implementing the
ICacheManagerinterface or modifying theStandardCacheManagerobject to manage the default cache.

There is much more to learn about model solvers, but for now, let's take a look at creating our
Template Engine object.

The model mechanism

The objects of the Template Engine are implementations of theorg.thymeleaf.ITemplateEngineinterface. One of those
implementations are offered by the Thymeleaf core:org.thymeleaf.TemplateEngineand we created an instance of it here:

templateEngine = new TemplateEngine();


templateEngine.setTemplateResolver(templateResolver);

Quite simple, isn't it? All we need to do is create an instance and define the Model Resolver for it.

A model resolver is the only parameter neededTemplate Enginenecessary, although there are many others who
will be addressed later (message resolvers, cache sizes, etc.). For now, that's all that
we need.

Our Template Engine is ready and we can start creating our pages using Thymeleaf.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 8/94
01/08/2020 Tutorial: Usando o Thymeleaf

3 Using texts

3.1 Boas-vindas em vários idiomas

Our first task will be to create a home page for our shopping website.

A primeira versão desta página será extremamente simples: apenas um título e uma mensagem de boas-vindas. Este é o
our/WEB-INF/templates/home.htmlfile:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all">
href="../../css/gtvg.css"th:href="@{/css/gtvg.css}" />
</head>

<body>

Welcome to our grocery store!

</body>

</html>

The first thing you will notice is that this file is HTML5, which can be properly displayed by any browser.
because it does not include any non-HTML tags (browsers ignore all attributes they do not understand, such as
th:text).

But you can also observe that this model is not really a valid HTML5 document, because these attributes
not the standard we are using inth:*forms are not allowed by the HTML5 specification. In fact, we are
adding axmlns:thattribute to our<html>tag, absolutely not HTML5-ish:

<htmlxmlns:th="http://www.thymeleaf.org">

... that has no influence on the processing of models, but works like an enchantment that prevents that
our IDE complains about the lack of a namespace definition for all theseth:*attributes.

And if we wanted to make this model valid for HTML5? Easy: switch to the Thymeleaf data attribute syntax,
using thedata-prefix for attribute names and- hyphen separators ( ) instead of semicolons (: ):

<!DOCTYPE html>

<html>

<head>
<title>Good Thymes Virtual Grocery</title>
<metahttp-equiv="Content-Type"content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all">
href="../../css/gtvg.css"data-th-href="@{/css/gtvg.css}" />
</head>

<body>

<pdata-th-text="#{home.welcome}">Welcome to our grocery store!</p>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 9/94
01/08/2020 Tutorial: Using Thymeleaf
</body>

</html>

data-Custom pre-defined attributes are allowed by the HTML5 specification; therefore, with the code above, our
model would be a valid HTML5 document.

Both notations are completely equivalent and interchangeable, but for the sake of simplicity and
compression of the code examples, this tutorial will use namespace annotationth:*. In addition, the
th:*notation is more general and allowed in all model modes of Thymeleaf (XML, TEXT...), while the
data-notation is only allowed inHTMLmode.

Using th:text and externalizing the text

The text externalization is extracting code snippets from template files so they can be
kept in separate files (usually.propertiesfiles) and that can be easily replaced by texts
written equivalents in other languages (a process called internationalization or simply i18n). Fragments of
Externalized texts are usually called 'messages'.

Messages always have a key that identifies them, and Thymeleaf allows you to specify that a text corresponds to a
specific message with the#{...} syntax:

Welcome to our grocery store!

What we can see here are, in fact, two different characteristics of the Standard Thymeleaf Dialect:

Theth:textattribute, which evaluates its value expression and defines the result as the body of the host tag, replacing
effectively the 'Welcome to our supermarket!' text that we see in the code.
AWelcome home.expression, specified in the standard expression syntax, instructing that the text to be used by the
th:textattribute must be the message with thehome.welcome key corresponding to the place we are in
processing the model.

Now, where is this text externalized?

The location of the externalized text in Thymeleaf is fully configurable and will depend on the
org.thymeleaf.messageresolver.IMessageResolverspecific implementation that is being used. Usually, a
implementation based on.propertiesfiles will be used, but we could create our own implementations if
we wanted, for example, to obtain messages from a database.

However, we did not specify a message resolver for our model engine during initialization, and
this means that our application is using the Default Message Resolver, implemented by
org.thymeleaf.messageresolver.StandardMessageResolver.

The default message resolver expects to find messages/WEB-INF/templates/home.htmlin the files of


properties in the same folder and with the same name as the model, such as:

/WEB-INF/templates/home_en.properties for texts in English.


/WEB-INF/templates/home_es.properties para textos em espanhol.
/WEB-INF/templates/home_pt_BR.properties for texts in Portuguese (Brazil).
/WEB-INF/templates/home.properties for standard texts (if the language code does not match).

Let's take a look at ourhome_es.properties file:

home.welcome=¡Bienvenido a nuestra tienda de comestibles!

It is all we need to make Thymeleaf our template. Let's create our home controller then.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 10/94
01/08/2020 Tutorial: Using Thymeleaf

Contexts

To process our model, we will create aHomeControllerclass implementing theIGTVGControllerthe interface we saw
antes:

public class HomeController implements IGTVGController {

public voidprocess(
finalHttpServletRequest request, finalHttpServletResponse response,
finalServletContext servletContext, finalITemplateEngine templateEngine
throwsException {

WebContext ctx =
newWebContext(request, response, servletContext, request.getLocale());

templateEngine.process("home", ctx, response.getWriter());

The first thing we see is the creation of a context. A Thymeleaf context is an object that implements the
org.thymeleaf.context.IContextinterface. The contexts must contain all the necessary data for execution of
model mechanism in a variable map and also refer to the language code that should be used for
externalized messages.

public interface IContext {

publicLocale getLocale();
public boolean containsVariable(final String name);
publicSet<String> getVariableNames();
public Object getVariable(final String name);

There is a specialized extension of this interface,org.thymeleaf.context.IWebContextintended to be used in applications


Web based on ServletAPI (like SpringMVC).

public interface IWebContext extends IContext {

public HttpServletRequest getRequest();


public HttpServletResponse getResponse();
publicHttpSession getSession();
public ServletContext getServletContext();

The main library of Thymeleaf provides an implementation of each of these interfaces:

org.thymeleaf.context.ContextimplementIContext
org.thymeleaf.context.WebContextimplementIWebContext

And as you can see in the controller code,WebContextit is what we use. In fact, we have to do it, because the use of
ServletContextTemplateResolverrequires the use of a context implementationIWebContext.

WebContext ctx =newWebContext(request, response, servletContext, request.getLocale());

Only three of these four constructor arguments are necessary because the system's default language code will be
usado se nenhum for especi cado (embora você nunca permita que isso aconteça em aplicativos reais).

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 11/94
01/08/2020 Tutorial: Using Thymeleaf

There are some specialized expressions we can use to obtain the request parameters and the attributes of
request, session and applicationWebContextin our models. For example:

${x}will return a variablexstored in the Thymeleaf context or as a request attribute.


${param.x}will return a request parameter calledx(which can have several values).
${session.x}will return a session attribute calledx .
${application.x}will return a servlet context attribute calledx.

Running the model engine

With our context object ready, we can now tell the model engine to process the model (by its
name) using the context and passing it a response recorder so that the answer can be recorded in it:

templateEngine.process("home", ctx, response.getWriter());

Let's see the results of this using the Spanish language code:

<!DOCTYPE html>

<html>

<head>
<title>Good Thymes Virtual Grocery</title>
<metacontent="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
</head>

<body>

Welcome to our grocery store!

</body>

</html>

3.2 More about texts and variables

Text without escape

The simplest version of our homepage seems to be ready now, but there is something we didn't think about... what if we had
a message like this?

home.welcome=Welcome to our <b>fantastic</b> grocery store!

If we execute this model as before, we will obtain:

<p>Welcome to our <b>fantastic</b> grocery store!</p>

What is not exactly what we expected, because our<b> the tag was escaped and therefore will be displayed in the browser.

This is the default behavior of theth:textattribute. If we want Thymeleaf to respect our HTML tags and not them
escape, we will have to use a different attribute:utext(for "text without escape")

Welcome to our grocery store!

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 12/94
01/08/2020 Tutorial: Using Thymeleaf

This will display our message exactly as we wanted:

Welcome to our fantastic grocery store!

Using and displaying variables

Now let's add more content to our homepage. For example, we can display the date below our message.
welcome, like this:

Welcome to our fantastic grocery store!

Today is: 12 july 2010

First, we will have to modify our controller to add this date as a context variable:

public void process(


final HttpServletRequest request, final HttpServletResponse response,
finalServletContext servletContext,finalITemplateEngine templateEngine)
throwsException {

SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");


Calendar cal = Calendar.getInstance();

WebContext ctx =
newWebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", dateFormat.format(cal.getTime()));

templateEngine.process("home", ctx, response.getWriter());

We added aStringvariable calledtodayto our context and now we can display it in our model:

<body>

Welcome to our grocery store!

Today is: 13 February 2011

</body>

As you can see, we are still using theth:textattribute for the work (and this is correct, because we want
replace the body of the tag), but the syntax is a bit different this time and instead of a#{...} value of expression, we are
using a${...} 1. This is a variable expression and contains an expression in a language called OGNL (Object-
Graph Navigation Language) that will be executed on the context variable map we talked about earlier.

A${today}the expression simply means 'get the variable called today', but these expressions can be more
complex (like${user.name}to get the variable called user and call itsgetName() method)

There are many possibilities in attribute values: messages, variable expressions ... and much more. The next chapter
it will show us what all these possibilities are.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 13/94
01/08/2020 Tutorial: Using Thymeleaf

4 Standard expression syntax


We will take a short break from developing our virtual supermarket store to learn about one of the parts
most important of the Thymeleaf Standard Dialect: the syntax of the Thymeleaf Standard Expression.

We have already seen two types of valid attribute values expressed in this syntax: message and variable expressions:

Welcome to our grocery store!

Today is: 13 February 2011

But there are more types of expressions and more interesting details to learn about those we already know. First,
let's take a quick look at a summary of the features of Regular Expression:

Simple expressions:

Variable expressions:${...}
Selection variable expressions:*{...}
Message expressions:#{...}
Link URL expressions:@{...}
Expressions of fragments:~{...}

Literals

Text literals:one text, Another one!, ...


Literal Numbers:0 , 34, 3.0, 12.3, ...
Boolean literals:true, false
Literal null:null
Literal symbols:one, sometext, main, ...

Text operations:

Concatenation of strings:+
Literal substitutions:The name is ${name}

Arithmetic operations:

Binary operators:+ , - , * , / , %
Minus sign (unary operator):-

Boolean operations:

Binary operators:and, or
Boolean negation (unary operator):! , not

Comparisons and equality:

Comparators:> , < , >=, <=( gt, lt, ge, the)


Equality operators:==, !=( eq, no)

Conditional operators:

So then:(if) ? (then)
So then another:(if) ? (then) : (else)
Standard:(value) ?: (defaultvalue)

Special cards:

No operation:_

All these resources can be combined and nested:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 14/94
01/08/2020 Tutorial: Using Thymeleaf

'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

4.1 Messages

As we already know, the#{...} message expressions allow us to link this:

Welcome to our grocery store!

...for that:

home.welcome=¡Bienvenido a nuestra tienda de comestibles!

But there is one aspect we have not considered yet: what happens if the text of the message is not completely static? And
If, for example, our application knew who the user visiting the site is at any moment and we wanted
greet him by name?

Welcome to our grocery store, John Apricot!

This means that we would need to add a parameter to our message. Just like this:

home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!

The parameters are specified according to thejava.text.MessageFormatstandard syntax, which means that you can
formatar para números e datas, conforme especi cado nos documentos da API para classes no java.text.*package.

To specify a value for our parameter and receive an HTTP session attribute calleduserwe could have:

Welcome, ${session.user.name}
Welcome to our grocery store, Sebastian Pepper!
</p>

Note that the useutexthere means that the formatted message will not be escaped. This example assumes that
user.name has already escaped.

Several parameters can be specified, separated by commas.

The key of the message itself can come from a variable:

<pth:utext="#{${welcomeMsgKey}(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>

4.2 Variables

We have already mentioned that the ${...} expressions are indeed OGNL (Object-Graph Navigation Language) expressions executed in
map of variables contained in the context.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 15/94
01/08/2020 Tutorial: Using Thymeleaf

To obtain detailed information about syntax and resources of theOGNL,read theLanguage GuidedoOGNL.

In Spring MVC-enabled applications, OGNL will be replaced by SpringEL, but its syntax is very
similar to that of OGNL (in fact, exactly the same for the most common cases).

From the syntax of OGNL, we know that the expression in:

Today is: 13 February 2011.

... It is indeed equivalent to that:

ctx.getVariable("today");

But OGNL allows us to create much more powerful expressions, and that's how it is:

<pth:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>

... Obtains the username by executing:

((User) ctx.getVariable("session").get("user")).getName();

But the navigation of the getter method is just one of the features of OGNL. Let's take a closer look:

/*
Access to properties using the point (.). Equivalent to calling property getters.
*/
${person.father.name}

/*
Access to properties can also be made by using brackets ([]) and writing
* the name of the property as a variable or between single quotes.
*/
${person['father']['name']}

/*
If the object is a map, both dot and bracket syntax will be equivalent to
* executing a call on its get(...) method.
*/
Spain
${personsByName['Stephen Zucchini'].age}

/*
Indexed access to arrays or collections is also performed with brackets,
* writing the index without quotes.
*/
${personsArray[0].name}

/*
Methods can be called, even with arguments.
*/
${person.createCompleteName()}
${person.createCompleteNameWithSeparator('-')}

Basic expression objects

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 16/94
01/08/2020 Tutorial: Using Thymeleaf

When evaluating OGNL expressions in the context variables, some objects are made available for expressions for greater
flexibility. These objects will be referenced (by default OGNL) starting with the# symbol:

#ctx the context object.


#vars: the context variables.
#localethe language code of the context.
#request (only in web contexts) theHttpServletRequestobject.
#response(only in web contexts) theHttpServletResponseobject.
#session(only in web contexts) theHttpSessionobject.
#servletContext(only in online contexts) theServletContextobject.

In order for us to do this:

Established locale country: US.

You can read the full reference of these objects in Appendix A.

Utility expression objects

In addition to these basic objects, Thymeleaf offers us a set of utility objects that will help us perform tasks.
common in our expressions.

#execInfo information about the model being processed.


#messages methods to obtain externalized messages within variable expressions, in the same way as
would be obtained using the syntax # {...}.
#urismethods to escape parts of URLs / URIs
[]methods to execute the configured conversion service (if any).
#datesmethods tojava.util.Dateobjects: formatting, extraction of components, etc.
#calendarsanalogous to#datesbut forjava.util.Calendarobjects.
#numbersmethods for formatting numerical objects.
#stringsmethods toStringobjects: contains, starts with, adding / adding, etc.
#objectsmethods for objects in general.
#boolsmethods for Boolean evaluation.
#arraysmethods for matrices.
#listsmethods for lists.
#setsmethods for sets.
#mapsmethods for maps.
aggregatesmethods to create aggregates in matrices or collections.
#idsmethods for dealing with ID attributes that may be repeated (for example, as a result of iteration).

You can check what functions are offered by each of these utility objects in Appendix B.

Reformatting of dates on our home page

Now that we know about these utility objects, we can use them to change the way we display the date in our
home page. Instead of doing this on ourHomeController:

SimpleDateFormat dateFormat =newSimpleDateFormat("dd MMMM yyyy");


Calendar cal = Calendar.getInstance();

WebContext ctx = new WebContext(request, servletContext, request.getLocale());


ctx.setVariable("today", dateFormat.format(cal.getTime()));

templateEngine.process("home", ctx, response.getWriter());

... we can do exactly that:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 17/94
01/08/2020 Tutorial: Using Thymeleaf

WebContext ctx =
newWebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", Calendar.getInstance());

templateEngine.process("home", ctx, response.getWriter());

... And format the date in the display layer itself:

<p>
Today is: 13 May 2011
</p>

4.3 Expressions in selections (asterisk syntax)

Not only variable expressions can be written as${...} , but also as*{...} .

There is an important difference: the syntax of the asterisk evaluates expressions in selected objects, and not in the whole context. Or
So, as long as no object is selected, the dollar and asterisk syntaxes do exactly the same.

And what is a selected object? The result of an expression using thethe objectattribute Let's use one in our
userprofile.htmluser profile page ( ) :

${session.user}
<p>Name: <spanth:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <spanth:text="*{lastName}">Pepper</span>.</p>
Nationality: Saturn.
</div>

What is exactly equivalent to:

<div>
<p>Name: Sebastian.</p>
Surname: Pepper.
Nationality: Saturn.
</div>

Obviously, the syntax of the dollar and the asterisk can be mixed:

${session.user}
Name: Sebastian.
Surname: Pepper.
Nationality: Saturn.
</div>

When an object selection is in effect, the selected object will also be available for dollar expressions.
like aobjectexpression variable:

<divth:object="${session.user}">
<p>Name: <spanth:text="${#object.firstName}">Sebastian</span>.</p>
Surname: Pepper.
<p>Nationality: Saturn.</p>
</div>

As stated, if no object selection has been made, the dollar and asterisk syntaxes are equivalent.

<div>
Name: Sebastian.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 18/94
01/08/2020 Tutorial: Usando o Thymeleaf
Surname: Pepper.
Nationality: Saturn.
</div>

4.4 Link URLs

Due to their importance, URLs are first-class citizens in web application models, and Thymeleaf
Standard Dialect has a special syntax for them, the@ syntax:@{...}

There are different types of URLs:

Absolute URLs:http://www.thymeleaf.org
Relative URLs, which can be:

Regarding the page:user/login.html


Regarding the/itemdetails?id=3 (the name of the context on the server will be added automatically)
Regarding the server:~/billing/processInvoice(allows calling URLs in another context (= application) in the same
server.
Relative URLs to the protocol://code.jquery.com/jquery-2.0.3.min.js

The actual processing of these expressions and their conversion into the URLs that will be produced is done by the implementations of
org.thymeleaf.linkbuilder.ILinkBuilderinterfaces that are registered inITemplateEngineobject that is being used.

By default, a single implementation of this interface is registered from the class


org.thymeleaf.linkbuilder.StandardLinkBuilderwhat is sufficient for offline scenarios (not web) and also for the web
based on the Servlet API. Other scenarios (such as integration with non-ServletAPI web frameworks) may need to
specific implementations of the link builder interface.

Let's use this new syntax. Meet theth:hrefattribute:

Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting)


<a href="details.html"
th:href="http://localhost:8080/gtvg/order/details(orderId=${o.id})}'>view</a>

/gtvg/order/details?orderId=3 (plus rewriting)


<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>

/gtvg/order/3/details (plus rewriting)


<ahref="details.html"th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

Some things to be noted here:

th:hrefit is a modifying attribute: once processed, it will calculate the URL of the link to be used and set that value to
ohrefattribute of<a>tag.
We have permission to use expressions for URL parameters (as you can see inorderId=${o.id} ). As
URL parameter encoding operations necessary will also be carried out automatically.
If several parameters are needed, they will be separated by commas:
@{/order/process(execId=${execId},execType='FAST')}
Variable models are also allowed in URL paths:@{/order/{orderId}/details(orderId=${orderId})}
Relative URLs that start with/ (for example/order/details) will be automatically prefixed by the name of the
application context.
If cookies are not enabled or this is still unknown, a;jsessionid=...su xo may be
added to the relative URLs so that the session is preserved. This is called URL Rewriting and Thymeleaf allows
connect your own rewriting filters using theresponse.encodeURL(...)API mechanism of the Servlet for each
URL.
Theth:hrefattribute allows us to (optionally) have ahrefactive static attribute in our model, so that the links
of our model remain navigable by a browser when opened directly for prototyping purposes.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 19/94
01/08/2020 Tutorial: Using Thymeleaf

How was the message syntax case (#{...} ), the URL bases can also be the result of the evaluation of another
expression:

<ath:href="@{${url}(orderId=${o.id})}">view</a>
<a href="/details/${user.login}(orderId=${o.id})">view</a>

A menu for our homepage

Now that we know how to create link URLs, how about adding a small menu to our homepage for some of the
other pages of the site?

Please select an option


<ol>
<li><a href="product/list.html" th:href="@{/product/list}">Product List</a></li>
<li><ahref="order/list.html"th:href="@{/order/list}">Order List</a></li>
Subscribe to our Newsletter
<li><a href="userprofile.html" th:href="@{/userprofile}">See User Profile</a></li>
</ol>

Relative URLs to the server root

An additional syntax can be used to create URLs relative to the server root (instead of relative to the context root), the m
to unlink to different contexts on the same server. These URLs will be specified as@/path/to/something

4.5 Fragments

Fragment expressions are an easy way to represent markup fragments and move them through templates. This
allows us to replicate them, pass them to other models as arguments and so on.

The most common use is for inserting fragments usingth:insertorth:replace(but more about this in a section
posterior):

...

But they can be used anywhere, just like any other variable:

divth:with="frag=~{footer :: #main/text()}"
<pth:insert="${frag}">
</div>

Later in this tutorial, there is an entire section dedicated to the Model Layout, including a deeper explanation.
the expressions of fragment.

4.6 Literals

Text literals

Text literals are just strings of characters specified within single quotes. They can include any character,
but you must escape single quotes within them using\' .

<p>
Now you are looking at a template file for a working web application.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 20/94
01/08/2020 Tutorial: Using Thymeleaf
</p>

Numeric literals

Numeric literals are just that: numbers.

The year is 1492.


In two years, it will be 1494.

Boolean literals

Boolean literals aretrueefalseFor example:

if the user is not an admin

In this example, the== falseit is written outside the device and, therefore, it is Thymeleaf that takes care of it. If it were written inside the
device, would be the responsibility of OGNL / SpringEL engines:

if user.isAdmin() == false

The null literal

Thenullliteral can also be used:

if(${variable.something}==null) { ... }

Literal tokens

Numeric, boolean, and null literals are, in fact, a special case of literal tokens.

These tokens allow for some simplification in Regular Expressions. They work exactly the same way as
the text literals ('...'), but allow only letters (A-Zanda-z), numbers (0-9), brackets ([ e] ), points ( . ),
hyphens (- ) and underlined (_ Therefore, there are no blank spaces, commas, etc.

The legal part? Tokens do not need quotes. So we can do this:

...

instead of:

...

4.7 Attaching texts

Texts, regardless of being literal or the result of evaluating variable expressions or messages,
can be easily attached using the+ operador:

The name of the user is


https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 21/94
01/08/2020 Tutorial: Using Thymeleaf

4.8 Literal substitutions

Literal substitutions allow for easy formatting of strings containing variable values without the need for
attach literals'...' + '...'.

These substitutions should be enclosed by vertical bars (| ), such as:

Welcome to our application, ${user.name}!

What is equivalent to:

<spanth:text="'Welcome to our application,'+ ${user.name} +'!'">

Literal substitutions can be combined with other types of expressions:

${onevar} + ' ' + |${twovar}, ${threevar}|

Only variable expressions / message (${...}, *{...}, #{...}) are allowed inside|...|substitutions
literals. No other literal ('...'boolean/numeric tokens, conditional expressions, etc.

4.9 Arithmetic operations

Some arithmetic operations are also available:+ , - , * , / e% .

isEven=(${prodStat.count} % 2==0)

Note that these operators can also be applied within the OGNL variable expressions themselves (and in this case
will be executed by OGNL instead of the Thymeleaf Standard Expression mechanism:

<divth:with="isEven=${prodStat.count % 2==0}">

Note that there are textual aliases for some of these operators:div( / ), mod( % ).

4.10 Comparators and equality

Values in expressions can be compared with the> , < , >=e<=symbols, and the==e!=the operators can be
used to verify equality (or the lack of it). Note that XML establishes that the symbols< e > they should not be
used in attribute values and, therefore, must be replaced by<e>.

<divth:if="${prodStat.count} > 1">


Execution mode is Development

A simpler alternative may be using existing text aliases for some of these operators:gt( > ), lt( < ),
ge( >=), the( <=), not( ! Alsoeq( ==), neq/ ne( !=).

4.11 Conditional expressions


https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 22/94
01/08/2020 Tutorial: Using Thymeleaf

Conditional expressions are designed to evaluate only one of the two expressions, depending on the result of the evaluation.
a condition (which is another expression).

Let's take a look at a sample fragment (the introduction of another modifier attribute,th:class):

<trth:class="${row.even}?'even':'odd'"]
...
</tr>

All three parts of a conditional expression (condition, thene elsethey are expressions themselves, which means
that can be variables (${...} , *{...} ), messages ( #{...} ), URLs ( @{...} ) or literals ('...' ).

Conditional expressions can also be nested using parentheses:

<trth:class="${row.even}? (${row.first}?'first':'even') :'odd'">


...
</tr>

Other expressions can also be omitted; in this case, a null value will be returned if the condition is false:

<trth:class="${row.even}?'alt'">
...
</tr>

4.12 Standard expressions (Elvis operator)

A standard expression is a special type of conditional value without a then part. It is equivalent to the Elvis operator present
in some languages, like Groovy, it allows specifying two expressions: the first one is used if it is not evaluated as null,
but if the zer, the second will be used.

Let's see it in action on our user profile page:

<divth:object="${session.user}">
...
Age: 27.
</div>

As you can see, the operator is?: and we use it here to specify a default value for a name (in this case, a
literal value) only if the result of the evaluation*{age} for null. This is, therefore, equivalent to:

<p>Age: <spanth:text="*{age !=null}? *{age} :'(no age specified)'">27</span>.</p>

Just like conditional values, they can contain nested expressions in parentheses:

<p>
Name:
<spanth:text="*{firstName}?: (*{admin}?'Admin': #{default.username})">Sebastian</span>
</p>

4.13 The non-operation token

The No-Operation token is represented by an underscore symbol (_)_ ).

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 23/94
01/08/2020 Tutorial: Using Thymeleaf

The idea behind this token is to specify that the desired outcome for an expression is to do nothing, that is, to do.
exactly as if the processable attribute (for exampleth:textwas not present.

Among other possibilities, this allows developers to use text prototyping as default values. For
example, instead of:

<spanth:text="${user.name} ?:'no user authenticated'">...</span>

We can directly use 'no authenticated user' as a prototyping text, which results in code
more concise and versatile from a design perspective:

no user authenticated

4.14 Data conversion / formatting

Thymeleaf defines a double-bracketed syntax for variable expressions (${...}) and selection (*{...}) that us
permitem aplicara conversão de dadospor meio de umserviço de conversãocon gurado .

Basically, this is how it is:

<tdth:text="${{user.lastAccessDate}}">...</td>

Did you notice the double belt there?${{...}}This instructs Thymeleaf to pass the result of theuser.lastAccessDateexpression for the
conversion service and requests that it perform a formatting operation (a conversion toStringbefore recording the
result.

Assuming thatuser.lastAccessDatebe of the typejava.util.Calendarif a conversion service (implementation of


IStandardConversionServiceif it has been registered and contains a valid conversionCalendar -> Stringit will be applied.

The standard implementation ofIStandardConversionService(aStandardConversionServicethe class simply executes


.toString()in any object converted intoStringTo obtain more information on how to register a
implementation of customized conversion service, refer to the section More about configuration.

Thymeleaf-spring3 official integration packages and thymeleaf-spring4 integrate the mechanism transparently
Thymeleaf conversion service with its own Spring infrastructure conversion service, so that...
conversion services and formatters declared in the Spring configuration will be made automatically available
for${{...}}and*{{...}}expressions.

4.15 Pre-processing

In addition to all these resources for expression processing, Thymeleaf has pre-processing features.
expressions.

The preprocessing is an execution of expressions made before the normal one that allows modification of the expression that
it will eventually be executed.

Pre-processed expressions are exactly the same as normal ones, but they appear surrounded by a double underscore symbol.
(like__${expression}__).

Let's imagine that we have aMessages_fr.propertiesi18n entry containing an OGNL expression calling a
specific static method of the language, such as:

[email protected]@translateToFrench({0})

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 24/94
01/08/2020 Tutorial: Using Thymeleaf

... And aMessages_es.properties equivalent:

[email protected]@translateToSpanish({0})

We can create a markup fragment that evaluates one expression or another, depending on the locale. To do this, first
we will select the expression (by pre-processing) and then let Thymeleaf execute it:

Some text here...

Note that the pre-processing stage for a French language code will create the following equivalent:

<pth:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p>

The preprocessing chain__can be escaped in the attributes using\_\_.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 25/94
01/08/2020 Tutorial: Using Thymeleaf

5 Defining attribute values


This chapter will explain how we can define (or modify) attribute values in our markup.

5.1 Defining the value of any attribute

Let's say our website publishes a newsletter and we want our users to be able to subscribe to it, so
we created a/WEB-INF/templates/subscribe.htmlmodel with a form:

<form action="subscribe.html">
<fieldset>
<input type="text" name="email" />
<inputtype="submit"value="Subscribe!" />
</fieldset>
</form>

Just like in Thymeleaf, this model starts more as a static prototype than for a web application.
First, theactionattribute in our form is statically linked to the model file itself, so that there is no
place to rewrite useful URLs. Second, thevalueattribute on the submit button causes a text in English to be displayed,
but we would prefer it to be internationalized.

Type theth:attrattribute and its ability to change the values of the attributes of the tags in which it is defined:

<form action="subscribe.html" th:attr="action=@{/subscribe}">


<fieldset>
<input type="text" name="email" />
<inputtype="submit"value="Subscribe!"th:attr="value=#{subscribe.submit}"/>
</fieldset>
</form>

The concept is quite straightforward:th:attrjust


take an expression that assigns a value to an attribute. After creating
the controller and the corresponding message files, the result of processing this file will be:

<formaction="/gtvg/subscribe">
<fieldset>
<input type="text" name="email" />
<inputtype="submit"value="¡Suscríbe!"/>
</fieldset>
</form>

In addition to the new attribute values, it is also possible to see that the application context name has been prefixed.
automatically at the URL base/gtvg/subscribe, as explained in the previous chapter.

But what if we wanted to define more than one attribute at once? XML rules do not allow you to define an attribute twice.
times in a tag; therefore,th:attryou will have a list of assignments separated by commas, such as:

<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

Given the necessary message files, this will result in:

<imgsrc="/gtgv/images/gtvglogo.png"title="Logo de Good Thymes"alt="Logo de Good Thymes" />

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 26/94
01/08/2020 Tutorial: Using Thymeleaf

5.2 Configuring value for specific attributes

By now, you must be thinking something like:

<inputtype="submit"value="Subscribe!"th:attr="value=#{subscribe.submit}"/>

... It is a quite ugly part of marking. The specification of an assignment within the value of an attribute can be very
practical, but it is not the most elegant way to create models if you need to do this all the time.

Thymeleaf agrees with you, and that's whyth:attrit is hardly used in models. Normally, you will use others
th:*attributes whose task is to define specific tag attributes (and not just any attribute liketh:attr).

For example, to define thevalueattribute, useth:value:

<inputtype="submit"value="Subscribe!"th:value="#{subscribe.submit}"/>

This looks much better! Let's try to do the same with theactionattribute informtag:

<form action="subscribe.html" th:action="@{/subscribe}">

And do you remember thoseth:hrefwhat we put in ourhome.htmlbefore? They are exactly that same kind of
attributes:

Product List

There are many attributes like these, each of them directed to a specific HTML5 attribute.

abbreviation th:accept accept-charset

accesskey th:action th:align

th:alt th:archive audio


th:autocomplete th:axis th:background

th:bgcolor th:border th:cellpadding


th:cellspacing challenge th:charset

th:cite th:class th:classid

codebase type:code th:cols


th:colspan compact th:content

th:contenteditable th:contextmenu th:data


th:datetime th:dir th:draggable

th:dropzone th:enctype th:for


th:form th:formaction form enctype

th:formmethod th:formtarget th:fragment

frame th:frameborder th:headers


th:height high th:href

th:hreflang th:hspace th:http-equiv

th:icon th:id th:inline


keytype kind th:label

th:lang th:list long description

low th:manifest th:marginheight

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 27/94
01/08/2020 Tutorial: Using Thymeleaf

th:marginwidth th:max th:maxlength

th:media th:method th:min

th:name onabort th:onafterprint


th:onbeforeprint onbeforeunload onblur

oncanplay oncanplaythrough onchange

th:onclick oncontextmenu th:ondblclick


on drag ondragend on drag enter

th:ondragleave on drag over on drag start

on drop on duration change onemptied

onended th:onerror onfocus


onformchange onforminput onhashchange

oninput oninvalid th:onkeydown

th:onkeypress onkeyup onload


onloadeddata onloadedmetadata onloadstart

onmessage onmousedown th:onmousemove

onmouseout onmouseover onmouseup


th:onmousewheel th:onoffline th:ononline

th:onpause onplay onplaying

onpopstate onprogress onratechange

th:onreadystatechange onredo onreset


th:onresize th:onscroll th:onseeked

seeking onselect on show

th:onstalled onstorage on submit


on suspend on time update th:onundo

onunload onvolumechange on waiting

optimum th:pattern th:placeholder


th:poster th:preload radio group

th:rel th:rev th:rows


th:rowspan th:rules th:sandbox

th:scheme th:scope scrolling


th:size th:sizes th:span

th:spellcheck th:src th:srclang

standby start th:step


th:style th:summary th:tabindex

th:target th:title th:type


th:usemap th:value th:valuetype

vspace th:width th:wrap

th:xmlbase th:xmllang th:xmlspace

5.3 Defining more than one value at once

There are two quite special attributes calledalternative titleandth:lang-xmllangthat can be used to define
two attributes with the same value at the same time. Specifically:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 28/94
01/08/2020 Tutorial: Using Thymeleaf

alternate titleto definealte title.


th:lang-xmllangwill definelanguagee xml:lang.

For our GTVG homepage, this will allow us to replace:

<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

... Or this, which is equivalent:

<img src="../../images/gtvglogo.png"
th:src="@{/images/gtvglogo.png}"th:title="#{logo}"th:alt="#{logo}" />

...with that:

<imgsrc="../../images/gtvglogo.png"
th:src="@{/images/gtvglogo.png}"th:alt-title="#{logo}" />

5.4 Attaching and attaching

Thymeleaf also offers the attributesth:attrappendeth:attrprepend, that add (suction) or precede


(pre xo) the result of your evaluation of the existing attribute values.

For example, it is advisable to store the name of a CSS class to be added (not configured, just added) to one of
its buttons in a context variable, because the specific CSS class to be used would depend on something the user did
before:

<inputtype="button"value="Do it!"class="btn"th:attrappend="class=${' '+ cssStyle}" />

If you process this model with thecssStyle variable defined aswarningyou will obtain:

<inputtype="button"value="Do it!"class="btn warning" />

There are also two specific attributes linked in the Standard Dialect: the attributesth:classappendeth:styleappend, which are
used to add a CSS class or a style fragment to an element without replacing the existing ones:

<trth:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}?'odd'">

Don't worry about thisth:eachattribute. It is an iteration attribute and we will talk about this later.)

5.5 Boolean attributes of value xo

HTML has the concept of boolean attributes, attributes that have no value and the presence of one means that the value is
true. In XHTML, these attributes take only 1 value, which is themselves.

For examplechecked:

<input type="checkbox" name="option2" checked/><!-- HTML -->


<input type="checkbox" name="option1" checked="checked" /><!-- XHTML -->

The Standard Dialect includes attributes that allow defining these attributes by evaluating a condition, so that if evaluated
If true, the attribute will be defined as its value is true, and if evaluated as false, the attribute will not be defined.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 29/94
01/08/2020 Tutorial: Using Thymeleaf

<inputtype="checkbox"name="active"th:checked="${user.active}" />

The following boolean attributes with value xo exist in the Standard Dialect:

th:async th:autofocus th:autoplay

th:checked th:controls declare

th:default defer th:disabled

th:formnovalidate th:hidden ismap


th:loop multiple no validate

nowrap open pubdate

th:readonly th:required reversed


scoped seamless th:selected

5.6 Configuring the value of any attribute (default attribute processor)

Thymeleaf provides a standard attribute processor that allows us to define the value of any attribute, even if
noneth:* a specific processor has been defined for it in the Standard Dialect.

So, something like:

<spanth:whatever="${user.name}">...</span>

It will result in:

John Apricot

5.7 Support for attribute and element names compatible with HTML5

It is also possible to use a completely different syntax to apply processors to your models in a way
more friendly to HTML5.

<table>
<trdata-th-each="user : ${users}">
<tddata-th-text="${user.login}">...</td>
<td data-th-text="${user.name}">...</td>
</tr>
</table>

Adata-{prefix}-{name}syntax is the standard way of writing custom attributes in HTML5, without requiring that the
developers use names like namespaceth:* . Thymeleaf automatically makes this syntax available for
all of your dialects (not just the standard ones).

There is also a syntax to specify custom tags:{prefix}-{name}that follow the specification of Elements
Customized W3C (part of the largest specification of W3C Web Components). This can be used, for example,
for theth:block element (or alsoth-block), which will be explained in a later section.

Important: this syntax is an addition to the namespaceth:*, do not replace it. There is no intention to discontinue the syntax in
space for name in the future.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 30/94
01/08/2020 Tutorial: Using Thymeleaf

6 Iteration
So far, we have created a homepage, a user profile page, and also a page to allow users to
subscribe to our newsletter ... but what about our products? For that, we will need a way to browse the items of
a collection to create our product page.

6.1 Basic concepts of iteration

To display products on our/WEB-INF/templates/product/list.html page, we will use a table. Each of our


products will be displayed in a line (a<tr>element) and therefore, for our model, we will need to create a line of
model - one that exemplifies how we want each product to be displayed - and instruct Thymeleaf to repeat it once
for each product.

The standard language provides us with an attribute for exactly that:th:each.

Using th:each

For our product list page, we will need a controller method that retrieves the product list from
service layer and add it to the model context:

public voidprocess(
finalHttpServletRequest request,finalHttpServletResponse response,
finalServletContext servletContext, finalITemplateEngine templateEngine
throwsException {

ProductService productService = new ProductService();


List<Product> allProducts = productService.findAll();

WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());


ctx.setVariable("prods", allProducts);

templateEngine.process("product/list", ctx, response.getWriter());

And then we will useth:eachin our model for traversing the list of products:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all">
href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>

<body>

Product list

<table>
<tr>
NAME
<th>PRICE</th>
IN STOCK
</tr>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 31/94
01/08/2020 Tutorial: Using Thymeleaf
<trth:each="prod : ${prods}">
<tdth:text="${prod.name}">Onions</td>
2.41
<tdth:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
</table>

<p>
Return to home
</p>

</body>

</html>

Thisprod : ${prods}The value of the attribute you see above means 'for each element in the evaluation result'${prods},
repeat this model fragment, using the current element in a variable called prod. Let's give a name to each
one of the things we see:

Let's call${prods}the iterated expression or iterated variable.


Let's callprodthe iteration variable or simply the iter variable.

Note that theprodthe iter variable has the defined scope for the<tr>element, which means it is available for tags
internals like<td>.

Iterable values

Ajava.util.Listclass is not the only value that can be used for iteration in Thymeleaf. There is quite a set
complete with objects that are considered iterable by ath:eachattribute:

Any implemented objectjava.util.Iterable


Any implemented objectjava.util.Enumeration.
Any implemented objectjava.util.Iterator, whose values will be used as returned by the iterator,
without the need to cache all values in memory.
Any implemented objectjava.util.Map. When iterating over maps, the iter variables will be of classjava.util.Map.Entry.
Any matrix.
Any other object will be treated as if it were a list containing the object itself.

6.2 Keeping the iteration status

When usingth:eachThymeleaf provides a useful mechanism for tracking the status of your iteration: the status variable.

The status variables are defined in ath:eachattribute and contains the following data:

The current iteration index, starting with 0. This is theindexproperty


The current iteration index, starting with 1. This is the countproperty.
The total number of elements in the iterated variable. This is thesizeproperty.
The variable iter for each iteration. This is thecurrentproperty.
Whether the current iteration is even or odd. These are theeven/oddboolean properties.
If the current iteration is the first. This is thefirstboolean property.
If the current iteration is the last. This is thelastboolean property.

Let's see how we could use it with the previous example:

<table>
<tr>
NAME
<th>PRICE</th>
IN STOCK
</tr>
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 32/94
01/08/2020 Tutorial: Using Thymeleaf
<trth:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}?'odd'">
Onions
2.41
yes
</tr>
</table>

The status variable (iterStatin this example) is defined in theth:eachattribute, writing its name after the iter variable itself,
separated by a comma. Just like the variable iter, the status variable also has the scope defined in the code fragment
defined by the tag that contains theth:eachattribute.

Let's take a look at the result of processing our model:

<!DOCTYPE html>

<html>

<head>
<title>Good Thymes Virtual Grocery</title>
<metacontent="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
</head>

<body>

Product list

<table>
<tr>
<th>NAME</th>
PRICE
IN STOCK
</tr>
<tr class="odd">
Fresh Sweet Basil
4.99
yes
</tr>
<tr>
Italian Tomato
<td>1.25</td>
no
</tr>
<tr class="odd">
Yellow Bell Pepper
2.50
yes
</tr>
<tr>
Old Cheddar
18.75
yes
</tr>
</table>

<p>
Return to home
</p>

</body>

</html>

Note that our iteration status variable worked perfectly, establishing theoddCSS class just for
odd lines.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 33/94
01/08/2020 Tutorial: Using Thymeleaf

If you do not explicitly define a status variable, Thymeleaf will always create one for you with the suffix.Statdo
iteration variable name:

<table>
<tr>
<th>NAME</th>
PRICE
IN STOCK
</tr>
<trth:each="prod : ${prods}"th:class="${prodStat.odd}?'odd'">
Onions
<tdth:text="${prod.price}">2.41</td>
<tdth:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
</table>

6.3 Optimizing through slow data recovery

Sometimes, we can optimize the retrieval of data collections (for example, from a database) so that these
collections should be retrieved only if they are actually used.

In fact, this is something that can be applied to any part of the data, but given the size of the collections in
memory can have, recovering the collections that must be iterated is the most common case of this scenario.

To support this, Thymeleaf offers a mechanism for slowly loading context variables.
context that implements theILazyContextVariableinterface - probably extending its
LazyContextVariableStandard implementation - will be resolved at the time of execution. For example:

context.setVariable(
users
newLazyContextVariable<List<User>>() {
@Override
protectedList<User> loadValue() {
returndatabaseRepository.findAllUsers();
}
});

This variable can be used without the knowledge of your laziness, in codes such as:

<ul>
user name
</ul>

But, at the same time, it will never be initialized (itsloadValue()method will never be called) if it iscondition evaluated
falsein code like:

<ulth:if="${condition}">
user name
</ul>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 34/94
01/08/2020 Tutorial: Using Thymeleaf

7 Conditional Assessment

7.1 Simple conditionals: 'if' and 'unless'

Sometimes, you will need a fragment of your model to appear only in the result if a certain condition
for attended.

For example, imagine that we want to show in our product table a column with the number of comments that
there are for each product and, if there are any comments, a link to the comment detail page for that
product.

To do this, we would use theth:ifattribute:

<table>
<tr>
<th>NAME</th>
PRICE
IN STOCK
<th>COMMENTS</th>
</tr>
<trth:each="prod : ${prods}"th:class="${prodStat.odd}?'odd'">
Onions
2.41
yes
<td>
<spanth:text="${#lists.size(prod.comments)}">2</span> comment/s
<ahref="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
if not #lists.isEmpty(prod.comments)>view</a>
</td>
</tr>
</table>

Many things to see here, so let's focus on the important line:

<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
if="${not #lists.isEmpty(prod.comments)}">view</a>

This will create a link to the comments page (with URL /product/comments) com um prodId parameter defined as the
idof the product, but only if the product has some comment.

Let's take a look at the resulting marking:

<table>
<tr>
<th>NAME</th>
PRICE
IN STOCK
<th>COMMENTS</th>
</tr>
<tr>
Fresh Sweet Basil
4.99
yes
<td>
0 comment/s
</td>
</tr>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 35/94
01/08/2020 Tutorial: Using Thymeleaf
<tr class="odd">
Italian Tomato
1.25
no
<td>
2 comment/s
<a href="/gtvg/product/comments?prodId=2">view</a>
</td>
</tr>
<tr>
Yellow Bell Pepper
2.50
yes
<td>
0 comment/s
</td>
</tr>
<tr class="odd">
Old Cheddar
18.75
yes
<td>
1 comment/s
<a href="/gtvg/product/comments?prodId=4">view</a>
</td>
</tr>
</table>

Perfect! It was exactly what we wanted.

Note that theth:ifthe attribute will not only evaluate boolean conditions. Its capabilities go a bit beyond that and will evaluate
the specified expression of thetruefollowing way:

If the value is not null:

If the value is a boolean and it istrue.


If the value is a number and is different from zero
If the value is a character and is different from zero
If the value is a String and is not 'false', 'o ' or 'no'
If the value is not a boolean, a number, a character, or a String.

(If the value is null, th: if will be evaluated as false).

Furthermore,th:ifhas an inverse attributeth:unlessthat we could have used in the previous example instead of using a
notwithin the OGNL expression:

<a href="comments.html"
th:href="@{/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>

7.2 Exchange Instructions

There is also a way to display the content conditionally using the equivalent of a decommutative structure.
Java: the setth:switch/ caseattribute.

<divth:switch="${user.role}">
<pth:case="'admin'">User is an administrator</p>
User is a manager
</div>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 36/94
01/08/2020 Tutorial: Using Thymeleaf

Note that as soon as acaseattribute is evaluated astrue, all the otherscaseattributes in the same context
the switches are evaluated asfalse.

The default option is specified asth:case="*":

<divth:switch="${user.role}">
User is an administrator
User is a manager
User is some other thing
</div>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 37/94
01/08/2020 Tutorial: Using Thymeleaf

8 Model layout

8.1 Including model fragments

Defining and referencing fragments

In our templates, we usually want to include parts from other templates, parts like footers, headers, menus ...

To do this, Thymeleaf requires us to define these parts, "fragments", for inclusion, which can be done using the
th:fragmentattribute

Let's say we want to add a standard copyright footer to all our shopping pages, so
we created a/WEB-INF/templates/footer.htmlfile that contains this code:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<body>

<divth:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>

</body>

</html>

The code above defines a fragment calledcopywhat we can easily include on our homepage using one of the
attributesth:insertorth:replace(and alsoth:include, although its use is no longer recommended since Thymeleaf
3.0):

<body>

...

<divth:insert="~{footer :: copy}"></div>

</body>

Note thatth:insert wait a fragment expression~{...} ), which is an expression that results in a fragment. In
example above, however, that is an expression of a non-complex fragment, the attachment (~{ , } ) is completely optional,
therefore the above code would be equivalent to:

<body>

...

<divth:insert="footer :: copy"></div>

</body>

Fragment specification syntax

The syntax of fragment expressions is quite straightforward. There are three different formats:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 38/94
01/08/2020 Tutorial: Using Thymeleaf

~{templatename::selector}Includes the resulting fragment from applying the specified markup selector on
named modeltemplatenameNote thatselectorit can be just a mere name of a fragment; therefore, you can
specify something as simple as~{templatename::fragmentname}o~{footer :: copy}described above.

The syntax of the Markup Selector is defined by the underlying AttoParser analysis library and is similar to the
XPath expressions or CSS selectors. See Appendix C for more information.

~{templatename}Includes the complete named modeltemplatename.

Note that the name of the model used inth:insert/ th:replacetags will have to be resolved by the Resolver
models currently being used by the Model Mechanism.

~{::selector}or~{this::selector}Insert a fragment of the same model, correspondingselectorIf not


found in the model in which the expression appears, the model call stack (insertions) is traversed downwards
to the originally processed model (root), untilselectorto correspond at some level.

BothtemplatenameeselectorIn the examples above, there can be expressions with complete resources (even conditionals!)
As:

<divth:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div>

Observe again how the~{...}envelope around is optional ininsert/ th:replace.

Fragments may include anyth:*attribute. These attributes will be evaluated as soon as the fragment is included in
destination model (the one with the attributeth:insert/ th:replace) and may refer to any variables of
context of the defined in this destination model.

A great advantage of this approach to fragments is that you can record your fragments on pages.
perfectly displayed by a browser, with a complete and even valid markup structure, maintaining the
ability to make Thymeleaf include them in other templates.

Referencing fragments withoutth:fragment

Thanks to the power of markup selectors, we can include fragments that do not useth:fragmentattributes. It can even be
a markup code coming from a different application without the knowledge of Thymeleaf:

...
<divid="copy-section">
© 2011 The Good Thymes Virtual Grocery
</div>
...

We can use the above fragment simply by referencing it by itsidattribute, in a way similar to a selector
CSS:

<body>

...

<divth:insert="~{footer :: #copy-section}"></div>

</body>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 39/94
01/08/2020 Tutorial: Using Thymeleaf

Difference betweenth:inserteth:replace(eth:include)

And what is the difference betweenth:inserteth:replace(eth:includenot recommended since 3.0)?

th:insertit's the simplest: it simply inserts the specified fragment as the body of the host tag.

th:replaceactually, replace the host tag with the specified fragment.

th:includeis similar toth:insert , but instead of inserting the fragment, it only inserts the content of this
fragment.

Therefore, an HTML fragment like this:

<footerth:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</footer>

...Included three times in<div> host tags, like this:

<body>

...

<divth:insert="footer :: copy"></div>

<divth:replace="footer :: copy"></div>

<divth:include="footer :: copy"></div>

</body>

...will result in:

<body>

...

<div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</div>

<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>

<div>
© 2011 The Good Thymes Virtual Grocery
</div>

</body>

8.2 Assinaturas de fragmentos parametrizáveis

To create a mechanism more similar to a function for model fragments, the defined fragments with
th:fragmentwe can specify a set of parameters:

<divth:fragment="frag (onevar,twovar)">
${onevar} + '-' + ${twovar}
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 40/94
01/08/2020 Tutorial: Using Thymeleaf
</div>

This requires the use of one of these two syntaxes to call the fragment ofth:insertorth:replace:

<divth:replace="::frag (${value1},${value2})">...</div>
<divth:replace="::frag (onevar=${value1},twovar=${value2})">...</div>

Note that the request is not important in the last option:

<divth:replace="::frag (twovar=${value2},onevar=${value1})">...</div>

Fragment local variables without fragment arguments

Even if fragments are defined without arguments like this:

<divth:fragment="frag">
...
</div>

We could use the second syntax specified above to call them (and only the second):

<divth:replace="::frag (onevar=${value1},twovar=${value2})">

This would be equivalent to a combination ofth:replaceewith:

<div th:replace="::frag" th:with="onevar=${value1}, twovar=${value2}">

Notice this specification of local variables for a fragment - regardless of whether it has a signature or not.
argument - does not cause the context to be emptied before its execution. The fragments will still be able to access all
the context variables that are being used in the call model as they currently are.

to arrange for assertions in the model

O th:assertattribute can specify a list of comma-separated expressions that must be evaluated and true for
each evaluation, generating an exception, if not.

<divth:assert="${onevar},(${twovar} !=43)">...</div>

This is useful for validating parameters in a fragment signature:

<header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>

8.3 Flexible layouts: beyond mere insertion of fragments

Thanks to fragment expressions, we can specify parameters for fragments that are not texts, numbers, or objects.
from the bean ... but fragments of marking.

This allows us to create our fragments in such a way that they can be enriched with the markup from the models of
call, resulting in a very flexible model layout mechanism.

Observe the use of variablestitleandlinksin the fragment below:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 41/94
01/08/2020 Tutorial: Using Thymeleaf

<headth:fragment="common_header(title,links)">

<titleth:replace="${title}">The awesome application</title>

Common styles and scripts


<link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}">
<link rel="shortcut icon" th:href="@{/images/favicon.ico}">
<script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script>

Per-page placeholder for additional links


<th:blockth:replace="${links}" />

</head>

Now we can call this fragment as:

...
<headth:replace="base :: common_header(~{::title},~{::link})">

<title>Awesome - Main</title>

<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">


<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">

</head>
...

... And the result will use the real tags.<title>e <link>in our call model how the variable valuestitlee
linksresulting in our fragment being customized during insertion:

...
<head>

<title>Awesome - Main</title>

Common styles and scripts


<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<scripttype="text/javascript"src="/awe/sh/scripts/codebase.js"></script>

<link rel="stylesheet" href="/awe/css/bootstrap.min.css">


<linkrel="stylesheet"href="/awe/themes/smoothness/jquery-ui.css">

</head>
...

Using the empty fragment

A special fragment expression, or empty fragment~{}It can be used to specify any marking. Using
the previous example:

<headth:replace="base :: common_header(~{::title},~{})">

<title>Awesome - Main</title>

</head>
...

Observe how the second parameter of the fragment (links) is defined as an empty fragment and, therefore, nothing is written to
o <th:block th:replace="${links}" />block:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 42/94
01/08/2020 Tutorial: Using Thymeleaf

...
<head>

<title>Awesome - Main</title>

Common styles and scripts


<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<scripttype="text/javascript"src="/awe/sh/scripts/codebase.js"></script>

</head>
...

Using the token without operation

The no-op can also be used as a parameter for a fragment if we only want to allow our fragment
use your current markup as a default value. Again, using thecommon_header example:

...
<headth:replace="base :: common_header(_,~{::link})">

<title>Awesome - Main</title>

<linkrel="stylesheet"th:href="@{/css/bootstrap.min.css}">
<linkrel="stylesheet"th:href="@{/themes/smoothness/jquery-ui.css}">

</head>
...

See how the title argument (first argument of thecommon header(fragment) is defined as no-op_ ), which makes with
that this part of the fragment is not executed ( title = =without operation):

<titleth:replace="${title}">The awesome application</title>

So the result is:

...
<head>

<title>The awesome application</title>

Common styles and scripts


<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<scripttype="text/javascript"src="/awe/sh/scripts/codebase.js"></script>

<link rel="stylesheet" href="/awe/css/bootstrap.min.css">


<link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css">

</head>
...

Advanced conditional insertion of fragments

The availability of the empty fragment of the token without operations allows for the conditional insertion of fragments of
a very easy and elegant way.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 43/94
01/08/2020 Tutorial: Using Thymeleaf

For example, we could do this to insert ourcommon :: adminheadfragmentoapenasse the user for a
administrator and do not insert anything (empty fragment) if not:

...
...
...

Additionally, we can use the non-operation token to insert a fragment only if the specified condition is met.
attended, but leave the marking unchanged if the condition is not met:

...
${user.isAdmin()} ? ~{common :: adminhead} : _
Welcome [[${user.name}]], click <ath:href="@{/support}">here</a> for help-desk support.
</div>
...

Furthermore, if we configure our model resolvers to verify the existence of model resources - through
seu checkExistenceindicator -, we can use the existence of the fragment itself as a condition in a standard operation:

...
The body of the <div> will be used if the "common :: salutation" fragment
does not exist (or is empty). -->
<divth:insert="~{common :: salutation} ?: _">
Welcome [[${user.name}]], click <ath:href="@{/support}">here</a> for help-desk support.
</div>
...

8.4 Removing model fragments

Back to the sample app, let's revisit the latest version of our product list model:

<table>
<tr>
<th>NAME</th>
PRICE
IN STOCK
<th>COMMENTS</th>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.odd}?'odd'">
Onions
2.41
<tdth:text="${prod.inStock}? #{true} : #{false}">yes</td>
<td>
2 comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
unless="${#lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
</table>

This code is excellent as a model, but as a static page (when opened directly by a browser without the
Thymeleaf process it), it wouldn't be a good prototype.

Why? Because, although perfectly displayable by browsers, this table has only one row and that row has
simulated data. As a prototype, it just wouldn't seem realistic enough... we should have more than one product,
we need more lines.

So, let's add some:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 44/94
01/08/2020 Tutorial: Using Thymeleaf

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
IN STOCK
<th>COMMENTS</th>
</tr>
<trth:each="prod : ${prods}"th:class="${prodStat.odd}?'odd'">
Onions
2.41
yes
<td>
2 comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
<tr class="odd">
Blue Lettuce
9.55
no
<td>
0 comment/s
</td>
</tr>
<tr>
Mild Cinnamon
1.99
yes
<td>
3 comment/s
<a href="comments.html">view</a>
</td>
</tr>
</table>

Ok, now we have three, definitely better for a prototype. But... what will happen when we process it with the
Thymeleaf ?:

<table>
<tr>
NAME
PRICE
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr>
Fresh Sweet Basil
4.99
yes
<td>
0 comment/s
</td>
</tr>
<trclass="odd">
Italian Tomato
1.25
no
<td>
2 comment/s
<a href="/gtvg/product/comments?prodId=2">view</a>
</td>
</tr>
<tr>
Yellow Bell Pepper
2.50
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 45/94
01/08/2020 Tutorial: Using Thymeleaf
yes
<td>
0 comment/s
</td>
</tr>
<tr class="odd">
Old Cheddar
18.75
yes
<td>
1 comment/s
<a href="/gtvg/product/comments?prodId=4">view</a>
</td>
</tr>
<tr class="odd">
Blue Lettuce
9.55
no
<td>
0 comment/s
</td>
</tr>
<tr>
Mild Cinnamon
1.99
yes
<td>
3 comment/s
<a href="comments.html">view</a>
</td>
</tr>
</table>

The last two lines are simulated lines! Well, of course they are: the iteration was applied only to the first line; therefore,
there is no reason for Thymeleaf to have removed the other two.

We need a way to remove these two lines during model processing. Let's use the
th:removeattribute in the second and third<tr>tags:

<table>
<tr>
<th>NAME</th>
PRICE
IN STOCK
<th>COMMENTS</th>
</tr>
<trth:each="prod : ${prods}"th:class="${prodStat.odd}?'odd'">
Onions
2.41
<tdth:text="${prod.inStock}? #{true} : #{false}">yes</td>
<td>
2 comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
view
</td>
</tr>
<tr class="odd" th:remove="all">
Blue Lettuce
9.55
no
<td>
0 comment/s
</td>
</tr>
<trth:remove="all">
Mild Cinnamon

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 46/94
01/08/2020 Tutorial: Usando o Thymeleaf
1.99
yes
<td>
3 comment/s
<a href="comments.html">view</a>
</td>
</tr>
</table>

After processing, everything will look as it should again:

<table>
<tr>
NAME
PRICE
IN STOCK
<th>COMMENTS</th>
</tr>
<tr>
Fresh Sweet Basil
<td>4.99</td>
yes
<td>
0 comment/s
</td>
</tr>
<tr class="odd">
Italian Tomato
1.25
no
<td>
2 comment/s
view
</td>
</tr>
<tr>
Yellow Bell Pepper
2.50
yes
<td>
0 comment/s
</td>
</tr>
<tr class="odd">
Old Cheddar
18.75
yes
<td>
1 comment/s
view
</td>
</tr>
</table>

And what is thisallWhat does the value in the attribute mean?th:removeit can behave in five different ways, depending on your
valor:

allRemove the tag that contains it and all its children.


bodyDo not remove the tag that contains, but remove all its eyes.
tagRemove the tag that contains, but do not remove its children.
all-but-firstRemove all eyes from the tag that contains, except for the first.
noneDo nothing. This value is useful for dynamic evaluation.

For thisall-but-firstCan value be useful? This will allow us to save a little.th:remove="all"in the creation of
prototypes:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 47/94
01/08/2020 Tutorial: Using Thymeleaf

<table>
<thead>
<tr>
<th>NAME</th>
PRICE
IN STOCK
<th>COMMENTS</th>
</tr>
</thead>
tbodyth:remove="all-but-first"
<trth:each="prod : ${prods}" th:class="${prodStat.odd}?'odd'">
Onions
2.41
yes
<td>
2 comment/s
<ahref="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
view
</td>
</tr>
<trclass="odd">
Blue Lettuce
9.55
no
<td>
0 comment/s
</td>
</tr>
<tr>
Mild Cinnamon
<td>1.99</td>
yes
<td>
3 comment/s
<a href="comments.html">view</a>
</td>
</tr>
</tbody>
</table>

Theremovethe attribute can take any standard Thymeleaf Expression, as long as it returns one of the allowed values from
cordall, tag, body, all-but-firstornone).

This means that removals can be conditional, such as:

<a href="/something" th:remove="${condition}? tag : none">Link text not to be removed</a>

Also note thatth:removeconsidernull a synonym fornone , therefore, the following works the same way as
the above example:

<a href="/something" th:remove="${condition}? tag">Link text not to be removed</a>

In this case, if${condition}for false,null it will be returned and, therefore, no removal will be executed.

8.5 Layout inheritance

In order to have a single file as a layout, fragments can be used. An example of a simple layout withtitle e
contentusingth:fragmenteth:replace:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 48/94
01/08/2020 Tutorial: Using Thymeleaf

<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
<titleth:replace="${title}">Layout Title</title>
</head>
<body>
Layout H1
<divth:replace="${content}">
Layout content
</div>
<footer>
Layout footer
</footer>
</body>
</html>

This example declares a fragment called layout with title and content as parameters. Both will be replaced in
page that inherited it through the fragment expressions provided in the example below.

<!DOCTYPE html>
<html th:replace="~{layoutFile :: layout(~{::title}, ~{::section})}">
<head>
<title>Page Title</title>
</head>
<body>
<section>
Page content
Included on page
</section>
</body>
</html>

In this file, thehtmltag will be replaced by layout, but in the layouttitle econtentwill be replaced bytitlee
sectionblocks, respectively.

If desired, the layout can be composed of several fragments such as header and footer.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 49/94
01/08/2020 Tutorial: Using Thymeleaf

9 Local Variables
Thymeleaf calls local variables that are defined for a specific fragment of a model and are available
just for evaluation within this fragment.

An example we have already seen is theprodvariable iter in our product list page:

<trth:each="prod : ${prods}">
...
</tr>

Thisprodthe variable will be available only within the limits of the<tr>tag. Specifically:

He will be available for any others.th:*attributes executed in this tag with lower precedence than
th:each(which means they will be executed later)each).
He will be available for any element.<tr>tag, like any<td>element.

Thymeleaf offers a way to declare local variables without iteration, using theth:withattribute, and its syntax is
similar to that of attribute value assignments:

<divth:with="firstPer=${persons[0]}">
<p>
The name of the first person is Julius Caesar.
</p>
</div>

Whenwithit is processed, thisfirstPervariable is created as a local variable and added to the variable map
originating from the context, to be available for assessment along with other variables stated in the context,
but only within the limits of the<div>mark that contains it.

You can define multiple variables at the same time using the usual multiple assignment syntax:

<divth:with="firstPer=${persons[0]},secondPer=${persons[1]}">
<p>
The name of the first person is Julius Caesar.
</p>
<p>
But the name of the second person is
Marcus Antonius
</p>
</div>

Theth:withattribute allows reusing variables defined in the same attribute:

<divth:with="company=${user.company +'Co.'},account=${accounts[company]}">...</div>

Let's use this on the homepage of our supermarket! Remember the code we wrote to generate a formatted date?

<p>
Today is:
February 13, 2011
</p>

Well, what if we wanted thatdd MMMM yyyyDid it really depend on the location? For example, can we add the
following message to ourhome_en.properties:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 50/94
01/08/2020 Tutorial: Using Thymeleaf

date.format=MMMM dd'','' yyyy

... And is equivalent to ourhome_es.properties :

date.format=dd ''de'' MMMM'','' yyyy

Now, let's usewithto obtain the localized date format in a variable and use it in ourtextexpression:

<pth:with="df=#{date.format}">
Today is: 13 February 2011
</p>

This was clear and easy. In fact, considering that the valuewithis superiorprecedenceath:text we could have solved
all of this in thespantag:

<p>
Today is:
<span th:with="df=#{date.format}">
February 13, 2011
</p>

You might be thinking: Precedence? We haven't talked about that yet! Well, don't worry, because it's exactly
This is what the next chapter is about.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 51/94
01/08/2020 Tutorial: Using Thymeleaf

10 Precedence of Attributes
What happens when you write more than oneth:* attribute in the same tag? For example:

[]
Item description here...
</ul>

We hope that thisth:eachattribute should be executed beforeth:textto obtain the desired results, but considering
that the HTML / XML standards do not give any significance to the order in which attributes in a tag are written, a
The precedence mechanism needed to be established in the attributes themselves to ensure that it worked according to the
expected.

Therefore, all the Thymeleaf attributes have a numerical precedence, which establishes the order in which they are
executed in the tag. This order is:

Order Feature Attributes

1 Inclusion of fragments th:insert


th:replace
2 Fragment iteration th:each
3 Conditional assessment th:if
th:unless
th:switch
th:case
4 Definition of a local variable th:object
with
5 General attribute modification th:attr
th:attrprepend
th:attrappend
6 Modification of specific attribute th:value
th:href
th:src
...
7 Text (modification of the tag body) th:text
th:utext
8 Fragment specification th:fragment
9 Removal of fragments th:remove

This precedence mechanism means that the iteration fragment above will yield exactly the same results if
the position of the for attribute inverted (although it is a little less readable):

<ul>
Item description here...
</ul>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 52/94
01/08/2020 Tutorial: Using Thymeleaf

11 Comments and Blocks

11.1 Standard HTML / XML Comments

Standard comments in HTML / XML<!-- ... -->can be used anywhere in Thymeleaf templates. Any
Anything inside these comments will not be processed by Thymeleaf and will be copied verbatim to the result.

<!-- User info follows -->


${...}
...
</div>

11.2 Comment blocks at the Thymeleaf parser level

The comments blocks at the parser level are codes that will simply be removed from the model when the
Thymeleaf or analyze. They look like this:

This code will be removed at Thymeleaf parsing time!

Thymeleaf will remove everything between<!--/*e */-->therefore, these comment blocks can also be used to
display code when a model is statically open, knowing that it will be removed when Thymeleaf does
process

<!--/*-->
<div>
you can see me only before Thymeleaf processes me!
</div>
<!--*/-->

This can be very useful for creating prototypes of tables with many<tr>, for example:

<table>
<trth:each="x : ${xs}">
...
</tr>
<!--/*-->
<tr>
...
</tr>
<tr>
...
</tr>
<!--*/-->
</table>

11.3 Comment blocks only for the Thymeleaf prototype

Thymeleaf allows the definition of special comment blocks marked as comments when the template is opened.
statically (that is, as a prototype), but considered normal markup by Thymeleaf when executing the model.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 53/94
01/08/2020 Tutorial: Using Thymeleaf

hello!
<!--/*/
<div th:text="${...}">
...
</div>
/*/-->
goodbye!

The Thymeleaf analysis system will simply remove the markers.<!--/*/ e/*/--> , but not its content, which
It is not commented, therefore. Therefore, when executing the model, Thymeleaf will see the following:

hello!

${...}
...
</div>

goodbye!

Just like in comment blocks at the parser level, this feature is independent of the dialect.

11.4th:blockSynthetic label

The only element processor of Thymeleaf (not an attribute) included in the Standard Dialects isth:block.

th:blockit's just a mere container of attributes that allows model developers to specify the attributes that
They want. Thymeleaf will execute these attributes and simply make the block, but not the content, disappear.

Therefore, it can be useful, for example, when creating iterated tables that require more than one<tr>for each element:

<table>
<th:blockth:each="user : ${users}">
<tr>
${user.login}
...${user.name}...
</tr>
<tr>
<td colspan="2" th:text="${user.address}">...</td>
</tr>
</th:block>
</table>

It is especially useful when used in combination with comment blocks just for prototypes:

<table>
<th:block th:each="user : ${users}">
<tr>
${user.login}
...<td th:text="${user.name}">...</td>
</tr>
<tr>
<tdcolspan="2"th:text="${user.address}">...</td>
</tr>
<!--/*/ </th:block> /*/-->
</table>

Observe how this solution allows the models to be valid HTML (there's no need to add<div> blocks
forbidden inside<table>) and it still works well when opened statically in browsers as prototypes!

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 54/94
01/08/2020 Tutorial: Using Thymeleaf

12 Inlining

12.1 Embedded expression

Although the Standard Dialect allows us to do almost everything using tag attributes, there are situations where we might prefer
write expressions directly in our HTML texts. For example, we might prefer to write this:

<p>Hello, [[${session.user.name}]]!</p>

... Instead:

<p>Hello, <spanth:text="${session.user.name}">Sebastian</span>!</p>

Expressions between[[...]]or[(...)]are considered embedded expressions in Thymeleaf and, within them, we can use
any type of expression that is also valid in an attributetextorunknown text.

Note that, although[[...]]correspond toth:text(that is, the result will have HTML escape)[(...)]correspond
th:utextand will not execute any HTML escape. So, with a variable likemsg = 'This is <b>great!</b>', given
this fragment:

<p>The message is "[(${msg})]"</p>

The result will have these<b>tags without escape, therefore:

<p>The message is "This is <b>great!</b>"</p>

Considering that it escaped as:

<p>The message is "[[${msg}]]"</p>

O resultado terá escape HTML:

<p>The message is "This is &lt;b&gt;great!&lt;/b&gt;"</p>

Note that text inlining is active by default in the body of all the tags of our markup - and not in the tags themselves.
tags -, so there is nothing we need to do to enable it.

Inline models vs natural models

If you come from other model mechanisms where this way of generating text is the norm, you may be feeling
asking: Why didn't we do this from the beginning? It's less code than all of thisth:textattributes!

Well, be careful there, because while you may find inlining quite interesting, always remember that expressions
incorporated will be displayed literally in your HTML files when you open them statically, so that you
probably won't be able to use them as design prototypes. no more!

The difference between how a browser statically rendered our code snippet without using inlining...

Hello, Sebastian!

... and use it ...

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 55/94
01/08/2020 Tutorial: Using Thymeleaf

Hello, [[${session.user.name}]]!

... is quite clear in terms of design utility.

Disabling inlining

However, this mechanism can be disabled, as there may be occasions when we want to generate the sequences.[[...]] you
[(...)] without its content being processed as an expression. For this, we will useth:inline="none":

A double array looks like this: [[1, 2, 3], [4, 5]]!

This will result in:

A double array looks like this: [[1, 2, 3], [4, 5]]!

12.2 Embedded text

The embedding of text is very similar to the embedded expressiveness that we have just seen, but in fact
add more power. It must be explicitly activated withth:inline="text".

The embedding of text not only allows us to use the same embedded expressions we just saw, but in fact
process the bodies of the tags as if they were processed models in theTEXTmodel mode, which allows us to execute the
logic of the text-based model (not just output expressions).

We will see more about this in the next chapter on text model modes.

12.3 Embedded JavaScript

The embedding of JavaScript allows for better integration of<script>JavaScript blocks in the models that are being
processed inHTMLmodel mode.

As embedded text, this is really equivalent to processing the content of scripts as if they were templates
noJAVASCRIPTmodel mode and, therefore, all the power of text model modes (see the next chapter) will be
available. However, in this section, we will focus on how we can use it to add the output of our expressions
Thymeleaf in our JavaScript blocks.

This mode must be explicitly activated usingth:inline="javascript":

<script th:inline="javascript">
...
var username = [[${session.user.name}]];
...
</script>

This will result in:

<script th:inline="javascript">
...
var username = "Sebastian \"Fruity\" Applejuice";
...
</script>

Two important things to note in the code above:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 56/94
01/08/2020 Tutorial: Using Thymeleaf

First, this embedded JavaScript will not only produce the necessary text but also include it in quotes and escape it.
In JavaScript, for the results of the expression to be displayed as a well-formed JavaScript literal.

Secondly, this is happening because we are leaving the${session.user.name} expression like escaped, or
be, using a double support expression:[[${session.user.name}]]If instead we use the escape without escape,
as:

<scriptth:inline="javascript">
...
var username = [(${session.user.name})];
...
</script>

The result would be similar to:

<script th:inline="javascript">
...
var username = Sebastian "Fruity" Applejuice;
...
</script>

... What is a malformed JavaScript code. But producing something without escape might be what we need if we are
construindo partes do nosso script por meio da adição de expressões embutidas, por isso é bom ter essa ferramenta à mão.

Natural models of JavaScript

The mentioned intelligence of the embedded mechanism of JavaScript goes far beyond the application of expression results.
escape and specific output of JavaScript as valid literals.

For example, we can group our embedded (escaped) expressions in JavaScript comments, such as:

<script th:inline="javascript">
...
var username = /*[[${session.user.name}]]*/ "Gertrud Kiwifruit";
...
</script>

And Thymeleaf will ignore everything we wrote after the comment and before the semicolon (in this caseGertrude
KiwifruitTherefore, the result of the execution will be exactly the same as when we were not using the comments.
nais:

<script th:inline="javascript">
...
var username = "Sebastian \"Fruity\" Applejuice";
...
</script>

But take another careful look at the code of the original model:

<script th:inline="javascript">
...
var username = /*[[${session.user.name}]]*/ "Gertrud Kiwifruit";
...
</script>

Notice how this is valid JavaScript code. And it will run perfectly when you open your file of
static model (without executing it on a server).

So, what we have here is a way to create natural models in JavaScript!

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 57/94
01/08/2020 Tutorial: Usando o Thymeleaf

Advanced evaluation and serialization of JavaScript

One important thing to note about the insertion of JavaScript is that this expression evaluation is smart and
is not limited to Strings. Thymeleaf will correctly write the following types of objects in JavaScript syntax:

Strings
Numbers
Booleans
Matrices
Collections
Maps
Beans (objects with getter and setter methods)

For example, if we had the following code:

<script th:inline="javascript">
...
var user = /*[[${session.user}]]*/ null;
...
</script>

This${session.user}expression will be evaluated in aUserthe object and Thymeleaf will convert it correctly into the syntax
Javascript:

<script th:inline="javascript">
...
var user = {"age":null,"firstName":"John","lastName":"Apricot",
"name":"John Apricot","nationality":"Antarctica"};
...
</script>

The way this JavaScript serialization is done is through an implementation of a


org.thymeleaf.standard.serializer.IStandardJavaScriptSerializerinterface, which can be configured in the instance of
Standard Dialectuse in the model mechanism.

The default implementation of this JS serialization mechanism will look for theJackson libraryon the class path and, if
there will be, it will use. Otherwise, it will apply an internal serialization mechanism that meets the needs of most
scenarios and produce similar results (but it is less flexible).

12.4 Embedded CSS

Thymeleaf also allows the use of inlining in<style> CSS tags, such as:

<style th:inline="css">
...
</style>

For example, let's say we have two variables defined for twoStringvalores diferentes :

classname = 'main elems'


align = 'center'

We could use them as:

<styleth:inline="css">
.[[${classname}]] {
text-align: [[${align}]];
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 58/94
01/08/2020 Tutorial: Using Thymeleaf
}
</style>

And the result would be:

<styleth:inline="css">
.main elems {
text-align: center;
}
</style>

Notice how CSS inlining also has some intelligence, just like JavaScript. Specifically, expressions
issued by escaped expressions such as[[${classname}]]serão escapadas comoidenti cadores CSS. É por isso que o
ourclassname = 'main elems'becamemain elementsin the above code fragment.

Advanced resources: natural CSS models etc.

In a manner equivalent to what was previously explained for JavaScript, CSS inlining also allows our<style>tags
function statically and dynamically, that is, as natural models of CSS by grouping expressions
embedded in the comments. I see:

<styleth:inline="css">
.main elems {
text-align: left;
}
</style>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 59/94
01/08/2020 Tutorial: Using Thymeleaf

13 modes of textual model

13.1 Textual syntax

Three of the Thymeleaf model modes are considered textual:TEXT, JAVASCRIPTeCSSThis differentiates them from the modes of
markup model:HTMLeXML.

The main difference between textual models and markup models is that, in a textual model, there are no tags in which
insert logic in the form of attributes; therefore, we need to rely on other mechanisms.

The first and most basic of these mechanisms is inlining, which we detailed in the previous chapter. The embedded syntax is the
simplest way to generate expression results in text mode, therefore, this is a model
perfectly valid for a text email.

Dear [(${name})],

Please find attached the results of the report you requested.


with name "[(${report.name})]".

Sincerely,
The Reporter.

Even without tags, the above example is a complete and valid Thymeleaf model that can be executed inTEXTway of
model.

But, to include a more complex logic than mere output expressions, we need a new syntax not based on
in tag:

[# th:each="item : ${items}"]
- [(${item})]
[/]

What is really the condensed version of the most detailed one?

[#th:block th:each="item : ${items}"]


- [#th:block th:utext="${item}" /]
[/th:block]

Observe how this new syntax is based on elements (i.e., processable tags) that are declared as in
[#element ...]instead of<element ...>Elements are open like[#element ...]it is closed like[/element], and
independent brands can be declared, minimizing the open element with a/ in a way almost equivalent to
tags XML:#element ... /.

The Standard Dialect contains only one processor for one of these elements: the already knownth:block, although we can
extend this in our dialects and create new elements in the usual way. Additionally, theth:blockelement[#th:block
...] ... [/th:block]) can be abbreviated as the empty string ( [# ...] ... [/]), therefore, the block above is really
equivalent to:

[# th:each="item : ${items}"]
- [# th:utext="${item}" /]
[/]

It is given[#
th:utext="${item}" /]it is equivalent to an embedded unescaped expression, we could just use it to have
less code. Thus, we end up with the first code snippet we saw above:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 60/94
01/08/2020 Tutorial: Using Thymeleaf

[# th:each="item : ${items}"]
- [(${item})]
[/]

Note that textual syntax requires total balance of elements (no unclosed tags) and attributes in quotes - it is more in
XML style than HTML style.

Let's take a look at a more complete example of aTEXTmodel, a model of email in plain text:

Dear [(${customer.name})],

This is the list of our products:

# th:each="prod : ${products}"
[(${prod.name})]. Price: [(${prod.price})] EUR/kg
[/]

Thanks,
The Thymeleaf Shop

After execution, the result of this can be something like:

Dear Mary Ann Blueberry,

This is the list of our products:

Apricots. Price: 1.12 EUR/kg


Bananas. Price: 1.78 EUR/kg
- Apples. Price: 0.85 EUR/kg
- Watermelon. Price: 1.91 EUR/kg

Thanks,
The Thymeleaf Shop

And another example inJAVASCRIPT mode model, agreeter.jsfile, we processed as text model and whose result
we call our HTML pages. Note that this is not a<script>block in an HTML model, but a.jsfile
being processed as a model by itself:

var greeter = function() {

var username = [[${session.user.name}]];

[# th:each="salut : ${salutations}"]
alert([[${greeting}]] + " " + username);
[/]

};

After execution, the result of this can be something like:

var greeter = function() {

var username = "Bertrand \"Crunchy\" Pear";

alert("Hello" + " " + username);


alert("Hello" + " " + username);
alert("Hello" + " " + username);

};

Element attributes with escape


https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 61/94
01/08/2020 Tutorial: Using Thymeleaf

To avoid interactions with parts of the model that can be processed in other modes (for example,text-mode
embedded within aHTMLmodel), Thymeleaf 3.0 allows attributes in the elements in its textual syntax to be
escaped. Like this:

The attributes inTEXTmodel mode will have an escape without HTML.


The attributes inJAVASCRIPTmodel mode will not have JavaScript escape.
The attributes inCSSmodel mode will not have CSS.

Therefore, that would be perfectly fine in aTEXTmodel -mode (observe the>):

[# th:if="${120<user.age}"]
Congratulations!
[/]

Of course that<this wouldn't make sense in a real text model, but it's a good idea if we are processing a
HTML model with ath:inline="text"block that contains the code above and we want to ensure that our browser does not
accept this<user.agelike the name of an open tag when opening the file statically as a prototype.

13.2 Extensibility

One of the advantages of this syntax is that it is as extensible as markup. Developers can still define
your own dialects with custom elements and attributes, optionally apply a prefix to them and use them in
text model modes:

some text

13.3 Comment blocks only for textual prototypes: adding code

The modesJAVASCRIPTandCSSmodel (not available forTEXTallows to include code within a comment syntax
special/*[+...+]*/ for Thymeleaf to automatically uncomment this code when processing the model:

var x = 23;

/*[+

var msg = "This is a working application";

+]*/

var f = function() {
...

It will be executed as:

var x = 23;

var msg = "This is a working application";

var f = function() {
...

You can include expressions within these comments, and they will be evaluated:

var x = 23;

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 62/94
01/08/2020 Tutorial: Using Thymeleaf
/*[+

var msg = "Hello, " + [[${session.user.name}]];

+]*/

var f = function() {
...

13.4 Comment blocks at the text analyzer level: removing code

Similarly to the comment blocks only for prototypes, all three modes of text model (
TEXT, JAVASCRIPTeCSSallows you to instruct Thymeleaf to remove the code between tags/*[- */and/* -]*/brands
specials, like this:

var x = 23;

/*[- */

var msg = "This is shown only when executed statically!";

/* -]*/

var f = function() {
...

Or that, noTEXTmode:

...
Note the user is obtained from the session, which must exist.
Welcome [(${session.user.name})]!
...

13.5 JavaScript natural and CSS models

As seen in the previous chapter, JavaScript and CSS inlining offer the possibility of including embedded expressions in
JavaScript / CSS comments, such as:

...
var username = /*[[${session.user.name}]]*/ "Sebastian Lychee";
...

... What is valid JavaScript and, once executed, it may look like:

...
var username = "John Apricot";
...

This same trick of attaching embedded expressions within comments can actually be used for the entire syntax of
text mode:

/*[# th:if="${user.admin}"]*/
alert('Welcome admin');
/*[/]*/

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 63/94
01/08/2020 Tutorial: Usando o Thymeleaf

This alert in the code above will be shown when the model is opened statically - because it is 100% valid JavaScript - and
also when the model will be executed if the user is an administrator. It is equivalent to:

if user is admin
alert('Welcome admin');
[/]

What is really the code into which the initial version is converted during the model analysis.

Note, however, that the arrangement of elements in the comments does not clear the lines in which they reside (to the right until
that a; be found), like the embedded output expressions. This behavior is reserved only for
embedded exit expressions.

Therefore, Thymeleaf 3.0 allows for the development of complex JavaScript scripts and CSS stylesheets in the form of
natural models, valid as prototypes and as a working model.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 64/94
01/08/2020 Tutorial: Using Thymeleaf

14 More pages for our grocery store


Now that we know a lot about the use of Thymeleaf, we can add new pages to our site for management of
orders.

Note that we will focus on the HTML code, but you can take a look at the included source code if you want to see.
the corresponding drivers.

14.1 Lista de pedidos

Let's start by creating an order list page/WEB-INF/templates/order/list.html:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>

<title>Good Thymes Virtual Grocery</title>


<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all">
href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>

<body>

Order list

<table>
<tr>
DATE
CUSTOMER
TOTAL
<th></th>
</tr>
<trth:each="o : ${orders}"th:class="${oStat.odd}?'odd'">
<tdth:text="${#calendars.format(o.date,'dd/MMM/yyyy')}">13 jan 2011</td>
Frederic Tomato
23.32
<td>
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
</td>
</tr>
</table>

<p>
Return to home
</p>

</body>

</html>

There is nothing here that should surprise us, except for a bit of the magic of OGNL:

23.32

What this does is that, for each order line (OrderLineobject) of the order, multiply its propertiespurchasePrice e
amount(calling the correspondent)getPurchasePrice()and thegetAmount() methods) and return the result in a list of

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 65/94
01/08/2020 Tutorial: Using Thymeleaf

numbers, later added by the#aggregates.sum(...)function to obtain the total of the order price.

You need to love the power of OGNL.

14.2 Order details

Now, for the order details page, where we will make intensive use of asterisk syntax:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all">
href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>

<bodyth:object="${order}">

Order details

<div>
<p><b>Code:</b> <span th:text="*{id}">99</span></p>
<p>
<b>Date:</b>
13 Jan 2011
</p>
</div>

Customer

<divth:object="*{customer}">
<p><b>Name:</b> <spanth:text="*{name}">Frederic Tomato</span></p>
<p>
Since:
1 Jan 2011
</p>
</div>

Products

<table>
<tr>
<th>PRODUCT</th>
AMOUNT
PURCHASE PRICE
</tr>
<trth:each="ol,row : *{orderLines}"th:class="${row.odd}?'odd'">
Strawberries
3
23.32
</tr>
</table>

<div>
<b>TOTAL:</b>
35.23
</div>

<p>
Return to order list
</p>

</body>
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 66/94
01/08/2020 Tutorial: Using Thymeleaf

</html>

There isn't much new here, except for this selection of nested objects:

<bodyth:object="${order}">

...

<divth:object="*{customer}">
<p><b>Name:</b> <spanth:text="*{name}">Frederic Tomato</span></p>
...
</div>

...
</body>

... What makes this*{name} equivalent to:

<p><b>Name:</b> <spanth:text="${order.customer.name}">Frederic Tomato</span></p>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 67/94
01/08/2020 Tutorial: Using Thymeleaf

15 More about configuration

15.1 Model Solvers

For our Virtual Grocery Good Thymes, we chose aITemplateResolverimplementation called


ServletContextTemplateResolverwhich allowed us to obtain models as resources from the Servlet Context.

In addition to giving us the ability to create our own model resolver by implementing the
ITemplateResolver, Thymeleaf includes four ready-to-use implementations:

org.thymeleaf.templateresolver.ClassLoaderTemplateResolver, which resolves models as resources of the loader


classes, such as:

returnThread.currentThread().getContextClassLoader().getResourceAsStream(template);

org.thymeleaf.templateresolver.FileTemplateResolver, which resolves models like files in the file system,


like:

return newFileInputStream(newFile(template));

org.thymeleaf.templateresolver.UrlTemplateResolver, which resolves models like URLs (even non-local ones), such as:

return(newURL(template)).openStream();

org.thymeleaf.templateresolver.StringTemplateResolver, which solves models directly as Stringbeing


specified astemplate(the name of the model, which in this case is obviously much more than just a name):

return newStringReader(templateName);

All pre-packaged implementationsITemplateResolverallow the same set of configuration parameters,


which include:

Prexo and suxo (as already seen):

templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");

Model aliases that allow the use of model names that do not directly correspond to the file names.
If the suffix/prefix and the alias exist, the alias will be applied before the prefix/suffix:

templateResolver.addTemplateAlias("adminHome","profiles/admin/home");
templateResolver.setTemplateAliases(aliasesMap);

Coding to be applied when reading models:

templateResolver.setCharacterEncoding("UTF-8");

Model mode to be used:

// Default is HTML
templateResolver.setTemplateMode("XML");

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 68/94
01/08/2020 Tutorial: Using Thymeleaf

Standard mode for model caching and patterns for determining if specific models can be cached or
no

Default is true
templateResolver.setCacheable(false);
templateResolver.getCacheablePatternSpec().addPattern("/users/*");

TTL in milliseconds for cache entries of analyzed models originating from this model resolver. If not
If defined, the only way to remove an entry from the cache will be to exceed the maximum cache size (the entry
the oldest will be removed.

// Default is no TTL (only cache size exceeded would remove entries)


templateResolver.setCacheTTLMs(60000L);

The Thymeleaf + Spring integration packages offer aSpringResourceTemplateResolverimplementation that


use all the Spring infrastructure to access and read resources in applications and what is the recommended implementation in
applications enabled for Spring.

Chaining model resolvers

In addition, a Model Mechanism can specify several model resolvers; in this case, a request can be
established between them for the resolution of the model, so that if the first cannot solve the model, the second
it will be requested and so on:

ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver();


classLoaderTemplateResolver.setOrder(Integer.valueOf(1));

ServletContextTemplateResolver servletContextTemplateResolver =
newServletContextTemplateResolver(servletContext);
servletContextTemplateResolver.setOrder(Integer.valueOf(2));

templateEngine.addTemplateResolver(classLoaderTemplateResolver);
templateEngine.addTemplateResolver(servletContextTemplateResolver);

When multiple model resolvers are applied, it is advisable to specify patterns for each model resolver.
so that Thymeleaf can quickly discard those who do not intend to resolve the model, improving the
performance. Doing this is not a requirement, but a recommendation:

ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver();


classLoaderTemplateResolver.setOrder(Integer.valueOf(1));
// This classloader will not be even asked for any templates not matching these patterns
classLoaderTemplateResolver.getResolvablePatternSpec().addPattern("/layout/*.html");
classLoaderTemplateResolver.getResolvablePatternSpec().addPattern("/menu/*.html");

ServletContextTemplateResolver servletContextTemplateResolver =
newServletContextTemplateResolver(servletContext);
servletContextTemplateResolver.setOrder(Integer.valueOf(2));

If these resolvable standards are not specified, we will focus on the specific resources of each one.
ITemplateResolver implementations we are using. Note that not all implementations can determine the
the existence of a model before solving it and, therefore, they can always consider it solvable and interrupt the chain of
resolution (not allowing other solvers to verify the same model), but they cannot read the actual resource.

All of theITemplateResolverimplementations that are included with the Thymeleaf core include a mechanism that allows us
will allow the resolvers to really verify if there is a resource before considering it resolved. It is the
checkExistenceflag, which functions as:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 69/94
01/08/2020 Tutorial: Using Thymeleaf

ClassLoaderTemplateResolver classLoaderTemplateResolver =newClassLoaderTemplateResolver();


classLoaderTemplateResolver.setOrder(Integer.valueOf(1));
classLoaderTempalteResolver.setCheckExistence(true);

ThischeckExistenceThe signal forces the resolver to carry out a real verification of the existence of resources during the phase
de resolução (e deixar o seguinte resolvedor da cadeia ser chamado se a veri cação da existência retornar falsa). Embora isso
Although it may seem good in all cases, in most cases, this means a double access to the resource itself (once for
check the existence, read it again) and it can be a performance issue in some scenarios, for example, with
based on remote URL model resources - a potential performance issue that can be mitigated by using cache
of models (in this case, the models will be resolved only the first time they are accessed).

15.2 Message resolvers

We did not explicitly specify an implementation of the Message Resolver for our Grocery application and, as it was
explained earlier, this meant that the implementation used was a
org.thymeleaf.messageresolver.StandardMessageResolverobject.

StandardMessageResolverit is the standard implementation ofIMessageResolverinterface, but we could create our own, if
we wanted, adapted to the specific needs of our application.

The Thymeleaf + Spring integration packages offer a standardIMessageResolverimplementation that uses the
standard way of Spring to retrieve externalized messages, usingMessageSource beans declared in
Spring Application Context.

Default message resolver

So, howStandardMessageResolversearch for the requested messages in a specific model?

If the model name ishomeand it is located/WEB-INF/templates/home.html, and the code of the requested language
forgl_ESthis resolver will look for messages in the following files, in this order:

/WEB-INF/templates/home_gl_ES.properties
/WEB-INF/templates/home_gl.properties
/WEB-INF/templates/home.properties

Consult the JavaDoc documentation ofStandardMessageResolverclass to obtain more details about how the mechanism
complete message resolution works.

Configuring message resolvers

And if we wanted to add a message resolver (or more) to the Model Mechanism? Easy:

For setting only one


templateEngine.setMessageResolver(messageResolver);

For setting more than one


templateEngine.addMessageResolver(messageResolver);

And why do we want to have more than one message resolver? For the same reason we have model resolvers: the
message resolvers are requested, and if the first cannot resolve a specific message, the second will.
requested, the third, etc.

Conversion services
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 70/94
01/08/2020 Tutorial: Using Thymeleaf

The conversion service that allows us to perform data conversion and formatting operations through the syntax of
colcheteduplo(${{...}} ) is really a feature of the Standard Dialect, not of the Thymeleaf Model Mechanism itself.

As such, the way to configure it is by configuring our customized implementation of


IStandardConversionServiceinterface directly in the instanceStandardDialectthat is being configured in the mechanism
of model. Like:

IStandardConversionService customConversionService = ...

StandardDialect dialect = new StandardDialect();


dialect.setConversionService(customConversionService);

templateEngine.setDialect(dialect);

Note that the thymeleaf-spring3 and thymeleaf-spring4 packages contain theSpringStandardDialect, and this dialect already
comes pre-configured with an implementationIStandardConversionServicethat integrates the service infrastructure of
Spring conversion in Thymeleaf.

15.4 Log

Thymeleaf pays a lot of attention to the log and always tries to provide the maximum amount of useful information through
your log interface.

The logging library used isslf4j,that really acts as a bridge for any logging implementation that
we can use in our application (for examplelog4j).

Thymeleaf classes will registerTRACE, DEBUGeINFOinformation -level, depending on the level of detail we want, and
Furthermore, the general registry will use three special lumberjacks associated with the TemplateEngine class that we can
configure separately for different instances:

org.thymeleaf.TemplateEngine.CONFIGwill produce a detailed configuration of the library during initialization.


org.thymeleaf.TemplateEngine.TIMERIt will produce information about the amount of time required to process
each model (useful for comparative evaluation!)
org.thymeleaf.TemplateEngine.cacheit is the prelude to a set of log creators that produce information
specifics about the caches. Although the names of the cache registers are configurable by the user and, therefore,
they can change, by default they are:

org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE
org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE

An example of configuring the log infrastructure of Thymeleaf, usinglog4j, it can be:

log4j.logger.org.thymeleaf=DEBUG
log4j.logger.org.thymeleaf.TemplateEngine.CONFIG=TRACE
log4j.logger.org.thymeleaf.TemplateEngine.TIMER=TRACE
log4j.logger.org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE=TRACE

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 71/94
01/08/2020 Tutorial: Using Thymeleaf

16 Model cache
Thymeleaf works thanks to a set of parsers - for markup and text - that analyze templates in sequences.
of events (open mark, text, closed mark, comment, etc.) and a series of processors - one for each type of
behavior that needs to be applied - which alters the sequence of events analyzed by the model to create the
results we expect by combining the original model with our data.

It also includes - by default - a cache that stores analyzed models; the sequence of events resulting from reading and
analysis of template files before processing them. This is especially useful when working on a Web application and is based on
in the following concepts:

Input / Output is almost always the slowest part of any application. In-memory processing is extremely
fast in comparison.
Cloning a sequence of events in existing memory is always much faster than reading a file from
model, analyze it and create a new sequence of events for it.
Web applications usually have only a few dozen templates.
Template files are small to medium in size and are not modified while the application is running.
execution.

All of this leads to the idea that caching the most used models in a web application is possible without
wasting large amounts of memory, as well as saving a lot of time spent on input/output operations in
a small set of files that, in fact, never changes.

And how can we control this cache? First, we learned earlier that we can enable or disable it in the Resolver of
models, even when acting only in specific models:

// Default is true
templateResolver.setCacheable(false);
templateResolver.getCacheablePatternSpec().addPattern("/users/*");

In addition, we can modify its configuration by establishing our own CacheManager object, which can be
an instance of theStandardCacheManagerstandard implementation :

// Default is 200
StandardCacheManager cacheManager = new StandardCacheManager();
cacheManager.setTemplateCacheMaxSize(100);
...
templateEngine.setCacheManager(cacheManager);

Consulte a API javadoc de org.thymeleaf.cache.StandardCacheManagerto obtain more information about how to configure
the caches.

Entries can be manually removed from the model's cache:

Clear the cache completely


templateEngine.clearTemplateCache();

// Clear a specific template from the cache


templateEngine.clearTemplateCacheFor("/users/userList");

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 72/94
01/08/2020 Tutorial: Usando o Thymeleaf

17 Dissociated model logic

17.1 Dissociated logic: the concept

So far, we have worked for our grocery store with models made in the usual way, with the logic being inserted into our
models in the form of attributes.

But Thymeleaf also allows us to completely dissociate the markup of the model from its logic, enabling the creation of
completely logic-free marking modelsHTMLandXMLmodel.

The main idea is that the logic of the model is defined in a separate logic file (more precisely, a logical resource,
but it does not need to be a file). By default, this logical resource will be an additional file that lives in the same location (due to
example, folder) that the template file, with the same name, but with.th.xmlextension:

/templates
/home.html
+->/home.th.xml

Therefore, thehome.htmlthe file can be completely illogical. It can be like this:

<!DOCTYPE html>
<html>
<body>
<tableid="usersTable">
<tr>
Jeremy Grapefruit
Normal User
</tr>
<tr>
<tdclass="username">Alice Watermelon</td>
Administrator
</tr>
</table>
</body>
</html>

Absolutely no Thymeleaf code there. This is a template file that a designer without Thymeleaf or
knowledge of models could have created, edited and/or understood. Or a fragment of HTML provided by someone
external system without Thymeleaf hooks.

Now let's transform this.home.htmlmodel in a Thymeleaf model creating ourhome.th.xml additional file
like this:

<?xml version="1.0"?>
<thlogic>
<attrsel="#usersTable" th:remove="all-but-first">
<attrsel="/tr[0]" th:each="user : ${users}">
<attrsel="td.username" th:text="${user.name}" />
<attrsel="td.usertype"th:text="#{|user.type.${user.type}|}" />
</attr>
</attr>
</thlogic>

Here we can see many<attr> tags inside athlogicblock. These<attr> execution tags attribute injection on nodes
of the original model selected through itsselattributes, which contain Thymeleaf marking selectors (actually
AttoParser markup selectors.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 73/94
01/08/2020 Tutorial: Usando o Thymeleaf

Note also that the<attr>tags can be nested so that their selectors are merged. This
sel="/tr[0]"above, for example, will be processed assel="#usersTable/tr[0]". And the selector for the username
<td>will be processed assel="#usersTable/tr[0]//td.username".

Therefore, once merged, the two files mentioned above will be the same:

<!DOCTYPE html>
<html>
<body>
<table id="usersTable" th:remove="all-but-first">
<trth:each="user : ${users}">
Jeremy Grapefruit
<tdclass="usertype"th:text="#{|user.type.${user.type}|}">Normal User</td>
</tr>
<tr>
Alice Watermelon
Administrator
</tr>
</table>
</body>
</html>

This seems more familiar and is indeed less detailed than creating two separate files. But the advantage of the
dissociated models know that we can give our models total independence from Thymeleaf and, therefore, better
maintenance from the design perspective.

Of course, some contracts between designers or developers will still be necessary - for example, the fact that users
<table>will need aid="usersTable" -, but in many scenarios a pure HTML model will be an artifact of
much better communication between the design and development teams.

17.2 Configuring decoupled models

Activating Decoupled Models

The decoupled logic will not be expected for all models by default. Instead, model resolvers
configured (implementations ofITemplateResolverthey will need to specifically mark the models they resolve
using decoupled logic.

Except forStringTemplateResolver(which does not allow dissociated logic), all other implementations
ITemplateResolverready for use will provide a marker calleduseDecoupledLogicthat will mark all the models
resolved by this resolver as potentially having all or part of its logic living in a separate resource
:

finalServletContextTemplateResolver templateResolver =
newServletContextTemplateResolver(servletContext);
...
templateResolver.setUseDecoupledLogic(true);

Mixing coupled and decoupled logic

The dissociated model logic, when activated, is not a requirement. When activated, it means that the mechanism will seek a
resource containing decoupled logic, analyzing and merging it with the original model, if it exists. No error will be generated if the
dissociated logical resource does not exist.

In addition, in the same model, we can mix coupled logic and decoupled logic, for example, by adding some
attributes of Thymeleaf in the original model file, but leaving others for the separate decoupled logical file. The
the most common case for this is to use the newth:refattribute (in v3.0) .

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 74/94
01/08/2020 Tutorial: Using Thymeleaf

17.3 The th:ref attribute

th:refit's just a marker attribute. It does nothing from a processing standpoint and simply disappears
when the model is processed, but its utility lies in the fact that it serves as a reference for marking, that is,
It can be resolved by name from a markup selector, such as a brand name or fragment.th:fragment)

Therefore, if we have a selector like:

<attrsel="whatever".../>

This will correspond to:

Anywhatevertag.
Any tags with ath:fragment="whatever"attribute.
Any tags with ath:ref="whatever"attribute.

What is the advantage ofth:ref, for example, use aidpure HTML attribute? Just the fact that we don't want to add
so many attributesideclassas our tags to act as logical anchors, which can end up polluting our output.

And in the same sense, what is the disadvantageth:ref? Well, obviously, we would be adding a bit of logic
Thymeleaf ("logic") to our models.

Note that this applicability of thereferencethe attribute does not apply only to dissociated logical model files:
works the same way in other types of scenarios, such as in fragment expressions (~{...}).

17.4 Impact on the performance of dissociated models

The impact is extremely small. When a solved model is marked to use decoupled logic and it is not
cached, the model logic resource will be resolved first, analyzed and processed in a sequence of
instructions in memory: basically a list of attributes to be injected into each markup selector.

But this is the only additional step necessary because, after that, the real model will be analyzed, and while it is being analyzed
these attributes will be injected on-the-fly by the analyzer itself, thanks to the advanced capabilities for node selection in the
AttoParser. The analyzed nodes will exit the parser as if their injected attributes were saved in the model file.
original.

The biggest advantage of this? When a model is configured to be cached, it will already be cached.
containing the injected attributes. Therefore, the overload of using decoupled models for models that can be
stored in cache, once cached, it will be absolutely zero.

17.5 Resolution of decoupled logic

The way Thymeleaf resolves the corresponding logical resources for each model is configurable by
user. It is determined by a point of extension, the
org.thymeleaf.templateparser.markup.decoupled.IDecoupledTemplateLogicResolver, for which an implementation
the standard is provided:Standard
Decoupled Template Logic Resolver.

What does this standard implementation do?

First of all, an is appliedprefixit is asuffixfor someone based on a model resource (obtained through your
ITemplateResource#getBaseName()method). Both the prefix and the suffix can be configured and, by default, the
the prexo will be empty and the suxo will beth.
Second, request the model resource to resolve a relative resource with the name calculated through its
ITemplateResource#relative(String relativeLocation)method.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 75/94
01/08/2020 Tutorial: Using Thymeleaf

The specific implementation ofIDecoupledTemplateLogicResolverthe one to be used can be configured


Template Engineeasily

finalStandardDecoupledTemplateLogicResolver decoupledresolver =
newStandardDecoupledTemplateLogicResolver();
decoupledResolver.setPrefix("../viewlogic/");
...
templateEngine.setDecoupledTemplateLogicResolver(decoupledResolver);

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 76/94
01/08/2020 Tutorial: Using Thymeleaf

Appendix A: Basic Expression Objects


Some objects and variable maps are always available to be called. Let's look at them:

Base objects

the context object. An implementationorg.thymeleaf.context.IContextor


org.thymeleaf.context.IWebContextdepending on our environment (standalone or web).

Observe#varse#rootare synonyms for the same object, but the use#ctxit is recommended.

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.context.IContext
* ======================================================================
*/

${#ctx.locale}
${#ctx.variableNames}

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.context.IWebContext
* ======================================================================
*/

${#ctx.request}
${#ctx.response}
${#ctx.session}
${#ctx.servletContext}

direct access tojava.util.Localeassociated with the current request.

${#locale}

Web context namespaces for request/session attributes, etc.

When using Thymeleaf in a web environment, we can use a series of shortcuts to access request parameters,
session attributes and application attributes:

Note that these are not context objects, but maps added to the context as variables; therefore, we them
we access without# In some way, they act as spaces for the name.

param: to recover request parameters.${param.foo}is theString[] with the values offoorequest parameter,
so${param.foo[0]}it will usually be used to obtain the first value.

/*
* ============================================================================
* See javadoc API for class org.thymeleaf.context.WebRequestParamsVariablesMap
* ============================================================================
*/

${param.foo} Retrieves a String[] with the values of request parameter 'foo'


${param.size()}
${param.isEmpty()}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 77/94
01/08/2020 Tutorial: Using Thymeleaf
${param.containsKey('foo')}
...

session: to retrieve session attributes.

/*
* ======================================================================
See javadoc API for class org.thymeleaf.context.WebSessionVariablesMap
* ======================================================================
*/

${session.foo} Retrieves the session attribute 'foo'


${session.size()}
${session.isEmpty()}
${session.containsKey('foo')}
...

application: to recover application/servlet context attributes.

/*
* =============================================================================
See javadoc API for class org.thymeleaf.context.WebServletContextVariablesMap
* =============================================================================
*/

${application.foo} // Retrieves the ServletContext attribute 'foo'


${application.size()}
${application.isEmpty()}
${application.containsKey('foo')}
...

Note that there is no need to specify a namespace to access request attributes (unlike
request parameters) because all request attributes are automatically added to the context as
variables in the context root:

${myRequestAttribute}

Web context objects

Within a web environment, there is also direct access to the following objects (note that they are objects, not maps /
spaces for name):

direct access tojavax.servlet.http.HttpServletRequestobject associated with the current request.

${#request.getAttribute('foo')}
${#request.getParameter('foo')}
${#request.getContextPath()}
${#request.getRequestName()}
...

#session: direct access tojavax.servlet.http.HttpSessionobject associated with the current request.

${#session.getAttribute('foo')}
${#session.id}
${#session.lastAccessedTime}
...

#servletContext: direct access tojavax.servlet.ServletContextobject associated with the current request.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 78/94
01/08/2020 Tutorial: Using Thymeleaf

${#servletContext.getAttribute('foo')}
${#servletContext.contextPath}
...

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 79/94
01/08/2020 Tutorial: Using Thymeleaf

Appendix B: Expression Utility Objects

Execution information

execInfo: an expression object that provides useful information about the model being processed in the
Standard Thymeleaf expressions.

/*
* ======================================================================
See javadoc API for class org.thymeleaf.expression.ExecutionInfo
* ======================================================================
*/

/*
Return the name and mode of the 'leaf' template. This means the template
from where the events being processed were parsed. So if this piece of
The code is not in the root template 'A' but in a fragment being inserted.
into "A" from another template called "B", this will return "B" as a
* name, and B's mode as template mode.
*/
${#execInfo.templateName}
${#execInfo.templateMode}

/*
Return the name and mode of the 'root' template. This means the template
that the template engine was originally asked to process. So if this
The piece of code is not in the root template "A" but on a fragment being
* inserted into "A" from another template called "B", this will still
return "A" and A's template mode.
*/
${#execInfo.processedTemplateName}
${#execInfo.processedTemplateMode}

/*
* Return the stacks (actually, List<String> or List<TemplateMode>) of
templates being processed. The first element will be the
* 'processedTemplate' (the root one), the last one will be the 'leaf'
* template, and in the middle all the fragments inserted in nested
A manner to reach the leaf from the root will appear.
*/
${#execInfo.templateNames}
${#execInfo.templateModes}

/*
Return the stack of templates being processed similarly (and in the
* same order) to 'templateNames' and 'templateModes', but returning
a List<TemplateData> with the full template metadata.
*/
${#execInfo.templateStack}

Messages

utilities methods to obtain externalized messages within variable expressions, of the same
way that would be obtained using the#{...} syntax.

/*
* ======================================================================
See javadoc API for class org.thymeleaf.expression.Messages
* ======================================================================
*/
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 80/94
01/08/2020 Tutorial: Using Thymeleaf

/*
* Obtain externalized messages. Can receive a single key, a key plus arguments,
* or an array/list/set of keys (in which case it will return an array/list/set of
externalized messages).
* If a message is not found, a default message (like '??msgKey??') is returned.
*/
${#messages.msg('msgKey')}
${#messages.msg('msgKey', param1)}
${#messages.msg('msgKey', param1, param2)}
${#messages.msg('msgKey', param1, param2, param3)}
${#messages.msgWithParams('msgKey',newObject[] {param1, param2, param3, param4})}
${#messages.arrayMsg(messageKeyArray)}
${#messages.listMsg(messageKeyList)}
${#messages.setMsg(messageKeySet)}

/*
Obtain externalized messages or null. Null is returned instead of a default
* message if a message for the specified key is not found.
*/
${#messages.msgOrNull('msgKey')}
${#messages.msgOrNull('msgKey', param1)}
${#messages.msgOrNull('msgKey', param1, param2)}
${#messages.msgOrNull('msgKey', param1, param2, param3)}
${#messages.msgOrNullWithParams('msgKey',newObject[] {param1, param2, param3, param4})}
${#messages.arrayMsgOrNull(messageKeyArray)}
${#messages.listMsgOrNull(messageKeyList)}
${#messages.setMsgOrNull(messageKeySet)}

URIs / URLs

uris: utility object for executing URL operations / URL (especially escape / unescaping) within the
expressões padrão do Thymeleaf.

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Uris
* ======================================================================
*/

/*
Escape/Unescape as a URI/URL path
*/
${#uris.escapePath(uri)}
${#uris.escapePath(uri, encoding)}
${#uris.unescapePath(uri)}
${#uris.unescapePath(uri, encoding)}

/*
Escape/Unescape as a URI/URL path segment (between '/' symbols)
*/
${#uris.escapePathSegment(uri)}
${#uris.escapePathSegment(uri, encoding)}
${#uris.unescapePathSegment(uri)}
${#uris.unescapePathSegment(uri, encoding)}

/*
Escape/Unescape as a Fragment Identifier (#frag)
*/
${#uris.escapeFragmentId(uri)}
${#uris.escapeFragmentId(uri, encoding)}
${#uris.unescapeFragmentId(uri)}
${#uris.unescapeFragmentId(uri, encoding)}

/*
Escape/Unescape as a Query Parameter (?var=value)
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 81/94
01/08/2020 Tutorial: Using Thymeleaf
*/
${#uris.escapeQueryParam(uri)}
${#uris.escapeQueryParam(uri, encoding)}
${#uris.unescapeQueryParam(uri)}
${#uris.unescapeQueryParam(uri, encoding)}

Conversions

#conversions: utility object that allows the execution of the conversion service at any point in a model:

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Conversions
* ======================================================================
*/

/*
* Execute the desired conversion of the 'object' value into the
* specified class.
*/
${#conversions.convert(object,'java.util.TimeZone')}
${#conversions.convert(object, targetClass)}

data

#dates: utility methods forjava.util.Dateobjects:

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Dates
* ======================================================================
*/

/*
Format date with the standard locale format
Also works with arrays, lists or sets
*/
${#dates.format(date)}
${#dates.arrayFormat(datesArray)}
${#dates.listFormat(datesList)}
${#dates.setFormat(datesSet)}

/*
Format date with the ISO8601 format
* Also works with arrays, lists or sets
*/
${#dates.formatISO(date)}
${#dates.arrayFormatISO(datesArray)}
${#dates.listFormatISO(datesList)}
${#dates.setFormatISO(datesSet)}

/*
Format date with the specified pattern
Also works with arrays, lists or sets
*/
${#dates.format(date,'dd/MMM/yyyy HH:mm')}
${#dates.arrayFormat(datesArray,'dd/MMM/yyyy HH:mm')}
${#dates.listFormat(datesList,'dd/MMM/yyyy HH:mm')}
${#dates.setFormat(datesSet,'dd/MMM/yyyy HH:mm')}

/*
Obtain date properties

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 82/94
01/08/2020 Tutorial: Using Thymeleaf
Also works with arrays, lists or sets
*/
${#dates.day(date)} // also arrayDay(...), listDay(...), etc.
${#dates.month(date)} // also arrayMonth(...), listMonth(...), etc.
${#dates.monthName(date)} // also arrayMonthName(...), listMonthName(...), etc.
${#dates.monthNameShort(date)} // also arrayMonthNameShort(...), listMonthNameShort(...), etc.
${#dates.year(date)} ["also arrayYear(...), listYear(...), etc."]
${#dates.dayOfWeek(date)} // also arrayDayOfWeek(...), listDayOfWeek(...), etc.
${#dates.dayOfWeekName(date)} // also arrayDayOfWeekName(...), listDayOfWeekName(...), etc.
${#dates.dayOfWeekNameShort(date)} // also arrayDayOfWeekNameShort(...), listDayOfWeekNameShort(...), etc.
${#dates.hour(date)} // also arrayHour(...), listHour(...), etc.
${#dates.minute(date)} // also arrayMinute(...), listMinute(...), etc.
${#dates.second(date)} // also arraySecond(...), listSecond(...), etc.
${#dates.millisecond(date)} // also arrayMillisecond(...), listMillisecond(...), etc.

/*
Create date (java.util.Date) objects from its components
*/
${#dates.create(year,month,day)}
${#dates.create(year,month,day,hour,minute)}
${#dates.create(year,month,day,hour,minute,second)}
${#dates.create(year,month,day,hour,minute,second,millisecond)}

/*
Create a date (java.util.Date) object for the current date and time
*/
Create Now

${#dates.createNowForTimeZone()}

/*
Create a date (java.util.Date) object for the current date (time set to 00:00)
*/
Create today

${#dates.createTodayForTimeZone()}

Calendars

#calendars: analogous to#dates, but forjava.util.Calendarobjects:

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Calendars
* ======================================================================
*/

/*
Format calendar with the standard locale format
Also works with arrays, lists or sets
*/
${#calendars.format(cal)}
${#calendars.arrayFormat(calArray)}
${#calendars.listFormat(calList)}
${#calendars.setFormat(calSet)}

/*
* Format calendar with the ISO8601 format
Also works with arrays, lists or sets
*/
${#calendars.formatISO(cal)}
${#calendars.arrayFormatISO(calArray)}
${#calendars.listFormatISO(calList)}
${#calendars.setFormatISO(calSet)}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 83/94
01/08/2020 Tutorial: Using Thymeleaf
/*
Format calendar with the specified pattern
Also works with arrays, lists or sets
*/
${#calendars.format(cal,'dd/MMM/yyyy HH:mm')}
${#calendars.arrayFormat(calArray,'dd/MMM/yyyy HH:mm')}
${#calendars.listFormat(calList,'dd/MMM/yyyy HH:mm')}
${#calendars.setFormat(calSet,'dd/MMM/yyyy HH:mm')}

/*
Obtain calendar properties
Also works with arrays, lists or sets
*/
${#calendars.day(date)} // also arrayDay(...), listDay(...), etc.
${#calendars.month(date)} // also arrayMonth(...), listMonth(...), etc.
${#calendars.monthName(date)} // also arrayMonthName(...), listMonthName(...), etc.
${#calendars.monthNameShort(date)} // also arrayMonthNameShort(...), listMonthNameShort(...), etc.
${#calendars.year(date)} // also arrayYear(...), listYear(...), etc.
${#calendars.dayOfWeek(date)}// also arrayDayOfWeek(...), listDayOfWeek(...), etc.
${#calendars.dayOfWeekName(date)}// also arrayDayOfWeekName(...), listDayOfWeekName(...), etc.
${#calendars.dayOfWeekNameShort(date)}// also arrayDayOfWeekNameShort(...), listDayOfWeekNameShort(...), etc.
${#calendars.hour(date)} // also arrayHour(...), listHour(...), etc.
${#calendars.minute(date)} // also arrayMinute(...), listMinute(...), etc.
${#calendars.second(date)} // also arraySecond(...), listSecond(...), etc.
${#calendars.millisecond(date)} // also arrayMillisecond(...), listMillisecond(...), etc.

/*
* Create calendar (java.util.Calendar) objects from its components
*/
${#calendars.create(year,month,day)}
${#calendars.create(year,month,day,hour,minute)}
${#calendars.create(year,month,day,hour,minute,second)}
${#calendars.create(year,month,day,hour,minute,second,millisecond)}

${#calendars.createForTimeZone(year,month,day,timeZone)}
${#calendars.createForTimeZone(year,month,day,hour,minute,timeZone)}
${#calendars.createForTimeZone(year,month,day,hour,minute,second,timeZone)}
${#calendars.createForTimeZone(year,month,day,hour,minute,second,millisecond,timeZone)}

/*
Create a calendar (java.util.Calendar) object for the current date and time
*/
${#calendars.createNow()}

${#calendars.createNowForTimeZone()}

/*
Create a calendar (java.util.Calendar) object for the current date (time set to 00:00)
*/
${#calendars.createToday()}

${#calendars.createTodayForTimeZone()}

Numbers

#numbers: utility methods for numeric objects:

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Numbers
* ======================================================================
*/

/*
* ==========================
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 84/94
01/08/2020 Tutorial: Using Thymeleaf
Formatting integer numbers
* ==========================
*/

/*
Set minimum integer digits.
Also works with arrays, lists or sets
*/
${#numbers.formatInteger(num,3)}
${#numbers.arrayFormatInteger(numArray,3)}
${#numbers.listFormatInteger(numList,3)}
${#numbers.setFormatInteger(numSet,3)}

/*
* Set minimum integer digits and thousands separator:
* 'POINT', 'COMMA', 'WHITESPACE', 'NONE' or 'DEFAULT' (by locale).
* Also works with arrays, lists or sets
*/
${#numbers.formatInteger(num,3,'POINT')}
${#numbers.arrayFormatInteger(numArray,3,'POINT')}
${#numbers.listFormatInteger(numList,3,'POINT')}
${#numbers.setFormatInteger(numSet,3,'POINT')}

/*
* ==========================
Formatting decimal numbers
* ==========================
*/

/*
Set minimum integer digits and (exact) decimal digits.
Also works with arrays, lists or sets
*/
${#numbers.formatDecimal(num,3,2)}
${#numbers.arrayFormatDecimal(numArray,3,2)}
${#numbers.listFormatDecimal(numList,3,2)}
${#numbers.setFormatDecimal(numSet,3,2)}

/*
Set minimum integer digits and (exact) decimal digits, and also decimal separator.
Also works with arrays, lists or sets.
*/
${#numbers.formatDecimal(num,3,2,'COMMA')}
${#numbers.arrayFormatDecimal(numArray,3,2,'COMMA')}
${#numbers.listFormatDecimal(numList,3,2,'COMMA')}
${#numbers.setFormatDecimal(numSet,3,2,'COMMA')}

/*
Set minimum integer digits and (exact) decimal digits, and also thousands and
* decimal separator.
Also works with arrays, lists or sets
*/
${#numbers.formatDecimal(num,3,'POINT',2,'COMMA')}
${#numbers.arrayFormatDecimal(numArray,3,'POINT',2,'COMMA')}
${#numbers.listFormatDecimal(numList,3,'POINT',2,'COMMA')}
${#numbers.setFormatDecimal(numSet,3,'POINT',2,'COMMA')}

/*
* =====================
Formatting currencies
* =====================
*/

${#numbers.formatCurrency(num)}
${#numbers.arrayFormatCurrency(numArray)}
${#numbers.listFormatCurrency(numList)}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 85/94
01/08/2020 Tutorial: Using Thymeleaf
${#numbers.setFormatCurrency(numSet)}

/*
* ======================
Formatting percentages
* ======================
*/

${#numbers.formatPercent(num)}
${#numbers.arrayFormatPercent(numArray)}
${#numbers.listFormatPercent(numList)}
${#numbers.setFormatPercent(numSet)}

/*
* Set minimum integer digits and (exact) decimal digits.
*/
${#numbers.formatPercent(num,3,2)}
${#numbers.arrayFormatPercent(numArray,3,2)}
${#numbers.listFormatPercent(numList,3,2)}
${#numbers.setFormatPercent(numSet,3,2)}

/*
* ===============
Utility methods
* ===============
*/

/*
Create a sequence (array) of integer numbers going
* from x to y
*/
${#numbers.sequence(from,to)}
${#numbers.sequence(from,to,step)}

Strings

utility methods forStringobjects:

/*
* ======================================================================
See javadoc API for class org.thymeleaf.expression.Strings
* ======================================================================
*/

/*
Null-safe toString()
*/
${#strings.toString(obj)} // also array*, list* and set*

/*
Check whether a String is empty (or null). Performs a trim() operation before check.
* Also works with arrays, lists or sets
*/
${#strings.isEmpty(name)}
${#strings.arrayIsEmpty(nameArr)}
${#strings.listIsEmpty(nameList)}
The name set is empty.

/*
Perform an 'isEmpty()' check on a string and return it if false, defaulting to
another specified string if true.
* Also works with arrays, lists or sets
*/
${#strings.defaultString(text,default)}
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 86/94
01/08/2020 Tutorial: Using Thymeleaf
${#strings.arrayDefaultString(textArr,default)}
${#strings.listDefaultString(textList,default)}
${#strings.setDefaultString(textSet,default)}

/*
Check whether a fragment is contained in a String
Also works with arrays, lists or sets
*/
${#strings.contains(name,'ez')} // also array*, list* and set*
${#strings.containsIgnoreCase(name,'ez')} // also array*, list* and set*

/*
Check whether a String starts or ends with a fragment
Also works with arrays, lists or sets
*/
${#strings.startsWith(name,'Don')} // also array*, list* and set*
${#strings.endsWith(name,endingFragment)} // also array*, list* and set*

/*
Substring-related operations
Also works with arrays, lists or sets
*/
${#strings.indexOf(name,frag)} // also array*, list* and set*
${#strings.substring(name,3,5)} // also array*, list* and set*
${#strings.substringAfter(name,prefix)} // also array*, list* and set*
${#strings.substringBefore(name,suffix)} // also array*, list* and set*
${#strings.replace(name,'las','ler')} // also array*, list* and set*

/*
Append and prepend
Also works with arrays, lists or sets
*/
${#strings.prepend(str,prefix)} // also array*, list* and set*
${#strings.append(str,suffix)} // also array*, list* and set*

/*
* Change case
Also works with arrays, lists or sets
*/
${#strings.toUpperCase(name)} // also array*, list* and set*
${#strings.toLowerCase(name)} // also array*, list* and set*

/*
Split and join
*/
${#strings.arrayJoin(namesArray,',')}
${#strings.listJoin(namesList,',')}
${#strings.setJoin(namesSet,',')}
${#strings.arraySplit(namesStr,',')} // returns String[]
${#strings.listSplit(namesStr,',')} // returns List<String>
${#strings.setSplit(namesStr,',')} // returns Set<String>

/*
Trim
Also works with arrays, lists or sets
*/
${#strings.trim(str)} // also array*, list* and set*

/*
Compute length
Also works with arrays, lists or sets
*/
${#strings.length(str)} // also array*, list* and set*

/*
* Abbreviate text to a maximum size of n. If the text is larger, it
will be clipped and finished in '...'
* Also works with arrays, lists or sets
*/

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 87/94
01/08/2020 Tutorial: Using Thymeleaf
${#strings.abbreviate(str,10)} // also array*, list* and set*

/*
Convert the first character to upper-case (and vice-versa)
*/
${#strings.capitalize(str)} // also array*, list* and set*
${#strings.unCapitalize(str)} // also array*, list* and set*

/*
* Convert The First Character Of Every Word To Upper-Case
*/
${#strings.capitalizeWords(str)} // also array*, list* and set*
${#strings.capitalizeWords(str,delimiters)} // also array*, list* and set*

/*
* Escape the string
*/
${#strings.escapeXml(str)} // also array*, list* and set*
${#strings.escapeJava(str)} // also array*, list* and set*
${#strings.escapeJavaScript(str)} // also array*, list* and set*
${#strings.unescapeJava(str)} // also array*, list* and set*
${#strings.unescapeJavaScript(str)} // also array*, list* and set*

/*
Null-safe comparison and concatenation
*/
${#strings.equals(first, second)}
${#strings.equalsIgnoreCase(first, second)}
${#strings.concat(values...)}
${#strings.concatReplaceNulls(nullValue, values...)}

/*
Random
*/
${#strings.randomAlphanumeric(count)}

Objects

#objects: utility methods for objects in general

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Objects
* ======================================================================
*/

/*
Return obj if it is not null, and default otherwise
Also works with arrays, lists or sets
*/
${#objects.nullSafe(obj,default)}
${#objects.arrayNullSafe(objArray,default)}
${#objects.listNullSafe(objList,default)}
${#objects.setNullSafe(objSet,default)}

Booleans

#bools: métodos utilitários para avaliação booleana

/*
* ======================================================================
See javadoc API for class org.thymeleaf.expression.Bools

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 88/94
01/08/2020 Tutorial: Using Thymeleaf
* ======================================================================
*/

/*
Evaluate a condition in the same way that it would be evaluated in a th:if tag
(see conditional evaluation chapter afterwards).
Also works with arrays, lists or sets
*/
${#bools.isTrue(obj)}
${#bools.arrayIsTrue(objArray)}
${#bools.listIsTrue(objList)}
${#bools.setIsTrue(objSet)}

/*
* Evaluate with negation
Also works with arrays, lists or sets
*/
${#bools.isFalse(cond)}
${#bools.arrayIsFalse(condArray)}
${#bools.listIsFalse(condList)}
${#bools.setIsFalse(condSet)}

/*
* Evaluate and apply AND operator
Receive an array, a list or a set as parameter
*/
${#bools.arrayAnd(condArray)}
${#bools.listAnd(condList)}
${#bools.setAnd(condSet)}

/*
Evaluate and apply OR operator
Receive an array, a list or a set as parameter
*/
${#bools.arrayOr(condArray)}
${#bools.listOr(condList)}
${#bools.setOr(condSet)}

Matrices

#arrays: utility methods for arrays

/*
* ======================================================================
See javadoc API for class org.thymeleaf.expression.Arrays
* ======================================================================
*/

/*
* Converts to array, trying to infer array component class.
* Note that if the resulting array is empty, or if the elements
of the target object are not all of the same class,
* this method will return Object[].
*/
${#arrays.toArray(object)}

/*
Convert to arrays of the specified component class.
*/
${#arrays.toStringArray(object)}
${#arrays.toIntegerArray(object)}
${#arrays.toLongArray(object)}
${#arrays.toDoubleArray(object)}
${#arrays.toFloatArray(object)}
${#arrays.toBooleanArray(object)}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 89/94
01/08/2020 Tutorial: Using Thymeleaf
/*
Compute length
*/
${#arrays.length(array)}

/*
Check whether the array is empty
*/
${#arrays.isEmpty(array)}

/*
Check if element or elements are contained in array
*/
${#arrays.contains(array, element)}
${#arrays.containsAll(array, elements)}

Lists

#lists: utility methods for lists

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Lists
* ======================================================================
*/

/*
Converts to list
*/
${#lists.toList(object)}

/*
Compute size
*/
${#lists.size(list)}

/*
* Check whether list is empty
*/
${#lists.isEmpty(list)}

/*
Check if element or elements are contained in list
*/
${#lists.contains(list, element)}
${#lists.containsAll(list, elements)}

/*
Sort a copy of the given list. The members of the list must implement
comparable or you must define a comparator.
*/
${#lists.sort(list)}
${#lists.sort(list, comparator)}

Sets

#sets: utility methods for sets

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Sets
* ======================================================================

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 90/94
01/08/2020 Tutorial: Using Thymeleaf
*/

/*
* Converts to set
*/
${#sets.toSet(object)}

/*
Compute size
*/
${#sets.size(set)}

/*
Check whether the set is empty
*/
${#sets.isEmpty(set)}

/*
Check if element or elements are contained in set
*/
${#sets.contains(set, element)}
${#sets.containsAll(set, elements)}

Maps

#maps: utility methods for maps

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Maps
* ======================================================================
*/

/*
Compute size
*/
${#maps.size(map)}

/*
Check whether map is empty
*/
${#maps.isEmpty(map)}

/*
* Check if key/s or value/s are contained in maps
*/
${#maps.containsKey(map, key)}
${#maps.containsAllKeys(map, keys)}
${#maps.containsValue(map, value)}
${#maps.containsAllValues(map, value)}

Aggregates

#aggregates: utility methods for creating aggregations in arrays or collections

/*
* ======================================================================
See javadoc API for class org.thymeleaf.expression.Aggregates
* ======================================================================
*/

/*

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 91/94
01/08/2020 Tutorial: Using Thymeleaf
Compute sum. Returns null if array or collection is empty
*/
${#aggregates.sum(array)}
${#aggregates.sum(collection)}

/*
Compute average. Returns null if array or collection is empty
*/
${#aggregates.avg(array)}
${#aggregates.avg(collection)}

IDs

utility methods to deal withidattributes that can be repeated (for example, as a result of a
iteration).

/*
* ======================================================================
See javadoc API for class org.thymeleaf.expression.Ids
* ======================================================================
*/

/*
Normally used in th:id attributes, for appending a counter to the id attribute value
so that it remains unique even when involved in an iteration process.
*/
${#ids.seq('someId')}

/*
* Normally used in th:for attributes in <label> tags, so that these labels can refer to Ids
* generated by means of the #ids.seq(...) function.
*
Depending on whether the <label> goes before or after the element with the #ids.seq(...)
function, the "next" (label goes before "seq") or the "prev" function (label goes after
"seq" function should be called.
*/
${#ids.next('someId')}
${#ids.prev('someId')}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 92/94
01/08/2020 Tutorial: Using Thymeleaf

20 Appendix C: Markup Selector Syntax


The Thymeleaf markup selectors are borrowed directly from the parsing library of theThymeleaf: AttoParser.

The syntax for these selectors has many similarities with the selectors in XPath, CSS, and jQuery, which makes them easy to
use for most users. You can take a look at the complete syntax reference atdocumentationdo
AttoParser.

For example, the following selector will select all<div>the members of the classcontent, in all positions within the
marking (note that this is not as concise as possible, read to understand why):

<divth:insert="mytemplate :: //div[@class='content']">...</div>

The basic syntax includes:

/x signifies direct children of the current node with the name x.

//xsignifies children of the current node with the name x, at any depth.

x[@z="v"]signifies elements with the name x and an attribute called z with the value "v".

x[@z1="v1" and @z2="v2"]signifies elements with the name x and the attributes z1 and z2 with the values 'v1' and 'v2',
respectively.

x[i]signifies an element with the name x positioned at the number i among its siblings.

x[@z="v"][i]signifies elements with the name x, attribute z with the value 'v' and positioned at number i among their siblings
that also correspond to this condition.

But a more concise syntax can also be used:

x it is exactly equivalent to//x(search for an element by name or reference)x at any level of


depth, a reference being oneth:refor ath:fragmentattribute).

Selectors are also allowed without the name/reference of the element, as long as they include a specification of
arguments. Thus,[@class='oneclass']is a valid selector that looks for any elements (tags) with a
class attribute with valueoneclass.

Advanced attribute selection resources:

Besides= (equal), other comparison operators are also valid:!=(not the same)^=(starts with) and$=
(ends with). For example:x[@class^='section']signifies elements with namex and a value for the attribute
classthat starts withsection.

The attributes can be specified, starting with@ (XPath style) and without (jQuery style). Sox[z='v']é
equivalent tox[@z='v'].

Multiple attribute modifiers can be combined withand(XPath style) and also chaining several
modifiers (jQuery style). So,x[@z1='v1' and @z2='v2']is really equivalent tox[@z1='v1'][@z2='v2'](e
also to thex[z1='v1'][z2='v2']).

Direct selectors of type jQuery:

x.oneclassis equivalent tox[class='oneclass'].

oneclassis equivalent to[class='oneclass'].

x#oneidis equivalent tox[id='oneid'].

#oneidis equivalent tooneid.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 93/94
01/08/2020 Tutorial: Using Thymeleaf

x%one referencesigni ca<x>tags that have ath:ref="oneref"orth:fragment="oneref"attribute.

%onerefit means any tag that has ath:ref="oneref"orth:fragment="oneref"attribute. Notice that this is
equivalent to simplyonerefbecause references can be used instead of the names of the elements.

Direct selectors and attribute selectors can be mixed:a.external[@href^='https'].

Therefore, the expression of the selector above:

<divth:insert="mytemplate :: //div[@class='content']">...</div>

It can be written as:

<divth:insert="mytemplate :: div.content">...</div>

Examining a different example, this:

<divth:replace="mytemplate :: myfrag">...</div>

He/She is going to look for ath:fragment="myfrag"fragment signature (orth:refreferences). But I would also look for tags
with the namemyfragif they existed (which they do not, in HTML). Note the difference with:

<divth:replace="mytemplate :: .myfrag">...</div>

... that will seek elements withclass="myfrag", without caring aboutth:fragmentsubscriptions (or th:refreferences).

Class correspondence with multiple values

Selector marking understanding the class attribute for multiple values, and thus allowing the application of selectors.
in this attribute even if the element has several class values.

For example,div.twowill correspond<div class="one two three" />

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 94/94

You might also like