Tutorial - Using Thymeleaf PDF
Tutorial - Using Thymeleaf PDF
Thymeleaf
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 1/94
01/08/2020 Tutorial: Using Thymeleaf
1 Introduction to 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.
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
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:
… The Thymeleaf Standard Dialect would allow us to achieve the same functionality with:
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
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:
Our app will also have a very simple service layer, composed ofServiceobjects containing methods
like:
...
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 4/94
01/08/2020 Tutorial: Using Thymeleaf
returnProductRepository.getInstance().findAll();
}
On the web layer, our application will have a filter that delegates execution to the commands enabled for Thymeleaf,
depending on the request URL:
try{
/*
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:
All we need to do now is create implementations of theIGTVGControllerinterface, retrieving data from services and
processing models using theITemplateEngineobject.
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).
public classGTVGApplication {
...
private finalTemplateEngine templateEngine;
...
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));
...
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.
ServletContextTemplateResolver templateResolver =
newServletContextTemplateResolver(servletContext);
Model resolvers are objects that implement an interface of the Thymeleaf API called
org.thymeleaf.templateresolver.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 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:
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
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>
</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>
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.
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:
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.
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.
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 voidprocess(
finalHttpServletRequest request, finalHttpServletResponse response,
finalServletContext servletContext, finalITemplateEngine templateEngine
throwsException {
WebContext ctx =
newWebContext(request, response, servletContext, request.getLocale());
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.
publicLocale getLocale();
public boolean containsVariable(final String name);
publicSet<String> getVariableNames();
public Object getVariable(final String name);
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.
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:
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:
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>
</body>
</html>
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?
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")
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 12/94
01/08/2020 Tutorial: Using Thymeleaf
Now let's add more content to our homepage. For example, we can display the date below our message.
welcome, like this:
First, we will have to modify our controller to add this date as a context variable:
WebContext ctx =
newWebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", dateFormat.format(cal.getTime()));
We added aStringvariable calledtodayto our context and now we can display it in our model:
<body>
</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
We have already seen two types of valid attribute values expressed in this syntax: message and variable expressions:
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 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
Conditional operators:
So then:(if) ? (then)
So then another:(if) ? (then) : (else)
Standard:(value) ?: (defaultvalue)
Special cards:
No operation:_
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 14/94
01/08/2020 Tutorial: Using Thymeleaf
4.1 Messages
...for that:
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?
This means that we would need to add a parameter to our message. Just like this:
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.
<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).
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>
((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('-')}
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:
In addition to these basic objects, Thymeleaf offers us a set of utility objects that will help us perform tasks.
common in our expressions.
You can check what functions are offered by each of these utility objects in Appendix B.
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:
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());
<p>
Today is: 13 May 2011
</p>
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>
<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>
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:@{...}
Absolute URLs:http://www.thymeleaf.org
Relative URLs, which can be:
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.
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>
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?
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
Boolean literals
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
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.
...
instead of:
...
Texts, regardless of being literal or the result of evaluating variable expressions or messages,
can be easily attached using the+ operador:
Literal substitutions allow for easy formatting of strings containing variable values without the need for
attach literals'...' + '...'.
Only variable expressions / message (${...}, *{...}, #{...}) are allowed inside|...|substitutions
literals. No other literal ('...'boolean/numeric tokens, conditional expressions, etc.
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( % ).
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>.
A simpler alternative may be using existing text aliases for some of these operators:gt( > ), lt( < ),
ge( >=), the( <=), not( ! Alsoeq( ==), neq/ ne( !=).
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 ('...' ).
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>
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.
<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:
Just like conditional values, they can contain nested expressions in parentheses:
<p>
Name:
<spanth:text="*{firstName}?: (*{admin}?'Admin': #{default.username})">Sebastian</span>
</p>
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:
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
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 .
<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.
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
[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:
Note that the pre-processing stage for a French language code will create the following equivalent:
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 25/94
01/08/2020 Tutorial: Using Thymeleaf
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:
<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}" />
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 26/94
01/08/2020 Tutorial: Using Thymeleaf
<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).
<inputtype="submit"value="Subscribe!"th:value="#{subscribe.submit}"/>
This looks much better! Let's try to do the same with theactionattribute informtag:
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.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 27/94
01/08/2020 Tutorial: Using Thymeleaf
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
<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
<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}" />
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:
If you process this model with thecssStyle variable defined aswarningyou will obtain:
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:
Don't worry about thisth:eachattribute. It is an iteration attribute and we will talk about this later.)
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:
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:
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.
<spanth:whatever="${user.name}">...</span>
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.
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 {
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:
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:
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:
<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.
<!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>
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
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.
<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>
<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.
<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>
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:
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>
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.
<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
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>
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.
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.
BothtemplatenameeselectorIn the examples above, there can be expressions with complete resources (even conditionals!)
As:
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.
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)
th:insertit's the simplest: it simply inserts the specified fragment as the body of the host tag.
th:includeis similar toth:insert , but instead of inserting the fragment, it only inserts the content of this
fragment.
<footerth:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</footer>
<body>
...
<divth:insert="footer :: copy"></div>
<divth:replace="footer :: copy"></div>
<divth:include="footer :: copy"></div>
</body>
<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>
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>
<divth:replace="::frag (twovar=${value2},onevar=${value1})">...</div>
<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})">
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.
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>
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.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 41/94
01/08/2020 Tutorial: Using Thymeleaf
<headth:fragment="common_header(title,links)">
</head>
...
<headth:replace="base :: common_header(~{::title},~{::link})">
<title>Awesome - Main</title>
</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>
</head>
...
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>
</head>
...
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):
...
<head>
</head>
...
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>
...
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.
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>
<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:
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).
Also note thatth:removeconsidernull a synonym fornone , therefore, the following works the same way as
the above example:
In this case, if${condition}for false,null it will be returned and, therefore, no removal will be executed.
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>
<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
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:
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
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.
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:
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>
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
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:
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.
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!
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 55/94
01/08/2020 Tutorial: Using Thymeleaf
Hello, [[${session.user.name}]]!
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":
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.
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.
<script th:inline="javascript">
...
var username = [[${session.user.name}]];
...
</script>
<script th:inline="javascript">
...
var username = "Sebastian \"Fruity\" Applejuice";
...
</script>
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>
<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.
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).
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 57/94
01/08/2020 Tutorial: Usando o Thymeleaf
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)
<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 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).
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 :
<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>
<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.
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
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})],
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})]
[/]
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})],
# th:each="prod : ${products}"
[(${prod.name})]. Price: [(${prod.price})] EUR/kg
[/]
Thanks,
The Thymeleaf Shop
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:
[# th:each="salut : ${salutations}"]
alert([[${greeting}]] + " " + username);
[/]
};
};
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:
[# 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
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 f = function() {
...
var x = 23;
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 f = function() {
...
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 f = function() {
...
Or that, noTEXTmode:
...
Note the user is obtained from the session, which must exist.
Welcome [(${session.user.name})]!
...
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
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.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<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.
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>
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 67/94
01/08/2020 Tutorial: Using Thymeleaf
In addition to giving us the ability to create our own model resolver by implementing the
ITemplateResolver, Thymeleaf includes four ready-to-use implementations:
returnThread.currentThread().getContextClassLoader().getResourceAsStream(template);
return newFileInputStream(newFile(template));
org.thymeleaf.templateresolver.UrlTemplateResolver, which resolves models like URLs (even non-local ones), such as:
return(newURL(template)).openStream();
return newStringReader(templateName);
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);
templateResolver.setCharacterEncoding("UTF-8");
// 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.
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:
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:
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
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).
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.
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.
And if we wanted to add a message resolver (or more) to the Model Mechanism? Easy:
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.
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.cache.TEMPLATE_CACHE
org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE
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.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 72/94
01/08/2020 Tutorial: Usando o Thymeleaf
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
<!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.
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);
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
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)
<attrsel="whatever".../>
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 (~{...}).
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.
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.
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
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
Base objects
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}
${#locale}
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
* ============================================================================
*/
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 77/94
01/08/2020 Tutorial: Using Thymeleaf
${param.containsKey('foo')}
...
/*
* ======================================================================
See javadoc API for class org.thymeleaf.context.WebSessionVariablesMap
* ======================================================================
*/
/*
* =============================================================================
See javadoc API for class org.thymeleaf.context.WebServletContextVariablesMap
* =============================================================================
*/
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}
Within a web environment, there is also direct access to the following objects (note that they are objects, not maps /
spaces for name):
${#request.getAttribute('foo')}
${#request.getParameter('foo')}
${#request.getContextPath()}
${#request.getRequestName()}
...
${#session.getAttribute('foo')}
${#session.id}
${#session.lastAccessedTime}
...
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
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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
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
/*
* ======================================================================
* 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
/*
* ======================================================================
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
/*
* ======================================================================
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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
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
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>
//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.
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.
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']).
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 93/94
01/08/2020 Tutorial: Using Thymeleaf
%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.
<divth:insert="mytemplate :: //div[@class='content']">...</div>
<divth:insert="mytemplate :: div.content">...</div>
<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).
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.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 94/94