You are on page 1of 57

Spring Boot with Thymeleaf

Prepared by Spring Team


Content

⎆ Spring Boot Thymeleaf


⎆ Standard Expression Syntax
⎆ Content Negotiation
⎆ Spring Hateoas

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.)

⎊ Java Template View Engines


● Thymeleaf
● Apache Velocity
● Apache FreeMarker
● Pebble
● Java Server Page (JSP)
⎊ To use Thymeleaf with HTML, we need to add the following attributes in the html start HTML
element:

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

⎊ Simple expressions: ⎊ Literals:


● Variable Expressions: ${...} ● Text literals: 'one text', 'Another one!',…
● Selection Variable Expressions: *{...} ● Number literals: 0, 34, 3.0, 12.3,…
● Message Expressions: #{...} ● Boolean literals: true, false
● Link URL Expressions: @{...} ● Null literal: null
● Fragment Expressions: ~{...} ● Literal tokens: one, sometext, main,…

6
Standard Expression Syntax (cont.)

⎊ Text operations: ⎊ Comparison and equality:


● String concatenation: + ● Comparators: >, <, >=, <= (gt, lt, ge, le)
● Literal substitutions: |The name is $ ● Equality operators: ==, != (eq, ne)
{name}| ⎊ Conditional operations:
⎊ Boolean operations: ● If-then: (if) ? (then)
● Binary operators: and, or ● If-then-else: (if) ? (then) : (else)
● Boolean negation (unary operator): !, not ● Default: (value) ?: (defaultvalue)
⎊ Special tokens:
● No-Operation: _

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.)

⎊ Selection Variable Expression *{ }


public class Category {
private int id;
private String name;
// setter & getter
}

----------------------------------------------------------------------------------------------
// ⇒ 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;
}

● Or using the application.properties

spring.messages.basename=i18n/messages
spring.messages.encoding=UTF-8

13
Simple Expression (cont.)

⎊ Link URL Expression @{ }

// 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.)

⎊ Link URL Expression @{ }

// URLs With Parameters


<a th:href="@{https://www.google.com/search(q='thymeleaf')}">Search Thymeleaf</a>
<a th:href="@{https://www.google.com/search(q='thymeleaf',num=12,lang=en)}">Result</a>
<a href="https://www.google.com/search?q=thymeleaf">Search Thymeleaf</a>
<a href="https://www.google.com/search?q=thymeleaf&amp;num=12&amp;lang=en">Search Thymeleaf</a>

// Using Expressions in URLs


<a th:href="@{/posts/preview(id=${post.id})}">View Post</a>
<a href="/posts/preview?id=15">View Post</a>

// URLs with Path Variables


<a th:href="@{/posts/{id}/preview(id=${post.id})}">View Post</a>
<a th:href="@{/{username}/posts/{id}/preview/(username=${post.user.handle},id=${post.id})}">Hi</
a>
<a href="/posts/15/preview">View Post</a>
<a href="/attacomsian/posts/15/preview">View Post</a>

15
Simple Expression (cont.)

⎊ Fragment Expression ~{ }
● Will be detailed in Fragment part

16
Text Operations

⎊ String Concatenation & String Substitution

// 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

⎊ Render HTML code

// ⇒ 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.)

⎊ Example with unescaped expression:

// ⇒ 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}]]*/ "john@example.com";
var age = /*[[${user.age}]]*/ 25;
var createdAt = /*[[${user.created}]]*/ "January 29, 2020";
</script>

⎊ More reference ⇒ JavaScript variable in Thymeleaf

22
Attribute Manipulation

⎊ Example:

<p th:class="${answer ? 'right' : 'wrong'}"


th:classappend="${!answer ? 'underline' : ''}"
th:attr="data-row-id=${answerId}"
th:text="${answerText}">Your answer</p>

<input type="text" th:disabled="${answer}">


<label><input type="checkbox" name="active" th:checked="${answer}">
Active</label>

