Hello! In this article I will explain what template engine is and give you a crash course about Thymeleaf with Spring Boot. My main resources are official documentations which are published under the title of Thymeleaf 3.0 and Thymeleaf+Spring. Github repo is also available at the end of the article.
- 1. Theoretical
- 2. Practical
- 2.1 Folder Hierarchy
- 2.2 Syntax
- 2.2.1 Simple Expressions
- 2.2.2 Constructing Texts
- 2.2.3 Arithmetic Operations, Comparators and Equality
- 2.2.4
th:if
,th:unless
,th:switch
,th:case
- 2.2.5 Loops with
th:each
- 2.2.6 Ternary and Elvis Operator
- 2.2.7 th:* Attributes
- 2.2.8 General Attribute Modification
- 2.2.9 Local Variables
- 2.2.10 Thymeleaf Comment Blocks
- 2.2.11 Expression Inlining
- 2.3 Expression Objects
- 3. A Few Words
1. Theoretical
1.1 What is Thymeleaf?
You can find the shortest and -I think- the best explanation at Thymeleaf’s docs:
Thymeleaf is a modern server-side Java template engine for…
Keywords are server-side and Java template engine. Rest of the definition is not important at this point. Let’s look at template engine.
Template engine is a language-independent software that processes given template and produces a result by filling it with data. There are several template engines for different languages. For instance, there is a page template for Jekyll below:
Jekyll uses Liquid template language. When you read the example, you can find out that post
is an object that has title
, content
and tags[]
fields. You can render this page in your mind.
Once you have a clear definition of template and template engine you can comprehend the difference between client-side and server-side rendering. At client-side rendering, client receives the template with some data(XML, JSON etc.) and renders them at the client-side. But when you use server-side templating, template and the data is processed at the server-side and the final result is sent to client.
Thymeleaf supports 6 kinds of templates which are HTML, XML, Text, JavaScript, CSS and RAW.
Thus, we have an idea of Thymeleaf. Let’s solidify our understanding with a brief definition of process.
We will prepare a template that extends simple HTML syntax; give Java objects and the template to template engine. Template engine will process these inputs and create an HTML file.
1.2 Thymeleaf Standart Dialect
If you don’t want to learn the details you can omit this section but I recommend you to learn terminology of Thymeleaf. This whole section also exists in the documentation but I want to explain them with my own sentences.
Let’s go with the documentation again:
An object that applies some logic to a markup artifact is called a processor, and…
This sentence seems so simple but let’s clarify our understanding by explaining the term markup. Yeah, I know, this is not a mystery for the people who have written at least a few lines with HTML but most of the people don’t know what it is. Markups are symbols and texts that we insert to a document to format its content. For instance when you type <h1>Thymeleaf</h1>
in a HTML document, you define it as the highest level heading. That’s why we call it markup language. After this definition, the sentence from the documentation is more clear when you read again: “An object that applies some logic to a markup artifact is called a processor, and…"
These processors are combined and called dialects. Thymeleaf provides us Standart Dialect which is mostly enough for basic processing but in case not, you can create your own processors and dialects.
2. Practical
As I specified at the beginning, this article will be based on Thymeleaf in a Spring Boot app. Therefore, I will explain both Standard Dialect and SpringStandard Dialect. Now, go to https://start.spring.io/ or use IDE plugins to create the project. Spring Web and Thymeleaf dependencies are enough.
2.1 Folder Hierarchy
When you create a Thymeleaf dependent project with Spring Initalzr, this is what you get by default:
Put your static content (e.g. css, js, img) in static
folder and templates in templates
folder. But it’s not a must. You can change this default also.
Best approach is to set spring.resources.static-locations
property in application.properties file. For instance:
spring.resources.static-locations=file:/awesome/path
or
spring.resources.static-locations=classpath:/resources/my_static_content
You can change templates folder as well.
2.2 Syntax
2.2.1 Simple Expressions
spring.thymeleaf.prefix=classpath:/my_templates/
-
Variable Expressions(
${...}
)When you send data in model object with a controller, you can process them with this syntax.
<p> Hello <span th:text="${username}"></span> </p>
We will cover
th:...
attributes later. Just focus on${...}
part.
-
Selection Variable Expressions(
*{...}
)This is another expression to process variables and more convenient for some cases. Instead of picking an object from whole model context, you can select from particular selected context. Let’s clarify this with an example:
<div> <h3 th:text="${post.title}"></h3> <p th:text="${post.excerpt}"></p> </div>
This is standard variable expression. It’s possible to use selection variable expression syntax:
<div th:object="${post}"> <h3 th:text="*{title}"></h3> <p th:text="*{excerpt}"></p> </div>
-
Message Expressions(
#{...}
)Best way to extract static texts to achieve i8n is using
MessageSource
. You can process these texts with this syntax. For instance if you want to process the text below:greeting.title=Message Expressions
You can write:
<h1 th:text="#{greeting.title}"></h1>
You can also pass variables to this texts:
greeting.text=Hello {0}! How are you today?
with:
<p th:text="#{greeting.text(${username})}"></p>
-
Link URL Expressions(
@{...}
)There are some attributes that needs URL as input. Thymeleaf provides a good syntax for URLs.
You can;
- create absolute URLs:
<a th:href="@{https://google.com}">...</a>
- create Context-relative URLs:
<a th:href="@{/comments}">...</a>
- Server-relative URLs:
<a th:href="@{~/posts}">...</a>
- Protocol-relative URLs:
<a th:href="@{//google.com}">...</a>
- use variables inside URLs with expression preprocessing which process the expression between “__ __” :
<a th:href="@{/users/__${userId}__/posts}">...</a>
- define URL parameters:
<a th:href="@{/posts(page=${pageNumber}, tag='java')}"></a>
-
Fragment Expressions(
~{...}
)I think its much harder than it should be for a crash course.
2.2.2 Constructing Texts
Thymeleaf provides a good syntax to construct texts. First one is +
operator.
<span th:text="'Hello' + ${username} + '! How are you today?'"></span>
You can do the same with literal substitutions:
<span th:text="|Hello ${username}!How are you today?|"></span>
Combining literal substitutions with other types is also possible.
<span th:text="'Combined: ' + |Hello ${username}!How are you today?|"></span>
2.2.3 Arithmetic Operations, Comparators and Equality
You can use arithmetic operators in Thymeleaf also.
<div th:with="totalCost=${perPrice} * ${totalCount}">
<span th:text="|Total Cost: ${totalCost}|"></span>
</div>
<div th:if="${currentProfile} == 'DEV'">
<span>Wubba Lubba Dub Dub</span>
</div>
You can also try < (>)
, > (<)
, >=
, ≤
, !=
or their textual aliases gt
, lt
, ge
, le
, not
, eq
, neq/ne
.
2.2.4 th:if
, th:unless
, th:switch
, th:case
I think there is no need to explain th:if
.
<p th:if="${username} neq null">
<span th:text="|User exists with name ${username}|"></span>
</p>
There is also th:unless
which is inverse of th:if
.
th:switch
/ th:case
is also the same.
<div th:switch="${currentProfile}">
<p th:case="'DEV'">Dev profile.</p>
<p th:case="'PROD'">Prod profile.</p>
</div>
2.2.5 Loops with th:each
Arrays and any kinds of objects that implement Iterable
, Enumaration
, Iterator
, Map
can be used for iteration.
<ul th:each="post : ${posts}">
<li th:object="${post}" th:text="|*{title} - *{excerpt}|"></li>
</ul>
2.2.6 Ternary and Elvis Operator
Ternary operator is not an unfamiliar term. So there’s no need to explain.
<div th:text="${username == null} ? 'Anonymous' : ${username}"></div>
Elvis operator is like Ternary operator but it doesn’t have then part. Let’s write the ternary expression above with the Elvis operator.
<div th:text="${username}?: 'Anonymous'"></div>
2.2.7 th:* Attributes
We’ve gone through most of the syntax. You can use th:...
syntax for all HTML5 attributes. There are some examples below:
<img th:src="@{/bernard-black.jpg}"/>
<form th:action="@{~/users}" method="post" />
<input type="button" th:value="#{save_button}">
<input type="checkbox" th:checked="${isUserActive}">
<div th:data-index="${index}"></div>
2.2.8 General Attribute Modification
Standard Dialect has th:attr
, th:attrappend
, th:attrprepend
, th:classappend
, th:styleappend
to modify attributes.
<input type="button" th:attr="value='Click me!'"/>
As you see, th:attr
does not offer a new feature. But the others are more useful. I won’t use all of them in this article. Here’s an example:
<div th:classappend="${isMessageNew}? 'notification'" th:text="${message}"></div>
2.2.9 Local Variables
There are two types of local variable definitions in Thymeleaf. First one is th:object
. I have gave an example at 2.2.1. But let’s remember it again:
<div th:object="${post}">
<h3 th:text="*{title}"></h3>
<p th:text="*{excerpt}"></p>
</div>
In this example, fields of ${post}
is accessible only within div
tag. That’s why we call it local variable.
Another option is th:with
. This option is more flexible relatively. You can declare a variable in a tag and use it with child tags.
<div th:with="featuringPost=${posts[2]}">
<p th:text="|Post of the week: ${featuringPost.title}|"></p>
</div>
2.2.10 Thymeleaf Comment Blocks
As you know we use <!-- -->
syntax to create a comment block in HTML. It’s the same with Thymeleaf since Thymeleaf treats them in the same way. But there’s another option. You can create parser-level comment blocks that is removed after the template is parsed and won’t be visible to end-users.
<!-- Users can see me! -->
<!--/* I'm invisible! */-->
There are also prototype-only comment blocks and they are very useful with th:block. But it’s much for a crash course. You can see 11.3 and 11.4.
2.2.11 Expression Inlining
So far, we have seen only tag attributes but Standard Dialect provides also expression inlining. We’ve seen different options:
<p> Hello <span th:text="${username}"></span></p>
<p th:text="|Hello ${username}|"> </p>
<p th:text="'Hello ' + ${username}"></p>
All of them are nice for different cases but you can also use:
<p>Hello [[${username}]]</p>
But personally, I don’t find them useful.
2.3 Expression Objects
So far, we have provided some data to Thymeleaf and used them. There are also some objects which are always available. There are many objects and they have more than 100 methods totally and this would make this article really long. So I will show none or one example per object. 2.3.1 and 2.3.2 will be mostly a list of objects. You can find more in the documentation. [1] [2]
2.3.1 Expression Basic Objects
First two objects are base objects and the others are Web context namespace shortcuts.
-
#ctx
Context object. There are also two synonyms: #vars and #root.
-
#locale
Gets locale of current request.
<p th:text="${#locale}"></p>
-
param
Gets request parameters.
-
session
Gets session attributes.
-
application
Gets application/servlet context attributes.
2.3.2 Expression Utility Objects
-
#execInfo
It provides information about processed template.
-
#messages
Another option for use externalized variables.
<p th:text="${#messages.msg('ext_msg')}"></p>
-
#uris
Let’s you perform URI/URL operations.
-
#conversions
Another option for double brackets. You can set target class.
-
#dates
Methods for
java.util.Date
objects.<p th:text="${#dates.createNow()}"></p>
-
#calendars
java.util.Calendar
version of #dates.
-
#numbers
Utilities for number objects.
-
#string
Utilities for
String
.<p th:if="${#strings.equals(currentProfile, 'DEV')}">Dev Profile</p>
- #objects
- #bools
- #arrays
-
#lists
<p th:if="${#lists.size(draftPosts) == 0}">No drafts.</p>
- #sets
- #maps
- #aggregates
- #ids
3. A Few Words
As the title suggests it’s a crash course only. Two documentations which I’ve used is much larger. You can go further by checking them. I’m thinking of writing a few blog posts about Thymeleaf also. If I do so, I will add their links here.
Github Repo: https://github.com/kamer/thymeleaf-crash-course