文章目录
一、简介
我们将在这一节学习 Thymeleaf 模板引擎技术,首先会介绍 Thymeleaf 的基本概念,然后再详细介绍 Thymeleaf 模板引擎的属性和表达式语法,并结合实际的代码进行页面功能开发。
首先,我们得有一个spring boot项目,通过spring initializer创建一个项目,选择web依赖,可以也选择thymeleaf依赖,当然也可以在pom中手动添加依赖:
<!-- Thymeleaf 模板引擎依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
实例1:thymeleaf初步展示
我们在template下创建一个thymeleaf.html文件,用来初步展示thymeleaf:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf demo</title>
</head>
<body>
<p>description字段值为:</p>
<p th:text="${description}">这里显示的是 description 字段内容</p>
</body>
</html>
当然,controller也是得写的,我们在controller包下创建ThymeleafController类:
@Controller
public class ThymeleafController {
@GetMapping("/thymeleaf")
public String hello(HttpServletRequest request,@RequestParam(value = "description", required = false,
defaultValue = "springboot-thymeleaf") String description){
request.setAttribute("description", description);
return "thymeleaf";
}
}
启动项目之后,就可以在浏览器中访问服务了:
二、语法详解
1.属性
th:text 对应的是 HTML5 中的 text 属性,除 th:text 属性外,Thymeleaf 也提供了其他的标签属性来替换 HTML5 原生属性的值,属性节选如下:
- th:background 对应 HTML5 中的背景属性
- th:class 对应 HTML5 中的 class 属性
- th:href 对应 HTML5 中的链接地址属性
- th:id 和 th:name 分别对应 HTML5 中的 id 和 name 属性…
- th:block 比较特殊,它是 Thymeleaf 提供的唯一的一个 Thymeleaf 块级元素,其特殊性在于 Thymeleaf 模板引擎在处理th:block的时候会删掉它本身,而保留其内容。
这里只列举了部分属性,完整内容可以查看 thymeleaf-attributes。
实例2:属性
在templates目录下创建attributes.html ,用于展示thymeleaf中属性与html属性语法的不同,其中生效的是thymeleaf语法,html原本的属性代码仅做对照和参考:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf setting-value-to-specific-attributes</title>
<meta charset="UTF-8" />
</head>
<!-- background 标签-->
<body th:background="${th_bgcolor}" background="#B0C4DE">
<!-- text 标签-->
<h1 th:text="${title}">html标签演示</h1>
<div>
<h5>id、name、value标签:</h5>
<!-- id、name、value标签-->
<input
id="input1"
name="input1"
value="1"
th:id="${th_id}"
th:name="${th_name}"
th:value="${th_value}"
/>
</div>
<br />
<div class="div1" th:class="${th_class}">
<h5>class、href标签:</h5>
<!-- class、href标签-->
<a th:href="${th_href}" href="##/">链接地址</a>
</div>
</body>
</html>
controller中对应方法如下:
@GetMapping("/attributes")
public String attributes(ModelMap map) {
//更改background
map.put("th_bgcolor","#B0C4DE");
// 更改 h1 内容
map.put("title", "Thymeleaf 标签演示");
// 更改 id、name、value
map.put("th_id", "thymeleaf-input");
map.put("th_name", "thymeleaf-input");
map.put("th_value", "13");
// 更改 class、href
map.put("th_class", "thymeleaf-class");
map.put("th_href", "http://www.hao123.com");
return "attributes";
}
效果:
2.语法规则
-
表达式语法
变量表达式: ${…}
选择变量表达式: *{…}
信息表达式: #{…}
链接 URL 表达式: @{…}
分段表达式: ~{…} -
字面量
字符串: ‘one text’, ‘Another one!’ …
数字: 0, 34, 3.0, 12.3 …
布尔值: true, false
Null 值: null
字面量标记:one, sometext, main … -
文本运算
字符串拼接: +
字面量置换: |The name is ${name}| -
算术运算
二元运算符: +, -, *, /, %
负号(一元运算符): (unary operator): - -
布尔运算
二元运算符: and, or
布尔非(一元运算符): !, not -
比较运算
比较: >, <, >=, <= (gt, lt, ge, le)
相等运算符: ==, != (eq, ne)
比较运算符也可以使用转义字符,比如大于号,可以使用 Thymeleaf 语法 gt 也可以使用转义字符> -
条件运算符
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue) -
特殊语法
无操作: _
实例3:字面量、运算
新建 simple.html 模板页面,该案例主要介绍字面量及简单的运算操作,包括字符串、数字、布尔值等常用的字面量及常用的运算和拼接操作,代码如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf simple syntax</title>
<meta charset="UTF-8" />
</head>
<body>
<h1>Thymeleaf简单语法</h1>
<div>
<h5>基本类型操作(字符串):</h5>
<p>
一个简单的字符串:<span th:text="'thymeleaf text'">default text</span>.
</p>
<p>
字符串连接:<span th:text="'thymeleaf text concat, '+${thymeleafText}"
>default text</span
>.
</p>
<p>
字符串连接:<span th:text="|thymeleaf text concat,${thymeleafText}|"
>default text</span
>.
</p>
</div>
<div>
<h5>基本类型操作(数字):</h5>
<p>一个简单的神奇的数字:<span th:text="2019">1000</span>.</p>
<p>算术运算: 2021+1=<span th:text="${number1}+1">0</span>.</p>
<p>算术运算: 14-1=<span th:text="14-1">0</span>.</p>
<p>算术运算: 673 * 3=<span th:text="673*${number2}">0</span>.</p>
<p>算术运算: 39 ÷ 3=<span th:text="39/3">0</span>.</p>
</div>
<div>
<h5>基本类型操作(布尔值):</h5>
<p>
一个简单的数字比较:2019 > 2018=<span th:text="2019>2018"> </span>.
</p>
<p>
字符串比较:兔兔 == '兔兔',结果为<span
th:text="${thymeleafText} == '兔兔'"
>0</span
>.
</p>
<p>数字比较:13 == 39/3 结果为: <span th:text="13 == 39/3">0</span>.</p>
</div>
</body>
</html>
对应的controller方法:
@GetMapping("/simple")
public String simple(ModelMap map) {
map.put("thymeleafText", "兔兔");
map.put("number1", 2021);
map.put("number2", 3);
return "simple";
}
效果:
实例4:表达式语法
- 变量表达式:
变量表达式即 OGNL 表达式或 Spring EL 表达式,作用是获取模板中与后端返回数据所绑定对象的值,写法为 ${…},这是最常见的一个表达式,在取值赋值、逻辑判断、循环语句中都可以使用该表达式,示例如下:
<!-- 读取参数 -->
<p>算术运算: 2018+1=<span th:text="${number1}+1">0</span>.</p>
<!-- 读取参数并运算 -->
<div th:class="${path}=='links'?'nav-link active':'nav-link'"></div>
<!-- 读取对象中的属性 -->
<p>
读取blog对象中title字段:<span th:text="${blog.blogTitle}">default text</span
>.
</p>
<!-- 循环遍历 -->
<li th:each="blog : ${blogs}"></li>
变量表达式也可以使用内置的基本对象:
ctx : the context object.
vars : the context variables.
locale : the context locale.
request : web 环境下的 HttpServletRequest 对象.
response :web 环境下的 HttpServletResponse 对象 .
session : web 环境下的 HttpSession 对象.
servletContext : web 环境下的 ServletContext 对象.
示例如下:
<p>
读取内置对象中 request 中的内容:<span
th:text="${#request.getAttribute('requestObject')}"
>default text</span
>.
</p>
<p>
读取内置对象中 session 中的内容:<span
th:text="${#session.getAttribute('sessionObject')}"
>default text</span
>.
</p>
同时,Thymeleaf 还提供了一系列 Utility 工具对象(内置于 Context 中),可以通过 # 直接访问,工具类如下:
dates : java.util.Date 的功能方法类。
calendars : 类似 #dates,面向 java.util.Calendar
numbers : 格式化数字的工具方法类
strings : 字符串对象的工具方法类,contains,startWiths,prepending/appending 等等。
bools:对布尔值求值的工具方法。
arrays:对数组的工具方法。
lists:对 java.util.List 的工具方法
sets:对 java.util.Set 的工具方法
maps:对 java.util.Map 的工具方法
你可以将这些方法视为工具类,通过这些方法可以使得 Thymeleaf 在操作变量时更加方便。
- 选择(星号)表达式:
选择表达式与变量表达式类似,不过它会用一个预先选择的对象来代替上下文变量容器(map)来执行,语法如下: *{blog.blogId},被指定的对象由 th:object 标签属性进行定义,前文中读取 blog 对象的 title 字段可以替换为:
<p th:object="${blog}">
读取blog对象中title字段:<span th:text="*{blogTitle}">text</span>.
</p>
copy
如果不考虑上下文的情况下,两者没有区别,使用 ${
...}读取的内容也完全可以替换为使用*{
...}进行读取,唯一的区别是使用*{
...}前可以预先在父标签中通过 th:object 定义一个对象并进行操作。
<p>
读取blog对象中title字段:<span th:text="*{blog.blogTitle}">default text</span>
</p>
<p>读取text字段:<span th:text="*{text}">default text</span>.</p>
- URL 表达式:
th:href 对应的是 html 中的 href 标签,它将计算并替换 href 标签中的链接 URL 地址,th:href 中可以直接设置为静态地址,也可以使用表达式语法读取到的变量值进行动态拼接 URL 地址。
比如一个详情页 URL 地址:http://localhost:8080/blog/1,当使用 URL 表达式时,可以写成这样:
<a th:href="@{'http://localhost:8080/blog/1'}">详情页</a>
也可以根据 id 值进行替换,写法为:
<a th:href="@{'/blog/'+${blog.blogId}}">详情页</a>
或者也可以写成这样:
<a th:href="@{/blog/{blogId}(blogId=${blog.blogId})">详情页</a>
以上三种表达式写法生成 URL 的最终结果都是相同的,开发者可以自己使用字符串拼接的方法组装 URL (第二种写法),也可以使用 URL 表达式提供的语法进行 URL 组装(第三种写法)。如果有多个参数可以自行拼装字符串,或者使用逗号进行分隔,写法如下:
<a th:href="@{/blog/{blogId}(blogId=${blog.blogId},title=${blog.blogTitle},tag='java')}">详情页</a>
最终生成的 URL 为 http://localhost:8080/blog/1?title=lou-springboot&tag=java 另外,URL 中以 “/” 开头的路径(比如 /blog/1 ),默认生成的 URL 会加上项目的当前地址形成完整的 URL 。
实例4:复杂语法
test.html代码:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title th:text="${title}">语法测试</title>
</head>
<body>
<h3>#strings 工具类测试</h3>
<div th:if="${not #strings.isEmpty(testString)}">
<p>testString初始值 : <span th:text="${testString}" /></p>
<p>
toUpperCase : <span th:text="${#strings.toUpperCase(testString)}" />
</p>
<p>
toLowerCase : <span th:text="${#strings.toLowerCase(testString)}" />
</p>
<p>
equalsIgnoreCase:
<span th:text="${#strings.equalsIgnoreCase(testString, '团团')}" />
</p>
<p>indexOf : <span th:text="${#strings.indexOf(testString, 'r')}" /></p>
<p>
substring : <span th:text="${#strings.substring(testString, 5, 9)}" />
</p>
<p>
startsWith :
<span th:text="${#strings.startsWith(testString, 'Spring')}" />
</p>
<p>
contains : <span th:text="${#strings.contains(testString, 'Boot')}" />
</p>
</div>
<h3>#bools 工具类测试</h3>
<!-- 如果 bool 的值为false的话,该div将不会显示-->
<div th:if="${#bools.isTrue(bool)}">
<p th:text="${bool}"></p>
</div>
<h3>#arrays 工具类测试</h3>
<div th:if="${not #arrays.isEmpty(testArray)}">
<p>length : <span th:text="${#arrays.length(testArray)}" /></p>
<p>contains : <span th:text="${#arrays.contains(testArray, 5)}" /></p>
<p>
containsAll :
<span th:text="${#arrays.containsAll(testArray, testArray)}" />
</p>
<p>循环读取 : <span th:each="i:${testArray}" th:text="${i+' '}" /></p>
</div>
<h3>#lists 工具类测试</h3>
<div th:unless="${#lists.isEmpty(testList)}">
<p>size : <span th:text="${#lists.size(testList)}" /></p>
<p>contains : <span th:text="${#lists.contains(testList, 0)}" /></p>
<p>sort : <span th:text="${#lists.sort(testList)}" /></p>
<p>循环读取 : <span th:each="i:${testList}" th:text="${i+' '}" /></p>
</div>
<h3>#maps 工具类测试</h3>
<div th:if="${not #maps.isEmpty(testMap)}">
<p>size : <span th:text="${#maps.size(testMap)}" /></p>
<p>
containsKey :
<span th:text="${#maps.containsKey(testMap, 'platform')}" />
</p>
<p>
containsValue : <span th:text="${#maps.containsValue(testMap, '13')}" />
</p>
<p>
读取map中键为title的值 :
<span
th:if="${#maps.containsKey(testMap,'title')}"
th:text="${testMap.get('title')}"
/>
</p>
</div>
<h3>#dates 工具类测试</h3>
<div>
<p>year : <span th:text="${#dates.year(testDate)}" /></p>
<p>month : <span th:text="${#dates.month(testDate)}" /></p>
<p>day : <span th:text="${#dates.day(testDate)}" /></p>
<p>hour : <span th:text="${#dates.hour(testDate)}" /></p>
<p>minute : <span th:text="${#dates.minute(testDate)}" /></p>
<p>second : <span th:text="${#dates.second(testDate)}" /></p>
<p>格式化: <span th:text="${#dates.format(testDate)}" /></p>
<p>
yyyy-MM-dd HH:mm:ss 格式化:
<span th:text="${#dates.format(testDate, 'yyyy-MM-dd HH:mm:ss')}" />
</p>
</div>
</body>
</html>
controller中对应方法的代码:
@GetMapping("/test")
public String test(ModelMap map) {
map.put("title", "Thymeleaf 语法测试");
map.put("testString", "玩转 Spring Boot");
map.put("bool", true);
map.put("testArray", new Integer[]{
2018,2019,2020,2021});
map.put("testList", Arrays.asList("Spring", "Spring Boot", "Thymeleaf", "MyBatis", "Java"));
Map testMap = new HashMap();
testMap.put("platform", "大夫地");
testMap.put("title", "玩转 Spring Boot");
testMap.put("author", "团团");
map.put("testMap", testMap);
map.put("testDate", new Date());
return "test";
}
展示效果(未截取完):
在 strings 工具类测试中,我们首先使用了 th:if 标签进行逻辑判断,th:if="${not #strings.isEmpty(testString)}"即为一条判断语句,${…} 表达式中会返回一个布尔值结果,如果为 true 则该 div 中的内容会继续显示,否则将不会显示 th:if 所在的主标签。
#strings.isEmpty 的作用为字符串判空,如果 testString 为空则会返回 true,而表达式前面的 not 则表示逻辑非运算,即如果 testString 不为空则继续展示该 div 中的内容。
与 th:if 类似的判断标签为 th:unless ,它与 th:if 刚好相反,当表达式中返回的结果为 false 时,它所在标签中的内容才会继续显示,在 #lists 工具类测试中我们使用了 th:unless ,在调试代码时可以比较二者的区别。
Thymeleaf 模板引擎中的循环语句语法为 th:each=“i:${testList}” ,类似于 JSP 中的 c:foreach 表达式,主要是做循环的逻辑,很多页面逻辑在生成时会使用到该语法。
还有读取 Map 对象的方式为 ${testMap.get(‘title’)} ,与 Java 语言中也很类似。逻辑判断、循环语句这两个知识点是系统开发中比较常用也比较重要的内容,应当结合代码练习并牢牢掌握。
三、注意事项
1.必须引入名称空间
<html lang="en" xmlns:th="http://www.thymeleaf.org"></html>
2.IDEA 中爆红色下划线问题
如下图所示,在刚开始使用 Thymeleaf 开发时可能会碰到这种问题,在模板文件中通过 Thymeleaf 语法读取变量时,该变量名称下会出现红色波浪线,也就是错误的标志。
如果不熟的人可能会认为自己的模板文件有问题,但其实并不是那么严重,只是由于 IDEA 中默认开启了表达式参数验证,即使在后端的 model 数据中添加了该变量,但是对于前端文件是无法感知的,因此会报这个错误,可以在 IDEA 中默认将验证关闭或者将提示级别修改掉也可以。