kamer.dev

Thymeleaf Crash Course

- #spring - #java - #thymeleaf

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

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 < (&gt;), > (&lt;), >=, , != 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