SpringBoot使用Thymeleaf模板引擎
1、认识Thymeleaf
SpringBoot主要支持Thymeleaf、Freemarker、Mustache、 Groovy Templates等模板引擎。Thymeleaf可以轻易地与SpringMVC等Web框架进行集成。Thymeleaf语法并不会破坏文档的结构,所以Thymeleaf模板依然是有效的HTML文档。模板还可以被用作工作原型,Thymeleaf会在运行期内替换掉静态值。它的模板文件能直接在浏览器中打开并正确显示页面,而不需要启动整个Web应用程序。
Thymeleaf的使用非常简单。比如,要输出“pan_junbiao的博客”字符串,可以很简单地在模板文件中加入以下代码:
<p th:text="${userName}?:'pan_junbiao的博客'"></p>
其中,“<p th:text="${userName}>”用来接收控制器传入的参数“userName”。如果控制器向模板传入了参数,则Thymeleaf会用“userName”参数的值替换掉“pan_junbiao的博客”。
2、使用Thymeleaf
(1)引入依赖
使用Thymeleaf,首先需要引入依赖。直接在pom.xml文件中加入以下依赖即可。
<!-- 引入Thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
(2)在模板文件中加入解析
在加入依赖后,还需要在HTML文件中加入命名空间,这样就能完成Thymeleaf的标签的渲染。命名空间如下:
<html lang="en" xmlns:th="http://www.thymeleaf.org">
以下代码是一个简单完整的Thymeleaf模板文件。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户信息</title>
<meta name="author" content="pan_junbiao的博客">
</head>
<body>
<p th:text="${userName}?:'pan_junbiao的博客'"></p>
</body>
</html>
(3)配置视图解析器
在application.properties配置文件中,可以配置Thymeleaf模板解析器属性,如以下代码:
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
#为便于测试,在开发时需要关闭缓存
spring.thymeleaf.cache=false
在application.yml的配置如下:
spring:
thymeleaf:
mode: HTML5
encoding: UTF-8
cache: false #使用Thymeleaf模板引擎,关闭缓存
servlet:
content-type: text/html
Thymeleaf检查HTML格式很严格。如果HTML格式不对,则会报错。如果想禁止这种严格的语法检查模式,这可以在application.properties配置文件中加入“spring.thymeleaf.mode=LEGACYHTML5”来解决。在开发过程中,一般将Thymeleaf的模板缓存设置为关闭,即在application.properties配置文件中加入“spring.thymeleaf.cache=false”。否则,修改之后可能不会及时显示修改后的内容。
【示例】使用Thymeleaf模板引擎,显示用户信息。
(1)创建实体类
在SpringBoot项目中,创建entity目录(实体类层),并创建User(用户信息实体类)。
package com.pjb.thymeleafdemo.entity;
/**
* 用户信息实体类
* @author pan_junbiao
**/
public class User
{
private int id; //用户编号
private String userName; //用户姓名
private String blogUrl; //博客地址
private String blogInfo; //博客信息
private String role; //用户角色
//省略getter与setter方法...
}
(2)创建Controller控制器
创建controller目录(控制器层),并创建UserController(用户信息控制器类)。
package com.pjb.thymeleafdemo.controller;
import com.pjb.thymeleafdemo.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 用户信息控制器类
* @author pan_junbiao
**/
@Controller
@RequestMapping("/user")
public class UserController
{
/**
* 获取用户信息
*/
@RequestMapping("/getUser")
public String getUser(Model model)
{
//创建用户信息
User user = new User();
user.setId(1);
user.setUserName("pan_junbiao的博客");
user.setBlogUrl("https://blog.csdn.net/pan_junbiao");
user.setBlogInfo("您好,欢迎访问 pan_junbiao的博客");
user.setRole("admin");
//将用户信息保存到Model对象中
model.addAttribute("user",user);
//返回页面
return "/user/userInfo";
}
}
(2)创建HTML页面
在resources/templates目录下,创建user目录,并在该目录下创建userInfo.html(用户信息页面)。
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户信息</title>
<meta name="author" content="pan_junbiao的博客">
</head>
<body>
<h3>使用Thymeleaf模板引擎</h3>
用户编号:<span th:text="${user.id}"></span><br/>
用户姓名:<span th:text="${user.userName}"></span><br/>
博客地址:<span th:text="${user.blogUrl}"></span><br/>
博客信息:<span th:text="${user.blogInfo}"></span><br/>
</body>
</html>
执行结果:
3、基础语法
3.1 常用th标签
(1)th:text
<div th:text="${userName}"></div>
它用于显示控制器传入的userName值。
如果userName不存在,要显示默认值,则使用以下代码:
<div th:text="${userName}?:'pan_junbiao的博客'"></div>
(2)th:object
它用于接收后台传过来的对象,如以下代码:
th:object="${userName}"
(3)th:action
它用来指定表单提交地址。
<form th:action="@{/user/}+${user.id}" method="post">
</form>
(4)th:value
它用对象将id的值替换为value的属性值。
<input type="text" th:value="${user.id}" name="id"/>
(5)th:field
它用来绑定后台对象和表单对象数据。Thymeleaf里的“th:field”等同于“th:name”和“th:value”。
<input type="text" id="userName" name="userName" th:field="${user.userName}" />
(6)th:block
th:block 可以排除标签(不写在标签中)直接显示内容。
例如在循环遍历内容时,可以使用 th:block 排除外层标签。
<th:block th:each="user:${userList}">
用户编号:<span th:text="${user.id}"></span><br/>
用户姓名:<span th:text="${user.userName}"></span><br/>
博客地址:<span th:text="${user.blogUrl}"></span><br/>
博客信息:<span th:text="${user.blogInfo}"></span><br/>
</th:block>
(7)th:attr
th:attr 可以将数据作为html标签的一个属性存起来。
<input id="male" name="sex" type="radio" value="1" th:attr="checked=${staffModel.sex==1}"/>
<label for="male">男</label>
<input id="female" name="sex" type="radio" value="2" th:attr="checked=${staffModel.sex==2}"/>
<label for="female">女</label>
等价于:
<input id="male" name="sex" type="radio" value="1" th:checked="${staffModel.sex==1}"/>
<label for="male">男</label>
<input id="female" name="sex" type="radio" value="2" th:checked="${staffModel.sex==2}"/>
<label for="female">女</label>
3.2 Thymeleaf中的URL写法
Thymeleaf是通过语法@{...}来处理URL的,需要使用“th:href”和“th:src”等属性,如以下代码:
<a th:href="@{https://blog.csdn.net/pan_junbiao}">pan_junbiao的博客</a>
<a th:href="@{js/jquery-3.4.1.min.js}">默认访问static目录下的js文件夹</a>
3.3 用Thymeleaf进行条件求值
Thymeleaf通过“th:if”和“th:unless”属性进行条件判断。在下面的例子中,<a>标签只有在“th:if”中的条件成立时才显示。
<a th:href="@{/login}" th:if="${session.user == null}">登录</a>
“th:unless”与“th:if”恰好相反——只有当表达式中的条件不成立时才显示其内容。在下方代码中,如果用户session为空,则不显示登陆连接。
<a th:href="@{/login}" th:unless="${session.user == null}">登录</a>
3.4 Switch
Thymeleaf支持Switch结构,如以下代码:
<div th:switch="${user.role}">
<p th:case="admin">管理员</p>
<p th:case="vip">vip会员</p>
<p th:case="*">普通会员</p>
</div>
在Switch结构中,“*”表示默认情况。
3.5 Thymeleaf中的字符串替代
有时需要对字符串中的某一处地方进行替换,可以通过字符串拼接操作完成,如下代码:
<span th:text="'您好,欢迎访问'+${user.userName} + '!'"></span>
或:
<span th:text="|您好,欢迎访问${user.userName}!|"></span>
上面的第2种形式限制比较多,|...|中只能包括变量表达式${...},不能包含其他常量、条件表达式等。
3.6 Thymeleaf的运算符
(1)算数运算符
如果要在模板中进行算数运算,则可以用下面的写法。
<span th:text="1+3"></span><br/>
<span th:text="9%2"></span><br/>
(2)条件运算符 th:if
下方代码演示了if判断。
<div th:if="${user.role} eq admin">
<span>欢迎您,管理员</span>
</div>
<div th:if="${user.role} eq vip">
<span>欢迎您,vip会员</span>
</div>
eq是判断表达式,代表等于。其他的判断表达式如下:
gt:大于。
ge:大于或等于。
eq:等于。
lt:小于。
le:小于或等于。
ne:不等于。
4、处理循环遍历
4.1 遍历对象(Object)
在开发过程中,经常会遇到遍历对象的情况,可以通过“tth:each="user:${user}"”标签来处理。以下代码是遍历从控制器中传来的用户对象。
<div th:each="user:${user}">
用户编号:<span th:text="${user.id}"></span><br/>
用户姓名:<span th:text="${user.userName}"></span><br/>
博客地址:<span th:text="${user.blogUrl}"></span><br/>
博客信息:<span th:text="${user.blogInfo}"></span><br/>
</div>
外层使用 th:block 标签,则可以排除标签(不写在标签中)直接显示内容。
<th:block th:each="user:${user}">
用户编号:<span th:text="${user.id}"></span><br/>
用户姓名:<span th:text="${user.userName}"></span><br/>
博客地址:<span th:text="${user.blogUrl}"></span><br/>
博客信息:<span th:text="${user.blogInfo}"></span><br/>
</th:block>
4.2 遍历列表(List)
要处理List,也使用“th:each="item:${userList}"”。
<div th:each="user:${userList}">
用户编号:<span th:text="${user.id}"></span><br/>
用户姓名:<span th:text="${user.userName}"></span><br/>
博客地址:<span th:text="${user.blogUrl}"></span><br/>
博客信息:<span th:text="${user.blogInfo}"></span><br/>
</div>
迭代下标变量用法:
状态变量定义在一个th:每个属性和包含以下数据:
当前迭代索引,从0开始。这是索引属性。index
当前迭代索引,从1开始。这是统计属性。count
元素的总量迭代变量。这是大小属性。 size
iter变量为每个迭代。这是目前的财产。 current
是否当前迭代是奇数还是偶数。这些even/odd的布尔属性。
是否第一个当前迭代。这是first布尔属性。
是否最后一个当前迭代。这是last布尔属性。
用法实例:
<table>
<tr>
<th>序号</th>
<th>用户姓名</th>
<th>博客地址</th>
<th>博客信息</th>
</tr>
<tr th:each="user,userStat:${userList}" th:class="${userStat.odd}?'odd':'even'">
<td th:text="${userStat.index}"></td>
<td th:text="${user.userName}"></td>
<td th:text="${user.blogUrl}"></td>
<td th:text="${user.blogInfo}"></td>
</tr>
</table>
4.3 遍历数组(Array)
使用“th:each="item:${userArray}"”标签来遍历数组。
<div th:each="item:${userArray}">
<li th:text="${item}"></li>
</div>
4.4 遍历集合(Map)
集合通过“th:text="${item.key}”显示集合的key,通过“th:text="${item.value}”显示集合的值。
<!--遍历key-->
<div th:each="item:${userMap}">
<li th:text="${item.key}"></li>
</div>
<!--遍历value-->
<div th:each="item:${userMap}">
<li th:text="${item.value}"></li>
</div>
<!--遍历key-value-->
<div th:each="item:${userMap}">
<li th:text="${item}"></li>
</div>
5、处理公共代码块
一个网页的结构基本可以分为上(header)、中(body)、下(footer)三个部分。在一般情况下,header和footer的信息在各个页面都会重复显示,如果每个页面都复制一份代码则太麻烦了。
Thymeleaf模板引擎提供了th:fragment、th:replace、th:include标签用来处理重复的代码块。
1、使用th:fragment标记重复的代码块
创建common.html页面,在该页面中使用th:fragment标记重复的代码块,代码如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<div class="header" th:fragment="header">
公共 header
</div>
<div class="footer" th:fragment="footer">
公共 footer
</div>
</html>
2、调用重复代码块
在需要调用的地方,使用th:replace或th:include标签根据th:fragment标记的值来调用,如以下代码:
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
<meta name="author" content="pan_junbiao的博客">
</head>
<body>
<div>replace调用方式:</div>
<div th:replace="~{common :: header}"></div>
<div>
<p>您好,欢迎访问 pan_junbiao的博客</p>
<p>https://blog.csdn.net/pan_junbiao</p>
</div>
<div>include调用方式:</div>
<div th:include="~{common :: footer}"></div>
</body>
</html>
执行结果:
th:replace和th:include标签都可以调用公共代码。它们的区别如下:
th:replace标签:替换当前标签为模板中的标签。比如上面用th:replace标签,则代码替换为:
<div class="header">
公共 header
</div>
th:include标签:只加载模板的内容。比如上面用th:include标签,则代码替换为:
<div>
公共 footer
</div>
6、Thymeleaf将后端返回的数据绑定给JS变量
Thymeleaf还可以将后端返回的数据绑定给JS变量,使用表达式,如下:
var userName = [[${userName}]];
【示例】在JS中使用Thymeleaf获取后端返回的数据。
(1)后端控制器
/**
* 用户信息控制器
* @author pan_junbiao
**/
@Controller
@RequestMapping("/user")
public class UserController
{
@RequestMapping("/getUserName")
public String getUserName(HttpServletRequest request)
{
request.setAttribute("userName","pan_junbiao的博客");
return "user.html";
}
}
(2)前端JS脚本
<script th:inline="javascript">
var userName = [[${userName}]];
alert(userName);
</script>
执行结果:
7、解决IDEA中使用Thymeleaf页面变量报错问题
IDEA在使用Thymeleaf页面编写变量,如${user.id}会出现红色波浪下划线错误,提示:Validates unresolved references and invalid expressions.
解决方法:
菜单:File → Settings → Editor → Inspections,将Thymeleaf的检测关闭。