Thymeleaf教程(10分钟入门)
Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。它与 JSP,Velocity,FreeMaker 等模板引擎类似,也可以轻易地与 Spring MVC 等 Web 框架集成。与其它模板引擎相比,Thymeleaf 最大的特点是,即使不启动 Web 应用,也可以直接在浏览器中打开并正确显示模板页面 。
一. Thymeleaf 简介
Thymeleaf 是新一代 Java 模板引擎,与 Velocity、FreeMarker 等传统 Java 模板引擎不同,Thymeleaf 支持 HTML 原型,其文件后缀为“.html”,因此它可以直接被浏览器打开,此时浏览器会忽略未定义的 Thymeleaf 标签属性,展示 thymeleaf 模板的静态页面效果;当通过 Web 应用>程序访问时,Thymeleaf 会动态地替换掉静态内容,使页面动态显示。
Thymeleaf 通过在 html 标签中,增加额外属性来达到“模板+数据”的展示方式,示例代码如下。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--th:text 为 Thymeleaf 属性,用于在展示文本-->
<h1 th:text="迎您来到Thymeleaf">欢迎您访问静态页面 HTML</h1>
</body>
</html>
当直接使用浏览器打开时,浏览器展示结果如下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9nSkOlHl-1657013815619)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\1.png)]
当通过 Web 应用程序访问时,浏览器展示结果如下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pmund1OJ-1657013815621)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\2.png)]
Thymeleaf 的特点
Thymeleaf 模板引擎具有以下特点:
- 动静结合:Thymeleaf 既可以直接使用浏览器打开,查看页面的静态效果,也可以通过 Web 应用程序进行访问,查看动态页面效果。
- 开箱即用:Thymeleaf 提供了 Spring 标准方言以及一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
- 多方言支持:它提供了 Thymeleaf 标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL 表达式;必要时,开发人员也可以扩展和创建自定义的方言。
- 与 SpringBoot 完美整合:SpringBoot 为 Thymeleaf 提供了的默认配置,并且还为 Thymeleaf 设置了视图解析器,因此 Thymeleaf 可以与 Spring Boot 完美整合。
二. Thymeleaf 语法规则
在使用 Thymeleaf 之前,首先需要导入thymeleaf依赖,然后在页面的 html 标签中声明名称空间,示例代码如下。
<!-- thymeleaf依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<html xmlns:th="http://www.thymeleaf.org">
</html>
在 html 标签中声明此名称空间,可避免编辑器出现 html 验证错误,但这一步并非必须进行的,即使我们不声明该命名空间,也不影响 Thymeleaf 的使用。
Thymeleaf 作为一种模板引擎,它拥有自己的语法规则。Thymeleaf 语法分为以下 2 类:
- 标准表达式语法
- th 属性
2.1 基础语法
2.1.1 变量表达式 ${}
使用方法:直接使用 th:xx = “${}” 获取对象属性。例如:
<form id="userForm">
<input id="id" name="id" th:value="${user.id}"/>
<input id="username" name="username" th:value="${user.username}"/>
<input id="password" name="password" th:value="${user.password}"/>
</form>
<div th:text="hello"></div>
<div th:text="${user.username}"></div>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZ7ta3MD-1657013815622)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\3.png)]
2.1.2 选择变量表达式 *{}
使用方法:首先通过th:object
获取对象,然后使用th:xx = "*{}"
获取对象属性。
这种简写风格极为清爽,推荐大家在实际项目中使用。 例如:
<form id="userForm" th:object="${user}">
<input id="id" name="id" th:value="*{id}"/>
<input id="username" name="username" th:value="*{username}"/>
<input id="password" name="password" th:value="*{password}"/>
</form>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cUVQ0wMT-1657013815623)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\4.png)]
2.1.3 链接表达式 @{}
使用方法:通过链接表达式@{}
直接拿到应用路径,然后拼接静态资源路径。例如:
<script th:src="@{jquery/jquery-1.10.2.min.js}"></script>
<link th:href="@{bootstrap/css/bootstrap.css}" rel="stylesheet" type="text/css">
2.1.4 片段表达式 ~{}
片段表达式是Thymeleaf的特色之一,细粒度可以达到标签级别,这是JSP无法做到的。
片段表达式拥有三种语法:
~{ viewName } 表示引入完整页面
~{ viewName ::selector} 表示在指定页面寻找片段 其中selector可为片段名、jquery选择器等
~{ ::selector} 表示在当前页寻找
使用方法:首先通过th:fragment
定制片段 ,然后通过th:replace
填写片段路径和片段名。例如:
<!-- comm.html-->
<head th:fragment="page">
<scrpit th:src="@{jquery/jquery-1.10.2.min.js}"></scrpit>
</head>
<body>
<h1>这是引入完整的页面</h1>
</body>
<!-- demo01.html -->
<div th:replace="~{comm}"></div>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nqnmgtq2-1657013815623)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\5.png)]
<!-- comm.html-->
<head th:fragment="page">
<scrpit th:src="@{jquery/jquery-1.10.2.min.js}"></scrpit>
</head>
<!-- demo01.html -->
<div th:replace="~{comm::page}"></div>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XDAr5URE-1657013815624)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\6.png)]
在实际使用中,我们往往使用更简洁的表达,去掉表达式外壳直接填写片段名。例如:
<!-- demo01.html -->
<div th:replace="comm::page"></div>
值得注意的是,使用替换路径th:replace
开头请勿添加斜杠,避免部署运行的时候出现路径报错。(因为默认拼接的路径为spring.thymeleaf.prefix = classpath:/templates/
)
2.1.5 消息表达式
即通常的国际化属性:#{msg}
用于获取国际化语言翻译值。例如:
<title th:text="#{user.title}"></title>
2.1.6 其它表达式
在基础语法中,默认支持字符串连接、数学运算、布尔逻辑和三目运算等。例如:
<input name="name" th:value="${'I am '+(user.name!=null ? user.name:'NoBody')}"/>
常用的 th 标签
关键字 | 功能介绍 | 案例 |
---|---|---|
th:id | 替换id | <input th:id="'xxx' + ${collect.id}"/> |
th:text | 文本替换 | <p th:text="${collect.description}">description</p> |
th:utext | 支持html的文本替换 | <p th:utext="${htmlcontent}">conten</p> |
th:object | 替换对象 | <div th:object="${session.user}"> |
th:value | 属性赋值 | <input th:value="${user.name}"/> |
th:with | 变量赋值运算 | <div th:with="isEven=${prodStat.count}%2==0"></div> |
th:style | 设置样式 | th:style="'display:' + @{(${sitrue} ? 'none' : 'inline-block')} + ''" |
th:onclick | 点击事件 | th:onclick="'getCollect()'" |
th:each | 属性赋值 | tr th:each="user,userStat:${users}"> |
th:if | 判断条件 | <a th:if="${userId == collect.userId}" > |
th:unless | 和th:if判断相反 | <a th:href="@{/login}" th:unless=${session.user != null}>Login</a> |
th:href | 链接地址 | <a th:href="@{/login}" th:unless=${session.user != null}>Login</a> /> |
th:switch | 多路选择 配合th:case 使用 | <div th:switch="${user.role}"> |
th:case | th:switch的一个分支 | <p th:case="'admin'">User is an administrator</p> |
th:fragment | 布局标签,定义一个代码片段,方便其它地方引用 | <div th:fragment="alert"> |
th:include | 布局标签,替换内容到引入的文件 | <head th:include="layout :: htmlhead" th:with="title='xx'"></head> /> |
th:replace | 布局标签,替换整个标签到引入的文件 | <div th:replace="fragments/header :: title"></div> |
th:selected | selected选择框 选中 | th:selected="(${xxx.id} == ${configObj.dd})" |
th:src | 图片类地址引入 | <img class="img-responsive" alt="App Logo" th:src="@{/img/logo.png}" /> |
th:inline | 定义js脚本可以使用变量 | <script type="text/javascript" th:inline="javascript"> |
th:action | 表单提交的地址 | <form action="subscribe.html" th:action="@{/subscribe}"> |
th:remove | 删除某个属性 | <tr th:remove="all"> 1.all:删除包含标签和所有的孩子。 |
th:attr | 设置标签属性,多个属性可以用逗号分隔 | 比如 th:attr="src=@{/image/aa.jpg},title=#{logo}" ,此标签不太优雅,一般用的比较少。 |
还有非常多的标签,这里只列出最常用的几个,由于一个标签内可以包含多个th:x属性,其生效的优先级顺序为:
include,each,if/unless/switch/case,with,attr/attrprepend/attrappend,value/href,src ,etc,text/utext,fragment,remove。
三、内置对象
3.1 七大内置对象
-
${#ctx}
上下文对象,可用于获取其它内置对象。 -
${#vars}
: 上下文变量。 -
${#locale}
:上下文区域设置。 -
${#request}
: HttpServletRequest对象。 -
${#response}
: HttpServletResponse对象。 -
${#session}
: HttpSession对象。 -
${#servletContext}
: ServletContext对象。
3.2 常用的工具类:
-
#strings
:字符串工具类 -
#lists
:List 工具类 -
#arrays
:数组工具类 -
#sets
:Set 工具类 -
#maps
:常用Map方法。 -
#objects
:一般对象类,通常用来判断非空 -
#bools
:常用的布尔方法。 -
#execInfo
:获取页面模板的处理信息。 -
#messages
:在变量表达式中获取外部消息的方法,与使用#{…}语法获取的方法相同。 -
#uris
:转义部分URL / URI的方法。 -
#conversions
:用于执行已配置的转换服务的方法。 -
#dates
:时间操作和时间格式化等。 -
#calendars
:用于更复杂时间的格式化。 -
#numbers
:格式化数字对象的方法。 -
#aggregates
:在数组或集合上创建聚合的方法。 -
#ids
:处理可能重复的id属性的方法。
四、迭代循环
想要遍历List集合很简单,配合th:each 即可快速完成迭代。例如遍历用户列表:
<div th:each="user:${users}">
id:<input id="id" name="id" th:value="${user.id}"/> <br>
姓名:<input id="username" name="username" th:value="${user.username}"/> <br>
</div>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j5b118Sk-1657013815625)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\7.png)]
在集合的迭代过程还可以获取状态变量,只需在变量后面指定状态变量名即可,状态变量可用于获取集合的下标/序号、总数、是否为单数/偶数行、是否为第一个/最后一个。例如:
<table>
<tr>
<td>下标</td>
<td>序号</td>
<td>id</td>
<td>姓名</td>
<td>年龄</td>
</tr>
<tr th:each="user,item:${users}">
<td th:text="${item.index}">index</td>
<td th:text="${item.count}">count</td>
<td th:text="${user.id}">id</td>
<td th:text="${user.username}">姓名</td>
<td th:text="${user.age}">年龄</td>
</tr>
</table>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6tUawEDY-1657013815625)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\8.png)]
五、条件判断
条件判断通常用于动态页面的初始化,例如:
<div th:if="${users}">
<div>哈哈哈,users存在</div>
</div>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EGSIJnwE-1657013815626)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\9.png)]
如果想取反则使用unless 例如:
<div th:unless="${userList}">
<div>不存在..</div>
</div>
六、日期格式化
使用默认的日期格式并不是我们预期的格式:Tue Jul 05 13:48:47 CST 2022
<h1 th:text="${user.creatTime}"></h1>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gb5SgERN-1657013815626)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\10.png)]
此时可以通过时间工具类#dates
来对日期进行格式化:2022-70-05 13:50:23
<h1 th:text="${#dates.format(user.createTime,'yyyy-MM-dd HH:mm:ss')}"></h1>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ntau8SmW-1657013815627)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\11.png)]
七、内联写法
-
(1) 为什么要使用内联写法?
答: 因为 JS 无法获取服务端返回的变量 -
(2) 如何使用内联表达式?
答: 标准格式:[[${xx}]]
,可以读取服务端变量,也可以调用内置对象的方法。例如获取用户变量和应用路径:
<script>
var users = `[[${userList}]]`;
console.log("users :" + users)
</script>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SEtCmMrL-1657013815627)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\12.png)]
-
(3) 标签引入的 JS 里面能使用内联表达式吗?
答: 不能!内联表达式仅在页面生效,因为
Thymeleaf
只负责解析一级视图,不能识别外部标签 JS 里面的表达式。
九、Demo案例
创建 SpringBoot 项目,如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fL7YKlhA-1657013815628)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\demo01.png)]
引入相关依赖
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- web相关依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- thymeleaf依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 热部署依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
然后在 application.yml 中配置
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
编写 User 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private String address;
private Date createTime;
}
编写 controller 层,返回用户信息
@Controller
public class UserController {
@RequestMapping("/get")
public String test01(Model model){
ArrayList<User> userList = new ArrayList<>();
userList.add(new User(1,"名字1","123456","成都",new Date()));
userList.add(new User(2,"名字2","456789","grgk",new Date()));
userList.add(new User(3,"名字3","789123","成都",new Date()));
userList.add(new User(4,"名字4","987654","grgk",new Date()));
model.addAttribute("userList",userList);
return "user";
}
}
编写前端 user.html 页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:each="user,userStat:${userList}">
序号:<input type="text" th:value="${userStat.count}">
账号:<input type="text" th:value="${user.username}">
密码:<input type="text" th:value="${user.password}">
时间:<input type="text" th:value="${user.createTime}">
格式化后的时间:<input type="text" th:value="${#dates.format(user.createTime,'yyyy-MM-dd HH:mm:ss')}">
</div>
</body>
<script>
var users = `[[${userList}]]`;
console.log("users :" + users)
</script>
</html>
然后访问 http://localhost:8080/get 运行结果如图所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iemweunh-1657013815628)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\demo02.png)]
u
s
e
r
.
u
s
e
r
n
a
m
e
"
>
密
码
:
<
i
n
p
u
t
t
y
p
e
=
"
t
e
x
t
"
t
h
:
v
a
l
u
e
=
"
{user.username}"> 密码:<input type="text" th:value="
user.username">密码:<inputtype="text"th:value="{user.password}">
时间:
格式化后的时间:
然后访问 http://localhost:8080/get 运行结果如图所示
[外链图片转存中…(img-Iemweunh-1657013815628)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LpodE6Qw-1657013815628)(C:\Users\fm016\Desktop\新建文件夹\新建文件夹\demo22.png)]