<select name="gender" id="gender">


<option value="">Select gender from the list</option>
<option value="M" th:selected="${gender == 'M'}">Male</option>
<option value="F" th:selected="${gender == 'F'}">Female</option>
</select>
23
Attribute Manipulation (cont.)

⎊ 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

⎊ switch and case


● The th:switch and th:case attributes are used to display content conditionally using the switch
statement structure.
⎊ Example:

<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:

@RequestMapping(value = "/saveStudent", method = RequestMethod.POST)


public String saveStudent(@ModelAttribute Student student,
BindingResult errors, Model model) {
// logic to process input data
}
29
Handling User Input (cont.)

⎊ Example:

<form action="#" th:action="@{/saveStudent}" th:object="${student}"


method="post">
<table border="1">
<tr>
<td><label th:text="#{msg.id}" /></td>
<td><input type="number" th:field="*{id}" /></td>
</tr>
<tr>
<td><label th:text="#{msg.name}" /></td>
<td><input type="text" th:field="*{name}" /></td>
</tr>
<tr>
<td><input type="submit" value="Submit" /></td>
</tr>
</table>
</form>
30
Handling User Input (cont.)

⎊ Displaying Validation Errors


● The #fields.hasErrors() function can be used to check if a field has any validation errors.
● The #fields.errors() function can be used to display errors for a particular field.
● The field name is the input parameter for both these functions.
● 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.)

⎊ You can easily include the fragment by using one of them:


○ th:insert - Inserts the fragment content inside the host tag
○ th:include - Similar to th:insert but it only inserts the content of the specified
fragment (depreciated since 3.0)
○ th:replace - Replaces the host tag with the specified fragment content
⎊ Syntax

<div th:insert="fragment_location/file_name :: fragment_name"></div>


<div th:include="fragment_location/file_name :: fragment_name"></div>
<div th:replace="fragment_location/file_name :: fragment_name"></div>

34
Fragment (cont.)

⎊ Including with DOM Selectors


○ You don’t need to explicit use th:fragment attribute to define fragments.
○ They can be included in another template by using just DOM selector like class name,
element ID, or tag name similar to do what we do in JavaScript.
<div th:insert="fragments/components ::
div.title"></div>
○ The above example will include a <div> element that has the .title CSS class from
the components.html file.

35
Fragment (cont.)

⎊ th:fragment attribute can specify arguments, just like methods, which called
Parameterized Fragments.
⎊ Example:

<div th:fragment="name(firstName, lastName)">


<p>
Hey! I'm <span th:text="${firstName + ' ' + lastName}"></span>!
</p>
</div>

<!--Option 1 to call parameterized fragment-->


<div th:replace="fragments/components :: name('John', 'Doe')"></div>
<!--Option 2 to call parameterized fragment-->
<div th:replace="fragments/components :: name(firstName='John', lastName='Doe')"></div>
<!--the order of the parameters is no longer important-->
<div th:replace="fragments/components :: name(lastName='Doe', firstName='John')"></div>
36
Fragment (cont.)

⎊ Thymeleaf fragment inclusion syntax fully supports conditional expressions to dynamically


load different fragments.

<div th:replace="fragments/footer :: ${user.admin} ? 'footer-admin' : 'footer'"></div>

⎊ 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 th:fragment="baseHead(title, links)">

<title th:replace="${title}">Atta | Founder. Developer. Blogger.</title>

<!-- default styles and scripts -->


<link rel="shortcut icon" th:href="@{/images/favicon.ico}">
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
<script th:src="@{/js/jquery.min.js}"></script>

<!-- placeholder for additional links -->


<th:block th:replace="${links}" />

</head> 38
Fragment (cont.)

⎊ Flexible Layouts (cont.)

<head th:replace="fragments/base :: baseHead(~{::title}, ~{::link})">

<title>Introduction to Thymeleaf Fragments | Atta Blog</title>

