In the previous article, we have understood a few about utilizing layout with Thymeleaf. But it is not enough knowledege to write front-end easily.
So, to dig into Thymeleaf, in this article, we will continue to learn some syntaxes such as conditional statement, loop statement, … in Thymeleaf.
All informations are referenced from this website of Thymeleaf.
Table of Contents
- Introduction expression in Thymeleaf
- Link URLs expression
- Messages expression
- Variable expression
- Selection variable
- Conditional operators
- Conditional evaluation
- Arithmetic operations
- Loop statement
- Recap
Introduction expression in Thymeleaf
There are some following expressions:
- Simple expression
- Variable expression:
${...}
- Selection Variable:
*{...}
- Message expression:
#{...}
- Link URLs expression:
@{...}
- Variable expression:
- Literals
- Text literals: ‘one text’, ‘Another one!’,…
- Number literals: 0, 34, 3.0, 12.3,…
- Boolean literals: true, false
- Null literal: null
- Literal tokens: one, sometext, main,…
- Text operations
- String concatenation: +
-
Literal substitutions: The name is ${name}
- Arithmetic operations:
- Binary operators: +, -, *, /, %
- Minus sign (unary operator): -
- Boolean operations:
- Binary operators: and, or
- Boolean negation (unary operator): !, not
- Comparisons and equality:
- Comparators: >, <, >=, <= (gt, lt, ge, le)
- Equality operators: ==, != (eq, ne)
- Conditional operators:
- If-then: (if) ? (then)
- If-then-else: (if) ? (then) : (else)
- Default: (value) ?: (defaultvalue)
Link URLs expression
Syntax:
@{...}
of Thymeleaf Standard Dialect.
There are two types of URLs:
- Absolute URLs
- Relative URLs
- Page-relative, like
user/login.html
- Context-relative, like
/itemdetails?id=3
(context name in server will be added automatically) - Server-relative, like
~/billing/processInvoice
(allows calling URLs in another context (= application) in the same server - Protocol-relative URLs, like
//code.jquery.com/jquery-2.0.3.min.js
- Page-relative, like
Thymeleaf can handle absolute URLs in any situation, but for relative ones it will require you to use a context object that implements the IWebContext interface, which contains some info coming from the HTTP request and needed to create relative links.
So, we have:
th:href
is an attribute modifier attribute: once processed, it will compute the link URL to be used and set thehref
attribute of the <a> tag to this URL.- We are allowed to use expressions for URL parameters (as you can see in
orderId=${o.id}
). The required URL-encoding operations will also be automatically performed. - If several parameters are needed, these will be separated by commas like
@{/order/process(execId=${execId},execType='FAST')}
- Variable templates are also allowed in URL paths, like
@{/order/{orderId}/details(orderId=${orderId})}
- Relative URLs starting with
/
(like/order/details
) will be automatically prefixed the application context name. - If cookies are not enabled or this is not yet known, a
";jsessionid=..."
suffix might be added to relative URLs so that session is preserved. This is called URL Rewriting, and Thymeleaf allows you to plug in your own rewriting filters by using theresponse.encodeURL(...)
mechanism from the Servlet API for every URL. - The
th:href
tag allowed us to (optionally) have a working statichref
attribute in our template, so that our template links remained navigable by a browser when opened directly for prototyping purposes.
<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
Messages expression
Syntax: #{...}
We will usually use this message expression int th:text
or th:utext
. We can pass parameter into message expression through a pair of parentheses.
<p th:utext="#{${welcomeMsgKey}(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>
So, the value of ${session.user.name}
will be passed to the message expression.
Variable expression
Syntax: ${...}
It is in fact OGNL (Object-Graph Navigation Language) expressions executed on the map of variables contained in the context.
Some expression basic object includes:
#ctx
: the context object.#vars
: the context variable.#locale
: the context locale.#httpServletRequest
: theHttpServletRequest
object (only in Web Contexts).#httpSession
: theHttpSession
object (only in Web Contexts).
<p>Today is: <span th:text="${today}">13 february 2011</span>.</p>
means:
ctx.getVariables().get("today");
Selection variable
Syntax: *{...}
The selection variable is the other way of the variable expression for evaluating a value of expression.
Especially, the asterisk syntax evaluates expressions on selected objects rather than on whole context variables map. As long as there is no selected object, the dollar $
and the asterisk *
syntaxes do exactly the same.
<div th:object="${sesson.user}">
<p>Name: <span th:text="*{firstName}"></span></p>
<p>Name: <span th:text="*{lastName}"></span></p>
<p>Name: <span th:text="*{nationality}"></span></p>
</div>
It is equivalent to:
<div>
<p>Name: <span th:text="${sesson.user.firstName}"></span></p>
<p>Name: <span th:text="${sesson.user.lastName}"></span></p>
<p>Name: <span th:text="${sesson.user.nationality}"></span></p>
</div>
When an object selection is in place, the selected object will be also available to dollar expressions as the #object
expression variable:
<div th:object="${sesson.user}">
<p>Name: <span th:text="${#object.firstName}"></span></p>
<p>Name: <span th:text="${sesson.user.lastName}"></span></p>
<p>Name: <span th:text="*{nationality}"></span></p>
</div>
Conditional operators
Conditional expressions are meant to evaluate only one of two expressions depending on the result of evaluating a condition (which is itself another expression).
<tr th:class="${row.even} ? 'even' : 'old'">
...
</tr>
Else expressions can also be omitted, in which case a null value is returned if the condition is false:
<tr th:class="${row.even} ? 'alt'">
...
</tr>
Next, the special kind of conditional operator is a default expression - Elvis operator. It has no then
part.
<div th:object="${session.user}">
...
<p>Age: <span th:text="*{age} ?: '(no age specified)'">15</span></p>
</div>
It is completely equivalent to:
<p>Age: <span th:text="*{age} ? *{age} : '(no age specified)'">15</span></p>
Conditional evaluation
When we have a specific condition to display elements in html, we can use simple conditionals: if
and unless
.
<div class="container">
<th:block th:if="${#lists.isEmpty(contacts)}">
...
</th:block>
<th:block th:unless="${#lists.isEmpty(contacts)}">
<div class="row">
...
</div>
</th:block>
</div>
With th:block
, we can refer to this part.
The th:if
attribute will not only evaluate boolean conditions. Its capabilities go a little beyond that, and it will evaluate the specified expression as true following these rules:
- If value is not null:
- If value is a boolean and is true.
- If value is a number and is non-zero
- If value is a character and is non-zero
- If value is a String and is not “false”, “off” or “no”
- If value is not a boolean, a number, a character or a String.
- If value is null,
th:if
will evaluate to false.
th:if
has a negative counterpart - th:unless
.
If we want to get the complement of a specific condition, use not
before condition.
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">
view
</a>
We can use th:unless
to transfer the above code that use th:if
to have similiar meaning:
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">
view
</a>
Now, we will go to the next condition statement, it is switch
statement such as th:switch
and th:case
.
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>
As soon as one th:case
attribute is evaluated as true
, every other th:case
attribute in the same switch context is evaluated as false
.
The default option of th:case
is *
.
Loop statement
In Controller, we pass the java.util.List
object into the Model in Spring Boot, assuming that it is prods
object, ResolveViewer have to iterate all elements in this array object. So, to do this, Thymeleaf provide an easy way. It is to use th:each
.
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr th:each="prod, iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
</table>
Because with prods
- java.util.List
object, we can iterate a set of objects. But we can utilize other data structures to iterate by using th:each
.
- Any object implementing
java.util.Iterable
- Any object implementing
java.util.Map
. When iterating maps, iter variables will be of class java.util.Map.Entry. - Any array
- Any other object will be treated as if it were a single-valued list containing the object itself.
When using th:each
, Thymeleaf offers a mechanism useful for keeping track of the status of your iteration: the status variable.
Status variables are defined within a th:each
attribute and contain the following data:
- The current iteration index, starting with 0. This is the
index
property. - The current iteration index, starting with 1. This is the
count
property. - The total amount of elements in the iterated variable. This is the
size
property. - The iter variable for each iteration. This is the
current
property. - Whether the current iteration is even or odd. These are the
even
/odd
boolean properties. - Whether the current iteration is the first one. This is the
first
boolean property. - Whether the current iteration is the last one. This is the
last
boolean property.
In order to use the status variable in th:each
attribute, we can exert two ways to declare it.
- In
th:each
attribute, Writing status variable name such asiterStat
after the iteration variable itself such asprod
, separated by a comma. -
Thymeleaf will always create one status variable for you by suffixing
Stat
to the name of the iteration variable, for example:prodStat
, if we do not explicitly set a status variable.... <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'"> <td th:text="${prod.name}">Onions</td> <td th:text="${prod.price}">2.41</td> <td th:text="${prod.inStock}? #{true} : #{false}">yes</td> </tr>
Arithmetic operations
In these operations, we have two ways to use them because of depending on each template engine:
th:with="isEven=(${prodStat.count} % 2 == 0)"
Or
th:with="isEven=${prodStat.count % 2 == 0}"
Comments and Blocks
Some comment styles in Thymeleaf are:
-
standard html/xml
<!--Comment to something-->
-
parser-level comment blocks
<!--/* We want to comment something here */-->
th:block
will be used in situation where we want to combine some elements into one block.
<table>
<th:block th:each="user : ${users}">
<tr>
<td th:text="${user.login}">...</td>
<td th:text="${user.name}">...</td>
</tr>
<tr>
<td colspan="2" th:text="${user.address}">...</td>
</tr>
</th:block>
</table>
Recap
-
Thymeleaf uses
/templates
as root. And in the defaultapplication.properties
, we have:spring.thymeleaf.prefix=classpath:/templates/
- Conditional evaluation is used to select between tags/elements in html/thymeleaf.
- Conditional operator is used to evaluate only one of two expressions depending on the result of evaluating a condition (which is itself another expression). such as:
<tr th:class="${row.even}? 'even' : 'odd'"> ... </tr>