Spring Boot With Thymeleaf
Spring Boot With Thymeleaf
Spring Boot With Thymeleaf
2
Spring Boot Thymeleaf
⎊ Thymeleaf is a Java template engine for processing and creating HTML, XML, JavaScript,
CSS, and text.
⎊ The library is extremely extensible and its natural templating capability ensures templates can
be prototyped without a back-end – which makes development very fast when compared with
other popular template engines such as JSP.
⎊ Dependency in Spring Boot:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
3
Spring Boot Thymeleaf (cont.)
xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
4
Spring Boot Thymeleaf (cont.)
⎊ Thymeleaf Features
● Make the mapped method in your MVC @Controller objects forward to templates
managed by Thymeleaf.
● Use Spring Expression Language (Spring EL) instead of OGNL.
● Integrated with your form-backing beans and result bindings, including the use of property
editors, conversion services and validation error handling.
● Display internationalization messages from message files
● Support for template logic (condition, iteration, …)
5
Standard Expression Syntax
6
Standard Expression Syntax (cont.)
7
Simple Expression
⎊ Variable Expression ${ }
// ⇒ Controller
@GetMapping("/home")
public String viewHomePage(ModelMap modelMap) {
modelMap.addAttribute("header", "Welcome to our website");
return "index";
}
@GetMapping(LIST_CATEGORIES_URL)
public String viewCategory(ModelMap modelMap) {
List<Category> categories = categoryService.findCategoryAndPrefixHRD();
modelMap.addAttribute("categories", categories); // object forward
return LIST_CATEGORIES_PATH;
}
-------------------------------------------------------------------------------------
-
// ⇒ Template
<p th:text="${header}">HEADER</p>
<p th:text="${categories}">Categories</p>
<p th:text="${#lists.size(categories)}">Size of Categories</p> 8
Simple Expression (cont.)
----------------------------------------------------------------------------------------------
// ⇒ Controller
@GetMapping(LIST_CATEGORIES_URL)
public String viewCategory(ModelMap modelMap) {
List<Category> categories = categoryService.findCategoryAndPrefixHRD();
modelMap.addAttribute("categories", categories); // object forward
return LIST_CATEGORIES_PATH;
}
----------------------------------------------------------------------------------------------
// ⇒ Template
<tr th:each="category, state : ${categories}" th:object="${category}">
<td th:text="${state.count}">1.</td>
<td th:text="*{name}">Update software</td>
</tr>
9
Simple Expression (cont.)
⎊ Message Expression #{ }
// ⇒ messages.properties
message = Hello
// ⇒ messages_kh.properties
message = សួសី្ដ
// ⇒ Template
<h1 th:text="#{message}">Message</h1>
// ⇒ Rendering (en)
<h1>Hello</h1>
// ⇒ Rendering (kh)
<h1>សួសី<
្ដ /h1>
10
Simple Expression (cont.)
⎊ Message Expression #{ }
// ⇒ messages.properties
welcome.message = Hello {0}! Welcome to {1}
// ⇒ Template
<h1 th:text="#{welcome.message(‘Sreynich’,’Khmer Academy’)}">Start up message</h1>
// ⇒ Rendering
<h1>Hello Sreynich! Welcome to Khmer Academy</h1>
11
Simple Expression (cont.)
⎊ Message Expression #{ }
● LanguageConfiguration.java
@Configuration
public class LanguageConfiguration implements WebMvcConfigurer {
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(new Locale("kh"));
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
12
Simple Expression (cont.)
⎊ Message Expression #{ }
● In order to change the default location of message properties file, we need to implement the
bean of MessageSource in LanguageConfiguration.
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("i18n/messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
spring.messages.basename=i18n/messages
spring.messages.encoding=UTF-8
13
Simple Expression (cont.)
// Absolute URLs
<a th:href="@{https://attacomsian.com/about}">About Me</a>
<a href="https://attacomsian.com/about">About Me</a>
// Context-relative URLs
<a th:href="@{/blog/what-is-thymeleaf}">What is Thymeleaf?</a>
<a href="/webapp/blog/what-is-thymeleaf">What is Thymeleaf?</a>
// Server-Relative URLs
<a th:href="@{~/topic/thymeleaf}">Thymeleaf 101</a>
<a href="topic/thymeleaf">Thymeleaf 101</a>
// Protocol-Relative URLs
<script th:src="@{//example.com/js/script.js}"></script>
<link th:href="@{//example.com/css/styles.css}" rel="stylesheet">
<script src="//example.com/js/script.js"></script>
<link href="//example.com/css/styles.css" rel="stylesheet">
14
Simple Expression (cont.)
15
Simple Expression (cont.)
⎊ Fragment Expression ~{ }
● Will be detailed in Fragment part
16
Text Operations
// Concatenation
<p th:text="'Hello ' + ${username} + '!, Nice to meet you'">Paragraph</p>
// Preprocessing
<p th:text="'Hello __${username}__!, Nice to meet you'">Paragraph</p>
// Literal Substitution
<p th:text="|Hello ${username}, Nice to meet you|">Paragraph</p>
// Concatenation
Hello Dara!, Nice to meet you
// Preprocessing
Hello Dara!, Nice to meet you
// Literal Substitution
Hello Dara!, Nice to meet you
17
Outputting Raw Values
// ⇒ Controller
@GetMapping("/home")
public String viewHomePage(ModelMap modelMap) {
modelMap.addAttribute("htmlText", "<h1>Welcome to our website</h1>");
return "index";
}
// ⇒ Template
<p th:utext="${htmlText}">HEADER</p>
18
Text Inlining
// ⇒ Template
<h1 th:inline="text" >
[[${name}]],
it is our sincere pleasure...
</h1>
// ⇒ Rendering
<h1>
Chan Chhaya,
it is our sincere pleasure...
</h1>
19
JavaScript Inlining
⎊ Syntax
<script th:inline="javascript">
// write code here
</script>
⎊ Example
<script th:inline="javascript">
var name = [[${user.firstName + ' ' + user.lastName}]];
var email = [[${user.email}]];
var age = [[${user.age}]];
var createdAt = [[${#dates.format(user.created, 'EEE, MMMM dd, yyyy')}]];
</script>
20
JavaScript Inlining (cont.)
// ⇒ Template
<script th:inline="javascript">
let number = [(${number})]
</script>
// ⇒ Rendering
<script>
let username = 1000
</script>
⎊ Be careful while using an unescaped expression in JavaScript mode. You might end up generating a
malformed JavaScript code.
21
JavaScript Inlining (cont.)
⎊ You can even specify default values for variables by wrapping the inline expressions in JavaScript
comments like below:
// ⇒ Template
<script th:inline="javascript">
var name = /*[[${user.firstName + ' ' + user.lastName}]]*/ "John
Deo";
var email = /*[[${user.email}]]*/ "[email protected]";
var age = /*[[${user.age}]]*/ 25;
var createdAt = /*[[${user.created}]]*/ "January 29, 2020";
</script>
22
Attribute Manipulation
⎊ Example:
⎊ Rendering:
<p class="right"
data-row-id="12020">Cambodia</p>
<input type="text" disabled="disabled">
<label><input type="checkbox" name="active" checked="checked">
Active</label>
<select name="gender" id="gender">
<option value="">Select gender from the list</option>
<option value="M" selected="selected">Male</option>
<option value="F">Female</option>
</select>
24
Condition Operation
⎊ if and unless
● The th:if=”${condition}” attribute is used to display a section of the view if the condition is
met.
● The th:unless=”${condition}” attribute is used to display a section of the view if the condition
is not met.
⎊ Example:
<td>
<span th:if="${student.gender} == 'M'" th:text="Male" />
<span th:unless="${student.gender} == 'M'" th:text="Female" />
</td>
25
Condition Operation
<td th:switch="${student.gender}">
<span th:case="'M'" th:text="Male" />
<span th:case="'F'" th:text="Female" />
<span th:case="*" th:text="Other" />
</td>
26
Condition Operation
⎊ th:each
● If the model attribute is a collection of objects, the th:each tag attribute can be used to iterate
over it.
⎊ Example:
<tbody>
<tr th:each="student: ${students}">
<td th:text="${student.id}" />
<td th:text="${student.name}" />
</tr>
</tbody>
27
Condition Operation
⎊ th:each (cont.)
● 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: index,
count, size, current, first, last, and even/odd.
⎊ Example:
<tr
th:each="student, iStat : ${students}"
th:style="${iStat.odd}? 'font-weight: bold;'"
th:alt-title="${iStat.even}? 'even' : 'odd'">
<td th:text="${student.id}" />
<td th:text="${student.name}" />
</tr> 28
Handling User Input
⎊ Form input can be handled using the th:action=”@{url}” and th:object=”${object}” attributes.
⎊ The th:action is used to provide the form action URL and th:object is used to specify an object to
which the submitted form data will be bound.
⎊ Individual fields are mapped using the th:field=”*{name}” attribute, where the name is the matching
property of the object.
⎊ Controller to handle the form submission:
⎊ Example:
<ul>
<li th:each="err : ${#fields.errors('id')}" th:text="${err}" />
<li th:each="err : ${#fields.errors('name')}" th:text="${err}" />
</ul>
31
Thymeleaf Page Layouts
⎊ Usually websites share common page components like the header, footer, menu and possibly
many more.
⎊ These page components can be used by the same or different layouts.
⎊ There are two main styles of organizing layouts in projects:
○ Include style: are built by embedding common page component code directly within each
view to generate the final result.
○ Hierarchical style: are usually created with a parent-child relation, from the more general
part (layout) to the most specific ones (subviews; e.g. page content).
32
Fragment
⎊ Fragment in Thymeleaf is a small piece of code that can be included in other templates.
⎊ It is a common practice in web development to create reusable, small components like header,
footer, navigation menu and other parts of a website that repeated used on multiple pages.
⎊ To define a Thymeleaf fragment, you need to use the th:fragment attribute.
<div th:fragment=”fragmentName”>
<!-- Content here -->
</div>
33
Fragment (cont.)
34
Fragment (cont.)
35
Fragment (cont.)
⎊ th:fragment attribute can specify arguments, just like methods, which called
Parameterized Fragments.
⎊ Example:
⎊ If the fragments, you want to conditionally include, are defined in separate files, you have to
use the fragment expression syntax introduced in version 3.0:
<div th:replace="${user.admin} ? ~{fragments/footer :: footer} : ~{fragments/components :: footer}"></div>
37
Fragment (cont.)
⎊ Flexible Layouts
○ This allows us to create fragments that can be enhanced with the markup coming from the
calling templates, thus providing a very flexible layout technique.
</head> 38
Fragment (cont.)
</head>
39
Fragment (cont.)
<head>
</head>
40
Fragment (cont.)
⎊ Layout Inheritance
○ You can create a single file called layout.html that has the following structure:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:fragment="layout(title, content)">
<head>
<meta charset="UTF-8">
<title th:replace="${title}">Layout Title</title>
</head>
<body>
<section th:replace="${content}">
<p>Layout contents</p>
</section>
<footer>
<p>© 2020 Layout footer</p>
</footer>
</body>
</html>
41
Fragment (cont.)
<section>
<p>This is just an extra text.</p>
<a th:href="@{/contact-us}">Contact Us</a>
</section>
</body>
</html>
42
Fragment (cont.)
⎊ While fragments are good enough for working with small applications, they become hard to
maintain and update as the application grows to hundreds of views.
⎊ Thymeleaf Layout Dialect is an open-source dialect for Thymeleaf that lets you easily build
complex layouts and reusable templates to achieve higher code reusability in Spring Boot
applications.
⎊ Gradle & Maven Dependency:
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>
44
Thymeleaf Layout Dialect (cont.)
⎊ Thymeleaf Layout Dialect will introduce the layout namespace along with 5 new attribute
processors that you can use in your templates: decorate, title-pattern, insert,
replace, and fragment.
⎊ To use Layout Dialect attributes in a template, you need to add the layout namespace to the
<html> tag as shown below:
<!DOCTYPE html>
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
</html>
45
Thymeleaf Layout Dialect (cont.)
⎊ layout:fragment - attribute marks sections in your layout or reusable templates that can be
replaced by sections with the same name in content templates.
⎊ Example:
<!DOCTYPE html>
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<body>
<div layout:fragment="content">
<p>Body contents</p>
</div>
</body>
</html>
⎊ Note: Fragment names must be unique within a template, otherwise fragment mismatches will
occur and an exception will be thrown. 46
Thymeleaf Layout Dialect (cont.)
<!DOCTYPE html>
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout}">
<body>
<div layout:fragment="content">
<p>Welcome to Thymeleaf Layout Dialect</p>
</div>
</body>
</html>
47
Thymeleaf Layout Dialect (cont.)
48
Thymeleaf Layout Dialect (cont.)
49
Thymeleaf Ajax Fragment
<div th:fragment="content1">
This is Content 1
</div>
<div th:fragment="content2">
This is Content 2
</div>
50
Thymeleaf Ajax Fragment (cont.)
@GetMapping("/ajax/content1")
public String fragmentContent1() {
return "user :: content1";
}
@GetMapping("/ajax/content2")
public String fragmentContent2() {
return "user :: content2";
}
}
51
Thymeleaf Ajax Fragment (cont.)
⎊ Dependency
// Maven
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
// Gradle
implementation group: 'org.thymeleaf.extras', name:
'thymeleaf-extras-springsecurity5', version: '3.0.4.RELEASE'
53
Thymeleaf Security (cont.)
⎊ Namespace
<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
⎊ Security Dialect
<p>Spring Security Thymeleaf tutorial</p>
<div sec:authorize="hasRole('USER')">Text visible to user.</div>
<div sec:authorize="hasRole('ADMIN')">Text visible to admin.</div>
<div sec:authorize="isAuthenticated()">
Text visible only to authenticated users.
</div>
Authenticated username:
<div sec:authentication="name"></div>
Authenticated user roles:
<div sec:authentication="principal.authorities"></div>
54
Thymeleaf Security (cont.)
55
References
56
THANKS!
Please try hard to study..!
57