<link rel="stylesheet" th:href="@{/css/font-awesome.min.css}">


<link rel="stylesheet" th:href="@{/css/jquery-ui.css}">

</head>

39
Fragment (cont.)

⎊ Flexible Layouts (cont.)

<head>

<title>Introduction to Thymeleaf Fragments | Atta Blog</title>

<!-- default styles and scripts -->


<link rel="shortcut icon" href="/images/favicon.ico">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<script src="/js/jquery.min.js"></script>

<!-- placeholder for additional links -->


<link rel="stylesheet" href="/css/font-awesome.min.css">
<link rel="stylesheet" href="/css/jquery-ui.css">

</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>

<h1>Layout Main Heading</h1>

<section th:replace="${content}">
<p>Layout contents</p>
</section>

<footer>
<p>&copy; 2020 Layout footer</p>
</footer>

</body>
</html>

41
Fragment (cont.)

⎊ Layout Inheritance (cont.)


○ Both these parameters will be replaced with the calling template tags by using the
fragment expressions as shown below:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
th:replace="layout :: layout(~{::title}, ~{::section})">
<head>
<title>Welcome to My Site</title>
</head>
<body>

<section>
<p>This is just an extra text.</p>
<a th:href="@{/contact-us}">Contact Us</a>
</section>

</body>
</html>
42
Fragment (cont.)

⎊ Layout Inheritance (cont.)


○ Final HTML output
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome to My Site</title>
</head>
<body>
<h1>Layout Main Heading</h1>
<section>
<p>This is just an extra text.</p>
<a href="/contact-us">Contact Us</a>
</section>
<footer>
<p>&copy; 2020 Layout footer</p>
</footer>
</body>
</html>
43
Thymeleaf Layout Dialect

⎊ 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.)

⎊ layout:decorate - is an important attribute processor that is used in content templates. It is


declared in the root tag (usually <html> in HTML document) as a fragment expression to
specify the layout template to decorate the current content template.
⎊ Example:

<!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.)

⎊ layout:title-pattern - allows us to specify a pattern with some special tokens to control


the resulting <title> element.
⎊ Example:

<title layout:title-pattern="$CONTENT_TITLE - $LAYOUT_TITLE">Atta Blog</title>

48
Thymeleaf Layout Dialect (cont.)

⎊ Layout Dialect Example


○ Create layout template (Master Layout)
○ Create content template (home, about-us, contact)

49
Thymeleaf Ajax Fragment

⎊ How to implement Thymeleaf Ajax Fragment:


○ Create template file which contains the fragments

<div th:fragment="content1">
This is Content 1
</div>
<div th:fragment="content2">
This is Content 2
</div>

50
Thymeleaf Ajax Fragment (cont.)

⎊ How to implement Thymeleaf Ajax Fragment: (cont.)


○ Create mapping URL to return the fragment of your template file
@Controller
public class AjaxController {

@GetMapping("/ajax/content1")
public String fragmentContent1() {
return "user :: content1";
}

@GetMapping("/ajax/content2")
public String fragmentContent2() {
return "user :: content2";
}
}
51
Thymeleaf Ajax Fragment (cont.)

⎊ How to implement Thymeleaf Ajax Fragment: (cont.)


○ Using Ajax to request in your template file
$(function () {
$.ajax({
url:'/ajax/content1',
method:'GET',
success:function (response) {
console.log(response)
$('.content').html(response)
},
error: function (error) {
console.log(error)
}
})
})
52
Thymeleaf Security

⎊ 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.)

⎊ Instruction ⇒ Spring Boot+Spring Security+Thymeleaf Simple Tutorial

55
References

• Setting up a JavaScript variable in Thymeleaf


• Introduction to Using Thymeleaf in Spring
• Thymeleaf Javascript Inline Example with Variable
• Working with Thymeleaf Layout Dialect in Spring Boot

56
THANKS!
Please try hard to study..!

57

You might also like