--Reading is "Time Leaf" --One of the template engines --Recommended for use with Spring Boot --You can write the template as pure HTML
Investigate on the assumption that it runs as a view template engine of Spring MVC on Spring Boot.
Hello World
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.5.6.RELEASE'
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
compile 'org.springframework.boot:spring-boot-devtools'
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
}
ext['thymeleaf.version'] = '3.0.7.RELEASE'
ext['thymeleaf-layout-dialect.version'] = '2.2.2'
jar.baseName = 'thymeleaf'
--Is spring-boot-devtools
a tool that allows you to reflect code changes immediately? So, it is not directly related to the use of Thymeleaf, but it is included because it is convenient
--As of August 2017, if Thymeleaf is enabled in Spring Boot (spring-boot-starter-thymeleaf
is added to the dependency), ver 2.x system will be adopted.
--However, Thymeleaf is currently in the ver 3.x series.
--To make Thymeleaf used by Spring Boot 3.x, add 'thymeleaf.version'
and 'thymeleaf-layout-dialect.version'
to the extended property (ʻext). --The version number is
'thymeleaf.version' is the version of ʻorg.thymeleaf: thymeleaf-spring4
.
--'thymeleaf-layout-dialect.version'
specifies the version of nz.net.ultraq.thymeleaf: thymeleaf-layout-dialect
respectively.
- Maven Repository: org.thymeleaf » thymeleaf-spring4
- Maven Repository: nz.net.ultraq.thymeleaf » thymeleaf-layout-dialect
Folder structure
|-build.gradle
`-src/main/
|-java/sample/thymeleaf/
| |-Main.java
| `-web/
| `-HelloController.java
|
`-resources/
|-application.properties
`templates/
`-hello.html
application.properties
spring.thymeleaf.mode=HTML
--By default it works in HTML5 mode --Since HTML mode is recommended from Thymeleaf ver 3.x, a warning message is output to the console in HTML5 mode. --If you put this setting, it will work in HTML mode and no warning will be issued.
Main.java
package sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello Thymeleaf!!");
return "hello";
}
}
--With the default settings of Spring Boot, template files are searched in /resources/templates/[Controller return value] .html
under the classpath.
--In the case of the above implementation, src / main / resources / templates / hello.html
will be used as the template file.
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<h1 th:text="${message}"></h1>
</body>
</html>
--Template files can be written as pure HTML
--Attributes for Thymeleaf can be used by declaring a namespace with xmlns: th =" http://www.thymeleaf.org "
Execution result
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<h1 th:text="'hello world'"></h1>
</body>
</html>
Execution result
Description
hello.html
<h1 th:text="'hello world'"></h1>
--By setting th: text =" [value to be output] "
, the text element of that tag can be output.
--Thymeleaf's own expression language can be used for [value to be output]
--Basically the same feeling as other expression languages (SpEL, EL expression, etc.)
hello.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<h1>[['hello world!!']]</h1>
</body>
</html>
Execution result
Description
--With [[[value to output]]]
, you can output the value directly on the template without using th: text
.
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("modelValue", "Model Value!!");
return "hello";
}
}
hello.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<h1>[[${modelValue}]]</h1>
</body>
</html>
Execution result
Description
hello.html
<h1>[[${modelValue}]]</h1>
--Use the expression $ {[variable name]}
to embed the value stored in the runtime context in the template.
HelloControlloer.java
public String hello(Model model) {
model.addAttribute("modelValue", "Model Value!!");
--Saving the value in the context is done by receiving the Model
class as a controller argument of Spring MVC and using the ʻaddAttribute ()` method.
<!--String-->
<span th:text="'some text'"></span>
<!--Numerical value-->
<span th:text="123"></span>
<!--Boolean value-->
<span th:text="true"></span>
<!-- null -->
<span th:text="null"></span>
<span th:text="'some text' + '!!'"></span>
--Strings can be concatenated with +
<span th:text="|Hello ${message}|"></span>
--You can embed variables in string literals by using $ {}
in string literals enclosed in |
.
<span th:text="(30 * 20 + 10 / 5 - 1) % 3"></span>
--*
, /
, +
,'-','%' can be used
--The meaning is the same as Java
<span th:text="true
and false
or true
and not true
or !false"></span>
--You can use ʻand or ʻor
--Can be denied with not
or!
<span th:text="1 < 10"></span>
<span th:text="1 > 10"></span>
<span th:text="1 <= 10"></span>
<span th:text="1 >= 10"></span>
<span th:text="1 == 10"></span>
<span th:text="1 != 10"></span>
--<
, >
, <=
, > =
can be used
--Each is the same as Java operator
--==
, ! =
Is replaced with a comparison using ʻequals () `(string comparison is also possible)
1: <span th:text="true ? 'a'"></span><br>
2: <span th:text="false ? 'b'"></span><br>
3: <span th:text="true ? 'c': 'C'"></span><br>
4: <span th:text="true ?: 'd'"></span><br>
5: <span th:text="false ?: 'e'"></span><br>
6: <span th:text="null ?: 'f'"></span><br>
** Output result **
** [Condition]? [Value]
**
-[Value] is evaluated when [Condition] is true
--Empty if false
** [Condition]? [Value 1]: [Value 2]
**
-If [Condition] is true
, [Value 1] is evaluated, and if it is false
, [Value 2] is evaluated.
** [Object]?: [Value]
**
-[Value] is evaluated when [Object] is null
--If it is other than null
, [object] is evaluated as it is.
The inside of $ {...}
is evaluated by the expression language called SpEL (Spring Expression Language).
With this SpEL, you can easily access objects.
Looking at the plain Thymeleaf reference, it says that OGNL is used as the expression language. However, when integrated with Spring, SpEL will be used instead.
2 The SpringStandard Dialect
Use Spring Expression Language (Spring EL or SpEL) as a variable expression language, instead of OGNL. Consequently, all ${...} and *{...} expressions will be evaluated by Spring’s Expression Language engine.
(Translation) Use Spring Expression Language (Spring EL or SpEL) as the expression language for variables instead of OGNL. Therefore, all
$ {...}
and* {...}
expressions are evaluated by Spring's expression language engine.
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("hoge", new Hoge());
return "hello";
}
public static class Hoge {
public int publicField = 1;
public int publicMethod() {return 2;}
public int getPublicValue() {return 3;}
}
}
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<div th:text="${hoge.publicField}"></div>
<div th:text="${hoge['publicField']}"></div>
<div th:text="${hoge.publicMethod()}"></div>
<div th:text="${hoge.publicValue}"></div>
<div th:text="${hoge['publicValue']}"></div>
</body>
</html>
Execution result
Description
--Direct reference is possible for fields and methods that are public
--The method getXxx ()
can be accessed as a property with ʻobjectName.xxx (the method must be
public). --Fields and properties can also be referenced in square brackets, such as ʻobjectName ['name']
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.HashMap;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
HashMap<String, String> map = new HashMap<>();
map.put("message", "Hello World!!");
model.addAttribute("map", map);
return "hello";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<div th:text="${map.message}"></div>
<div th:text="${map['message']}"></div>
</body>
</html>
Execution result
Description
--For Map
, you can refer to the value with map. Key
--You can also refer to it with square brackets like map ['key']
HelloConteroller.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Arrays;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("list", Arrays.asList(1, 2, 3));
return "hello";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<div th:text="${list[0]}"></div>
<div th:text="${list[1]}"></div>
<div th:text="${list[2]}"></div>
</body>
</html>
Execution result
Description
--To access List
and array elements, uselist [index]
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<span th:class="'hello' + 'world'">hoge</span>
</body>
</html>
Execution result
Description
--By describing the attribute in the form of th: attribute name
, the processing result of the expression can be set in the attribute.
--From v3.x? Seems to be able to use this method for arbitrary attributes
- 5.6 Setting the value of any attribute (default attribute processor)
application.properties
server.contextPath=/thymeleaf
--Set Spring Boot to start with context path to check operation
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<ul>
<li>
@{foo/bar} = [[@{foo/bar}]]
</li>
<li>
@{/foo/bar} = [[@{/foo/bar}]]
</li>
<li>
@{~/foo/bar} = [[@{~/foo/bar}]]
</li>
<li>
@{http://localhost:8080/foo/bar} = [[@{http://localhost:8080/foo/bar}]]
</li>
<li>
@{//localhost:8080/foo/bar} = [[@{//localhost:8080/foo/bar}]]
</li>
</ul>
</body>
</html>
Execution result
Description
--You can use the expression @ {...}
to construct a path in a format that complements the context path etc. well.
type | Description |
---|---|
foo/bar |
Normal relative path |
/foo/bar |
Relative path from context path |
~/foo/bar |
Relative path from the root of the server (/foo Becomes another context path) |
http://xxx/xxx |
Absolute path |
//xxx/xxx |
Protocol relative path |
Normally, it is specified in the href
attribute of the<a>
tag, so
<a th:href="@{/foo/bar}">/foo/bar</a>
It will be set as follows.
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("paramValue", "PARAM VALUE");
return "hello";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
[[@{/foo/bar(hoge='HOGE', paramValue=${paramValue})}]]
</body>
</html>
Execution result
Description
--To set query parameters in the path, write the key-value combination enclosed in parentheses (()
) at the end of the path.
--If you specify more than one, list them separated by commas.
--URL encoding is done automatically
hello.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
[[@{/foo/{pathValue}/bar(pathValue=123)}]]
</body>
</html>
Execution result
Description
--Variables declared with ()
at the end can be used in the path enclosed in {}
--In this case, the parameter is not set in the query parameter and is only used as part of the path
In addition to the value set in the controller Model
, there are objects that can be accessed by default.
<span th:text="${#httpServletRequest}"></span>
<span th:text="${#httpSession}"></span>
<span th:text="${param}"></span>
<span th:text="${session}"></span>
<span th:text="${application}"></span>
-- # httpServletRequest
is the HttpServletRequest
object itself
-- # httpSession
refers to the HttpSession
object itself
--param
is the object that holds the request parameters
--session
is the attribute information stored in HttpSession
--ʻApplicationis the attribute information stored in
ServletContext --
param,
session, ʻapplication
can be referenced like Map
--param ['parameterName']
etc.
#
--httpServletRequest
is headed with#
, not session
or param
--This difference depends on where the object is stored in the context
--If #
is not attached, its value is searched from the implicit variable # vars
.
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("hoge", new Hoge());
return "hello";
}
public static class Hoge {
public String name = "hogeee";
public String value = "HOGEEE";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<div th:object="${hoge}">
<div>name = [[*{name}]]</div>
<div>value = [[*{value}]]</div>
</div>
</body>
</html>
Execution result
Description
hello.html
<div th:object="${hoge}">
<div>name = [[*{name}]]</div>
<div>value = [[*{value}]]</div>
</div>
--If you specify an object with th: object
, you can directly refer to the fields and properties of that object in the form* {}
in the child elements of that tag.
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<div th:with="message = 'Hello World!!'">
message = [[${message}]]
</div>
</body>
</html>
Execution result
Description
hello.html
<div th:with="message = 'Hello World!!'">
message = [[${message}]]
</div>
--Variables declared in th: with
become valid only in the child elements of that tag and can be referenced in$ {}
expressions
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("flag", true);
return "hello";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<h1 th:if="${flag}">flag is true</h1>
<h1 th:if="${!flag}">flag is false</h1>
</body>
</html>
Execution result
Description
--If you use th: if =" [condition] "
, it will be displayed only when [condition] is ** a value that evaluates to true **.
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("booleanTrue", true);
model.addAttribute("booleanFalse", false);
model.addAttribute("numericZero", 0);
model.addAttribute("numericOne", 1);
model.addAttribute("charZero", '0');
model.addAttribute("stringEmpty", "");
model.addAttribute("stringZero", "0");
model.addAttribute("stringOff", "off");
model.addAttribute("stringNo", "no");
model.addAttribute("stringFalse", "false");
model.addAttribute("anyObject", new Object());
model.addAttribute("nullValue", null);
return "hello";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<ul>
<li th:if="${booleanTrue}">booleanTrue</li>
<li th:if="${booleanFalse}">booleanFalse</li>
<li th:if="${numericZero}">0</li>
<li th:if="${numericOne}">numericOne</li>
<li th:if="${charZero}">charZero</li>
<li th:if="${stringEmpty}">stringEmpty</li>
<li th:if="${stringZero}">stringZero</li>
<li th:if="${stringOff}">stringOff</li>
<li th:if="${stringNo}">stringNo</li>
<li th:if="${stringFalse}">stringFalse</li>
<li th:if="${anyObject}">anyObject</li>
<li th:if="${nullValue}">nullValue</li>
</ul>
</body>
</html>
Execution result
** Value evaluated as false </ font> **
--boolean`` false
--Numeric 0
null
--String " false "
, " off "
, " no "
** Value evaluated as true </ font> **
--Everything except "value evaluated as false"
--Example
-- true
of boolean
--Number 1
--String " 0 "
--Empty string
--Any object that is not null
Reading the reference, it says, "Values other than"0"
in the string are evaluated as true
. "
In other words, it seems that ""0"
of the character string is evaluated as false
", but when it is actually tried, it is evaluated as true
.
bug?
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Arrays;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("list", Arrays.asList("hoge", "fuga", "piyo"));
return "hello";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<ul>
<li th:each="element : ${list}">[[${element}]]</li>
</ul>
</body>
</html>
Execution result
Description
--th: each =" [Variable name to store each element]: $ {[Object to be iterated]} "
, the tag can be output repeatedly.
-[Variable name to store each element] becomes a variable that is valid only in repeated output
--If the class implements ʻIterable, it can be iteratively processed with
th: each`.
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.HashMap;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
HashMap<String, String> map = new HashMap<>();
map.put("hoge", "HOGE");
map.put("fuga", "FUGA");
map.put("piyo", "PIYO");
model.addAttribute("map", map);
return "hello";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<ul>
<li th:each="entry : ${map}">
key=[[${entry.key}]], value=[[${entry.value}]]
</li>
</ul>
</body>
</html>
Execution result
Description
--When Map
is iteratively processed, Map.Entry
is iteratively processed as each element.
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<ul>
<li th:each="element : 'text'">[[${element}]]</li>
</ul>
</body>
</html>
Execution result
Description
--If an object other than Map
that does not implement ʻIterable is iteratively processed with
th: each, it is treated as a
List` that has only that value.
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Arrays;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("list", Arrays.asList("hoge", "fuga", "piyo"));
return "hello";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<ul>
<li th:each="element, status : ${list}">
index = [[${status.index}]],
count = [[${status.count}]],
size = [[${status.size}]],
current = [[${status.current}]],
even = [[${status.even}]],
odd = [[${status.odd}]],
first = [[${status.first}]],
last = [[${status.last}]]
</li>
</ul>
</body>
</html>
Execution result
Description
--th: each =" [each element], [status variable]: [repeated target] "
, by adding , variable name
after the declaration of each element variable, the state of each loop can be changed. You will be able to use the retained variables
--If the declaration of the status variable is omitted, the status variable is prepared with the variable named [each element] Stat
.
--If you set th: each =" element: $ {list} "
, you can refer to the status variable with ʻelementStat`.
--Status contains information about the current loop state
--The information stored in the status is as follows
Variable name | meaning |
---|---|
index |
Current loop index (0 Start) |
count |
Current loop count (1 Start) |
size |
Size of object to be repeated |
current |
Current repeating element |
even |
Whether the current element is even (true / false value) |
odd |
Whether the current element is odd (true / false value) |
first |
Whether the current element is the first (true / false value) |
last |
Whether the current element is at the end (true / false value) |
th:block
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Arrays;
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("list", Arrays.asList("hoge", "fuga", "piyo"));
return "hello";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<th:block th:each="element : ${list}">
<h2>[[${element}]]</h2>
</th:block>
</body>
</html>
Execution result
Description
hello.html
<th:block th:each="element : ${list}">
<h2>[[${element}]]</h2>
</th:block>
--<th: block>
tags are completely erased after rendering
--Thymeleaf processing (th: if
, etc.) can be written, so there is no need to increase the<div>
tag just to write the processing.
Folder structure
`-src/main/
|-java/sample/thymeleaf/
| `-FragmentController.java
|
`-resources/templates/fragment/
|-fragment.html
`-embedded.html
FragmentController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class FragmentController {
@GetMapping("/fragment")
public String fragment() {
return "fragment/fragment";
}
}
embedded.html
<h2>embedded</h2>
fragment.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Fragment</title>
</head>
<body>
<h1>Hello Fragment</h1>
<div id="foo"
th:insert="fragment/embedded"></div>
</body>
</html>
Execution result
Description
Folder structure
`-src/main/
`-resources/templates/fragment/
|-fragment.html
`-embedded.html
fragment.html
<div id="foo"
th:insert="fragment/embedded"></div>
--Use th: insert
to embed other templates
--There was th: include
in v2.x, but it is deprecated in v3.x.
--Specify the template to embed in the value
--The value specified here applies the same logic as when resolving the template file from the return value of the controller class, so match it.
--You can also use an expression with the value of th: insert
(th: insert = "fragment / $ {name}"
)
embedded.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<span th:fragment="hoge">hoge</span>
<span th:fragment="fuga">fuga</span>
</body>
</html>
fragment.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Fragment</title>
</head>
<body>
<h1>Hello Fragment</h1>
<div id="foo"
th:insert="fragment/embedded :: fuga"></div>
</body>
</html>
Execution result
Description
embedded.html
<span th:fragment="hoge">hoge</span>
<span th:fragment="fuga">fuga</span>
--Specify the fragment name in the th: fragment
attribute on the embedding side
fragment.html
<div id="foo"
th:insert="fragment/embedded :: fuga"></div>
--By specifying [template name] :: [fragment name to embed]
in th: insert
, you can embed only the specified fragment of the specified template.
embedded.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<span id="hoge" th:fragment="hoge">
HOGE
</span>
</body>
</html>
fragment.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Fragment</title>
</head>
<body>
<h1>Hello Fragment</h1>
<div id="foo"
th:insert="fragment/embedded :: hoge"></div>
<div id="bar"
th:replace="fragment/embedded :: hoge"></div>
</body>
</html>
Execution result
Description
--In the case of th: insert
, the child of the tag to which the content (th: fragment
) of the tag of the embedding target (ʻembedded.html) is added to the tag on the embedding source (
fragment.html) side. Element) is embedded --In the case of
th: replace, the tag on the embedding source (
fragment.html) side is replaced with the tag itself (the tag itself with
th: fragment) of the embedding target (ʻembedded.html
).
embedded.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<span th:fragment="hoge(p1, p2)">
p1 = [[${p1}]], p2 = [[${p2}]]
</span>
</body>
</html>
fragment.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Fragment</title>
</head>
<body>
<h1>Hello Fragment</h1>
<div th:insert="fragment/embedded :: hoge('HOGE', 'FUGA')"></div>
</body>
</html>
Execution result
Description
embedded.html
<span th:fragment="hoge(p1, p2)">
p1 = [[${p1}]], p2 = [[${p2}]]
</span>
--After declaring the fragment name, you can declare the parameters that the fragment receives in the form of method arguments.
fragment.html
<div th:insert="fragment/embedded :: hoge('HOGE', 'FUGA')"></div>
--The embedding side can pass arguments after the fragment name like a method call
--The argument can also be specified by name, such as hoge (p1 ='HOGE', p2 ='FUGA')
Thymeleaf Layout Dialect Fragments were in the form of embedding common parts in individual pages. This time, on the contrary, embed the own page in the common layout (header and footer).
This process itself cannot be realized by the standard function alone, and it is necessary to include an extension called thymeleaf-layout-dialect
.
build.gradle
ext['thymeleaf.version'] = '3.0.7.RELEASE'
ext['thymeleaf-layout-dialect.version'] = '2.2.2'
This 'thymeleaf-layout-dialect.version'
is sore.
Folder structure
`-src/main/
|-java/sample/thymeleaf/web/
| `-LayoutController.java
|
`-resources/templates/layout/
|-layout.html
`-content.html
LayoutController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LayoutController {
@GetMapping("/layout")
public String method() {
return "layout/content";
}
}
layout.html
<!doctype html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8" />
<title>Layout File</title>
</head>
<body>
<h1>Common Layout</h1>
<section layout:fragment="content">
</section>
</body>
</html>
content.html
<!doctype html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<body>
<section layout:fragment="content">
Content
</section>
</body>
</html>
Execution result
Description
LayoutController.java
@GetMapping("/layout")
public String method() {
return "layout/content";
}
--Specify as a template for content.html
layout.html
<!doctype html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8" />
<title>Layout File</title>
</head>
<body>
<h1>Common Layout</h1>
<section layout:fragment="content">
</section>
</body>
</html>
--Declare xmlns: layout =" http://www.ultraq.net.nz/thymeleaf/layout "
as namespace
--Define the location to embed the information of each page with layout: fragment
content.html
<!doctype html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<body>
<section layout:fragment="content">
Content
</section>
</body>
</html>
--Specify the template whose layout is defined by layout: decorate
--Use the fragment expression added in v3.x for the value
--Fragment expression is described in the format ~ {}
, and the position of the template is specified.
--The format of the fragment expression is almost the same as the one specified by th: insert
.
--For details, see Official Document
--By the way, it works even if you don't use the fragment expression ~ {}
, but a warning message is output to the console.
--According to the message, it will not be usable in the future
layout.html
<!doctype html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8" />
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">Layout</title>
</head>
<body>
<h1>Common Layout</h1>
<section layout:fragment="content">
</section>
</body>
</html>
content.html
<!doctype html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<head>
<title>Content</title>
</head>
<body>
<section layout:fragment="content">
Content
</section>
</body>
</html>
Execution result
Description
layout.html
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">Layout</title>
--The contents of <title>
can be a combination of those in the layout file and those in the content file.
--Set the layout: title-pattern
attribute to<title>
on the layout file side, and define the shape of the combined<title>
in the value.
-- $ LAYOUT_TITLE
refers to<title>
in the layout file, $ CONTENT_TITLE
refers to <title>
on the content file side.
content.html
<head>
<title>Content</title>
</head>
--The content file side just declares <title>
normally
Generated HTML title
<title>Layout - Content</title>
--The value is a combination of <title>
in the layout file and <title>
in the content file.
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<!-- standard html comment -->
<!--/* -->
<h1 th:text="'parser level comment'"></h1>
<!-- */-->
<!--/*/
<h1 th:text="'prototype comment'"></h1>
/*/-->
</body>
</html>
Execution result
Description
--There are three types of Thymeleaf comments
hello.html
<!-- standard html comment -->
--This will be a normal HTML comment --It is output as it is after rendering, and it is treated as a comment on HTML.
hello.html
<!--/* -->
<h1 th:text="'parser level comment'"></h1>
<!-- */-->
--Starting with <!-/ *
And ending with * /->
--This comment is completely removed at render time and no longer exists in HTML
--You can also enclose it with <!-/ *->
And <!-* /->
, So it will be displayed when you open it in a browser, but it will disappear when you run it on the server. Can be realized
hello.html
<!--/*/
<h1 th:text="'prototype comment'"></h1>
/*/-->
--The range from <!-/ * /
To / * /->
is applicable.
--When opened directly in the browser, it is treated as a simple HTML comment, but Thymeleaf rendering is targeted.
--It is treated as a comment as a prototype, but it is used when you want Thymeleaf to process it when you actually run it on the server.
Folder structure
`-src/main/
|-java/sample/thymeleaf/
| |-web/
| | `-HelloController.java
| `-Main.java
|
`-resources/
|-messages/
| `-Messages_ja.properties
`-templates/
`-hello.html
Main.java
package sample.thymeleaf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ResourceBundleMessageSource;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages/Messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
Messages_ja.properties
foo.message=Good morning world
bar.message=good-bye world
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<h1 th:text="#{foo.message}"></h1>
<h1 th:text="#{bar.message}"></h1>
</body>
</html>
Execution result
Description
Main.java
import org.springframework.context.support.ResourceBundleMessageSource;
...
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages/Messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
--Specifying the resource bundle file handled by Spring --Since the encoding is set to ʻUTF-8`, you can write Japanese as it is in the message file.
hello.html
<h1 th:text="#{foo.message}"></h1>
<h1 th:text="#{bar.message}"></h1>
--Use the expression # {}
to refer to the message from the Thymeleaf template.
--Specify the key of the message you want to display inside
Messages_ja.properties
foo.message=Good morning{0}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<h1 th:text="#{foo.message('World')}"></h1>
<h1 th:text="#{foo.message('world')}"></h1>
</body>
</html>
Execution result
Description
hello.html
<h1 th:text="#{foo.message('World')}"></h1>
<h1 th:text="#{foo.message('world')}"></h1>
--If the message has placeholders, you can pass parameters like # {key name (parameter ...)}
, like a method argument.
MySpringBean.java
package sample.thymeleaf;
import org.springframework.stereotype.Component;
@Component
public class MySpringBean {
public String hello() {
return "Hello MySpringBean!!";
}
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Hello Thymeleaf</title>
</head>
<body>
<h1 th:text="${@mySpringBean.hello()}"></h1>
</body>
</html>
Execution result
Description
--$ {}
is evaluated as a SpEL expression, so you can also refer to Spring beans.
--To refer to Spring Bean, use @bean name
MyForm.java
package sample.thymeleaf.web;
public class MyForm {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
--Getter and Setter are required because the Form class defaults to property access.
FormController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/form")
public class FormController {
@GetMapping
public String init(Model model) {
model.addAttribute(new MyForm());
return "form";
}
@PostMapping
public String submit(MyForm form) {
System.out.println("form.value=" + form.getValue());
return "form";
}
}
--Register the object of MyForm
withModel.addAttribute ()
at the initial display
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<input type="text" th:field="*{value}" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Enter the appropriate characters and click the Submit button.
Server console output
form.value=test
Description
form.html
<form th:action="@{/form}" method="post" th:object="${myForm}">
<input type="text" th:field="*{value}" />
<input type="submit" value="Submit" />
</form>
--To associate the Java side Form object (MyForm
) with the HTML<form>
tag, specify the Form object registered in the controller with the th: object
tag.
-(It worked even if I didn't use th: object
, but the documentation said it was required ...)
--Use th: field
attribute to map each input item to Form property
MyForm.java
package sample.thymeleaf.web;
public class MyForm {
private boolean checked;
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
}
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<label th:for="${#ids.next('checked')}">checked</label>
<input type="checkbox" th:field="*{checked}" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Description
--When using th: field
, the ʻidattribute is automatically assigned by Thymeleaf. --It becomes difficult to specify
for, especially if you are creating multiple checkboxes while looping. --To support this, by specifying
$ {# ids.next ('[name of the property you want to associate]')}with
th: for, you can set the
for attribute value to the target property. ʻId
attribute value is set
--Use $ {# ids.prev ()}
if the checkbox item precedes <label>
MyForm.java
package sample.thymeleaf.web;
import java.util.LinkedHashMap;
import java.util.Map;
public class MyForm {
private String selectedValue = "piyo";
public Map<String, String> radioButtons() {
Map<String, String> radioButtons = new LinkedHashMap<>();
radioButtons.put("hoge", "HOGE");
radioButtons.put("fuga", "FUGA");
radioButtons.put("piyo", "PIYO");
return radioButtons;
}
public String getSelectedValue() {
return selectedValue;
}
public void setSelectedValue(String selectedValue) {
this.selectedValue = selectedValue;
}
}
FormController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/form")
public class FormController {
@GetMapping
public String init(Model model) {
model.addAttribute(new MyForm());
return "form";
}
@PostMapping
public String submit(MyForm form) {
System.out.println("form.selectedValue=" + form.getSelectedValue());
return "form";
}
}
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<div th:each="radioButton : *{radioButtons()}">
<label th:for="${#ids.next('selectedValue')}" th:text="${radioButton.value}"></label>
<input type="radio" th:field="*{selectedValue}" th:value="${radioButton.key}" />
</div>
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Description
MyForm.java
private String selectedValue = "piyo";
public Map<String, String> radioButtons() {
Map<String, String> radioButtons = new LinkedHashMap<>();
radioButtons.put("hoge", "HOGE");
radioButtons.put("fuga", "FUGA");
radioButtons.put("piyo", "PIYO");
return radioButtons;
}
form.html
<div th:each="radioButton : *{radioButtons()}">
<label th:for="${#ids.next('selectedValue')}" th:text="${radioButton.value}"></label>
<input type="radio" th:field="*{selectedValue}" th:value="${radioButton.key}" />
</div>
--Prepare the following two to map the radio button and Form.
--Information for building radio buttons (raidoButtons ()
)
――This time, I used LinkedHashMap
to make it quickly, but in reality, I think that I will prepare a dedicated container class to store labels and values and pack it in List
.
--Property to store the selected value (selectedValue
)
--Construct radio buttons while turning the result of radioButtons ()
with th: each
--When mapping a radio button to a Form, it is mandatory to specify not only th: field
but also th: value
.
--Because you need the value when that item is selected
--If you want to select the initial value, set the selected value in the corresponding property (selectedValue =" piyo "
)
--If you do not set a value, all items will be unselected.
MyForm.java
package sample.thymeleaf.web;
import java.util.LinkedHashMap;
import java.util.Map;
public class MyForm {
private String selectedValue;
public Map<String, String> options() {
Map<String, String> radioButtons = new LinkedHashMap<>();
radioButtons.put("hoge", "HOGE");
radioButtons.put("fuga", "FUGA");
radioButtons.put("piyo", "PIYO");
return radioButtons;
}
public String getSelectedValue() {
return selectedValue;
}
public void setSelectedValue(String selectedValue) {
this.selectedValue = selectedValue;
}
}
FormController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/form")
public class FormController {
@GetMapping
public String init(Model model) {
model.addAttribute(new MyForm());
return "form";
}
@PostMapping
public String submit(MyForm form) {
System.out.println("form.selectedValue=" + form.getSelectedValue());
return "form";
}
}
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<select th:field="*{selectedValue}">
<option th:each="option : *{options()}"
th:value="${option.key}"
th:text="${option.value}">
</option>
</select>
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Description
--To dynamically generate a drop-down list, loop <option>
with th: each
-- th: field
is written in the <select>
tag
--th: value
must be specified for<option>
How to create input items that can dynamically add / delete lines
MyForm.java
package sample.thymeleaf.web;
import java.util.ArrayList;
import java.util.List;
public class MyForm {
private List<Row> rows = new ArrayList<>();
public void appendRow() {
this.rows.add(new Row());
}
public void removeRow(int index) {
this.rows.remove(index);
}
public List<Row> getRows() {
return rows;
}
public void setRows(List<Row> rows) {
this.rows = rows;
}
public static class Row {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
FormController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
@RequestMapping("/form")
public class FormController {
@GetMapping
public String init(Model model) {
model.addAttribute(new MyForm());
return "form";
}
@PostMapping(params="appendRow")
public String appendRow(MyForm form) {
form.appendRow();
this.printRows(form);
return "form";
}
@PostMapping(params="removeIndex")
public String submit(MyForm form, @RequestParam int removeIndex) {
form.removeRow(removeIndex);
this.printRows(form);
return "form";
}
private void printRows(MyForm form) {
List<MyForm.Row> rows = form.getRows();
for (int i = 0; i < rows.size(); i++) {
MyForm.Row row = rows.get(i);
System.out.println("i=" + i + ", row.value=" + row.getValue());
}
}
}
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<table border="1">
<tr>
<th>No</th>
<th>User input</th>
<th>Remove Row</th>
</tr>
<tr th:each="row, loop : *{rows}">
<th th:text="${loop.count}"></th>
<th>
<input type="text" th:field="*{rows[__${loop.index}__].value}" />
</th>
<th>
<button type="submit" name="removeIndex" th:value="${loop.index}">
Remove
</button>
</th>
</tr>
</table>
<input type="submit" name="appendRow" value="Append Row" />
</form>
</body>
</html>
Execution result
Description
form.html
<tr th:each="row, loop : *{rows}">
<th th:text="${loop.count}"></th>
<th>
<input type="text" th:field="*{rows[__${loop.index}__].value}" />
</th>
<th>
<button type="submit" name="removeIndex" th:value="${loop.index}">
Remove
</button>
</th>
</tr>
--If you want to associate fields that increase or decrease dynamically, you need to devise a little to specify th: field
.
--Simply $ {row.value}
will result in an error
--Each element needs to be accessed by indexing rows
--Furthermore, index specification uses the ** preprocessing ** mechanism.
--Pre-processing is a mechanism that allows you to evaluate before a normal expression, and it is described by enclosing it in __
.
--__ $ {loop.index} __
part
--This allows * {rows [__ $ {loop.index} __] .value}
to be evaluated as * {rows [0] .value}
and then further evaluated inside * {}
. Become so
--It seems to work as simply * {rows [loop.index] .value}
, but unfortunately it doesn't work.
--This is due to the fact that SpEL expressions cannot be specified in parentheses that specify the index of the list.
FormController.java
@PostMapping(params="appendRow")
public String appendRow(MyForm form) {
form.appendRow();
this.printRows(form);
return "form";
}
@PostMapping(params="removeIndex")
public String submit(MyForm form, @RequestParam int removeIndex) {
form.removeRow(removeIndex);
this.printRows(form);
return "form";
}
―― To identify whether the Append Rowbutton was clicked or the
Remove button was clicked, specify the
nameattribute for the button and switch the method call of the controller with or without the parameter. Are --You can control "execute if there is a parameter" with
params of annotations for mapping (
@PostMapping,
@ GetMapping`, etc.)
Spring MVC provides an input checking mechanism using Bean Validation. Thymeleaf provides a mechanism for checking this error result and displaying an error message.
MyForm.java
package sample.thymeleaf.web;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
public class MyForm {
@Size(min=3)
private String text;
@Min(100)
private Integer number;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
}
FormController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/form")
public class FormController {
@GetMapping
public String init(Model model) {
model.addAttribute(new MyForm());
return "form";
}
@PostMapping
public String submit(@Validated MyForm form, BindingResult result) {
System.out.println("********************************************************");
System.out.println("form = " + form);
System.out.println("result = " + result);
System.out.println("********************************************************");
return "form";
}
}
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<div>
<input type="text" th:field="*{text}" />
<ul th:each="error : ${#fields.errors('text')}">
<li th:text="${error}"></li>
</ul>
</div>
<div>
<input type="text" th:field="*{number}" />
<ul th:each="error : ${#fields.errors('number')}">
<li th:text="${error}"></li>
</ul>
</div>
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Description
MyForm.java
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
...
@Size(min=3)
private String text;
@Min(100)
private Integer number;
--Annotate Form fields with Bean Validation annotations and define check contents --Refer to here for how to use Bean Validation.
FormController.java
import org.springframework.validation.annotation.Validated;
...
@PostMapping
public String submit(@Validated MyForm form, BindingResult result) {
--Input checking is enabled by annotating the form object with @Validated
in the argument of the controller method.
form.html
<div>
<input type="text" th:field="*{text}" />
<ul th:each="error : ${#fields.errors('text')}">
<li th:text="${error}"></li>
</ul>
</div>
<div>
<input type="text" th:field="*{number}" />
<ul th:each="error : ${#fields.errors('number')}">
<li th:text="${error}"></li>
</ul>
</div>
--Error messages generated by each property can be accessed with $ {# fields.errors ('[property name]')}
--The results are listed, so you can get each error message by looping around.
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<div>
<input type="text" th:field="*{text}" />
<span th:if="${#fields.hasErrors('text')}">It's an error!</span>
</div>
<div>
<input type="text" th:field="*{number}" />
<span th:if="${#fields.hasErrors('number')}">It's an error!</span>
</div>
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Description
--You can check if there are any errors in the property with $ {# fields.hasErrors ('[property name]')}
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<div>
<input type="text" th:field="*{text}" class="always-assigned" th:errorclass="error-style" />
</div>
<div>
<input type="text" th:field="*{number}" class="always-assigned" th:errorclass="error-style" />
</div>
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Description
--If you specify the th: errorclass
attribute, the specified class attribute will be added only when an error occurs in that property.
--If you want to apply an error style, it's simpler and easier to understand than doing something like $ {# fields.hasErrors ('text')?'Error-style'}
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<h3 th:if="${#fields.hasAnyErrors()}">There is an error</h3>
<div>
<input type="text" th:field="*{text}" />
</div>
<div>
<input type="text" th:field="*{number}" />
</div>
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Description
form.html
<h3 th:if="${#fields.hasAnyErrors()}">There is an error</h3>
--You can check if there is at least one error with # fields.hasAnyErrors ()
--You can get the same result with # fields.hasErrors ('*')
or # fields.hasErrors ('all')
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<ul>
<li th:each="error: ${#fields.allErrors()}">
[[${error}]]
</li>
</ul>
<div>
<input type="text" th:field="*{text}" />
</div>
<div>
<input type="text" th:field="*{number}" />
</div>
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Description
--You can get all error messages with # fields.allErrors ()
--This works the same if you use # fields.errors ('*')
or # fields.errors ('all')
.
Errors that are not tied to a particular property are called global errors (probably).
Create your own validation to perform correlation check and generate a global error.
MyValidation.java
package sample.thymeleaf.validation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Constraint(validatedBy = MyValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyValidation {
String message() default "It's an error!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
MyValidator.java
package sample.thymeleaf.validation;
import sample.thymeleaf.web.MyForm;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class MyValidator implements ConstraintValidator<MyValidation, MyForm> {
@Override
public void initialize(MyValidation constraintAnnotation) {
System.out.println("MyValidator initialize");
}
@Override
public boolean isValid(MyForm value, ConstraintValidatorContext context) {
System.out.println("MyValidator isValid");
if (value == null) {
return true;
}
Integer number = value.getNumber();
if (number == null) {
return true;
}
String text = value.getText();
if (number == 500) {
return "500".equals(text);
}
return true;
}
}
--Validator to check that text
is also"500"
if number
is 500
--For information on how to create your own Bali data [here](http://qiita.com/opengl-8080/items/3926fbde5469c0b330c2#%E8%87%AA%E4%BD%9C%E3%81%AE%E3% 83% 90% E3% 83% AA% E3% 83% 87% E3% 83% BC% E3% 82% BF% E3% 81% A8% E5% 88% B6% E7% B4% 84% E3% 82% A2% E3% 83% 8E% E3% 83% 86% E3% 83% BC% E3% 82% B7% E3% 83% A7% E3% 83% B3% E3% 82% 92% E4% BD% 9C% See E6% 88% 90% E3% 81% 99% E3% 82% 8B)
MyForm.java
package sample.thymeleaf.web;
import sample.thymeleaf.validation.MyValidation;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
@MyValidation
public class MyForm {
@Size(min=3)
private String text;
@Min(100)
private Integer number;
...
}
--Annotate MyForm
with @MyValidation
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<h3>[[${#fields.hasGlobalErrors()}]]</h3>
<div>
<input type="text" th:field="*{text}" />
</div>
<div>
<input type="text" th:field="*{number}" />
</div>
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Set number
to something other than 500
to make an error
Change number
to 500
to make an error
Description
form.html
<h3>[[${#fields.hasGlobalErrors()}]]</h3>
--You can check if there are global errors with # fields.hasGlobalErrors ()
--This is the same result with # fields.hasErrors ('global')
form.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Form Sample</title>
</head>
<body>
<form th:action="@{/form}" method="post" th:object="${myForm}">
<h3>[[${#fields.globalErrors()}]]</h3>
<div>
<input type="text" th:field="*{text}" />
</div>
<div>
<input type="text" th:field="*{number}" />
</div>
<input type="submit" value="Submit" />
</form>
</body>
</html>
Execution result
Description
--You can get all global error messages with # fields.globalErrors ()
--This gives the same result with # fields.errors ('global')
Main.java
package sample.thymeleaf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ResourceBundleMessageSource;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.addBasenames("messages/Messages", "messages/validation-messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
MyWebConfig.java
package sample.thymeleaf;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class MyWebConfig extends WebMvcConfigurerAdapter {
private MessageSource messageSource;
public MyWebConfig(MessageSource messageSource) {
this.messageSource = messageSource;
}
@Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource);
return validator;
}
}
src/main/resources/messages/validation-messages_ja.properties
javax.validation.constraints.Min.message = {value}Please enter above
Execution result
Description
--Due to Bean Validation specifications, you can overwrite the default message by placing a property file with ValidationMessages
as the base name directly under the classpath.
--However, it is a little inconvenient because you have to encode the property file with native2ascii.
--If you use Spring's MessageSource
mechanism, you can write messages in UTF-8, which is convenient.
Main.java
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.addBasenames("messages/Messages", "messages/validation-messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
--Added message file for Bean Validation with ʻaddBasenames () `
validation-messages_ja.properties
javax.validation.constraints.Min.message = {value}Please enter above
--Check the default message file to find out what the key is
--Hibernate Validator is included in the dependency, so you can see the list of default messages by looking at ValidationMessages.properties
in that jar.
ValidationMessages.properties
javax.validation.constraints.AssertFalse.message = must be false
javax.validation.constraints.AssertTrue.message = must be true
javax.validation.constraints.DecimalMax.message = must be less than ${inclusive == true ? 'or equal to ' : ''}{value}
javax.validation.constraints.DecimalMin.message = must be greater than ${inclusive == true ? 'or equal to ' : ''}{value}
javax.validation.constraints.Digits.message = numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
javax.validation.constraints.Future.message = must be in the future
javax.validation.constraints.Max.message = must be less than or equal to {value}
javax.validation.constraints.Min.message = must be greater than or equal to {value}
javax.validation.constraints.NotNull.message = may not be null
javax.validation.constraints.Null.message = must be null
javax.validation.constraints.Past.message = must be in the past
javax.validation.constraints.Pattern.message = must match "{regexp}"
javax.validation.constraints.Size.message = size must be between {min} and {max}
org.hibernate.validator.constraints.CreditCardNumber.message = invalid credit card number
org.hibernate.validator.constraints.EAN.message = invalid {type} barcode
org.hibernate.validator.constraints.Email.message = not a well-formed email address
org.hibernate.validator.constraints.Length.message = length must be between {min} and {max}
org.hibernate.validator.constraints.LuhnCheck.message = The check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed
org.hibernate.validator.constraints.Mod10Check.message = The check digit for ${validatedValue} is invalid, Modulo 10 checksum failed
org.hibernate.validator.constraints.Mod11Check.message = The check digit for ${validatedValue} is invalid, Modulo 11 checksum failed
org.hibernate.validator.constraints.ModCheck.message = The check digit for ${validatedValue} is invalid, ${modType} checksum failed
org.hibernate.validator.constraints.NotBlank.message = may not be empty
org.hibernate.validator.constraints.NotEmpty.message = may not be empty
org.hibernate.validator.constraints.ParametersScriptAssert.message = script expression "{script}" didn't evaluate to true
org.hibernate.validator.constraints.Range.message = must be between {min} and {max}
org.hibernate.validator.constraints.SafeHtml.message = may have unsafe html content
org.hibernate.validator.constraints.ScriptAssert.message = script expression "{script}" didn't evaluate to true
org.hibernate.validator.constraints.URL.message = must be a valid URL
org.hibernate.validator.constraints.br.CNPJ.message = invalid Brazilian corporate taxpayer registry number (CNPJ)
org.hibernate.validator.constraints.br.CPF.message = invalid Brazilian individual taxpayer registry number (CPF)
org.hibernate.validator.constraints.br.TituloEleitoral.message = invalid Brazilian Voter ID card number
Recommended Posts