Thymeleaf是一个流行的模板引擎,是基于HTML的,语法应用在HTML标签中。该模板引擎采用java语言开发。
模板引擎是做视图层工作的,在服务器端将Controller传过来的数据替换掉模板上的静态数据。(模板+数据=展示页面) Java生态下的模板引擎有:Thymeleaf、Freemaker、Velocity、Beetl等。
Spring Boot框架集成了Thymeleaf,并且Spring Boot官方也推荐使用Thymeleaf来替代jsp技术。Thymeleaf 是另外的一种模板技术,它本身并不属于 Spring Boot,Spring Boot只是很好地集成这种模板技术,作为前端页面的数据展示,在过去的 Java Web 开发中,我们往往会选择使用 Jsp 去完成页面的动态渲染,但是 jsp 需要翻译编译运行,效率低。
Thymeleaf 的官方网站: http://www.thymeleaf.org
Thymeleaf 官方手册: https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
1、创建Spring Boot项目,添加web和Thymeleaf起步依赖
<!--thymeleaf起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2、在resources/templates目录下创建index.html
1)在html根元素添加命名空间声明:xmlns:th="http://www.thymeleaf.org"
2)在html标签内引用thymeleaf的语法:th:text="${data}"
,接收key为data的数据,替换所修饰标签内的文本内容。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><!--1.添加命名空间声明-->
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--2.引用thymeleaf语法-->
<h2 th:text="${data}">测试数据</h2>
</body>
</html>
3、创建Controller
package com.tsccg.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ThymeleafController {
@GetMapping("/find")
public String find(Model model) {
//向请求作用域中写入数据
model.addAttribute("data","Hello Thymeleaf");
//返回视图
return "index";
4、启动应用,在浏览器中发送请求: http://localhost:8080/find
3. 模板引擎的常用设置
我们在前面的入门案例中,Controller方法返回的是逻辑视图名称,但我们并没有设置视图解析器,也能访问到页面。这是因为Thymeleaf在核心配置文件中有一些默认的设置,如下:
##Thymeleaf默认设置
#缓存 默认为true,但在开发阶段为了修改立即生效,需设置为false
spring.thymeleaf.cache=false
#模板使用的字符编码格式
spring.thymeleaf.encoding=UTF-8
#模板类型 默认为HTML,模板是html文件
spring.thymeleaf.mode=HTML
#模板视图前缀 默认设置为 classpath:/templates/
spring.thymeleaf.prefix=classpath:/templates/
#模板视图后缀 默认为 .html
spring.thymeleaf.suffix=.html
4. 表达式
表达式就是在页面中获取数据的一种thymeleaf语法,类似于el表达式的${key}
4.1 标准变量表达式
语法:${key}
作用:获取请求作用域中key对应的文本数据
实例演示:
以前面的入门案例为基础进行修改。
①创建Student类
package com.tsccg.pojo;
public class Student {
private String name;
private Integer age;
private String email;
//无、有参构造
//get set
//toString
②修改index.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<title>Title</title>
</head>
<h1>标准变量表达式</h1>
<h2>获取简单类型数据</h2>
<p th:text="${data}"></p>
<h2>获取对象属性值</h2>
<p th:text="${student.name}"></p>
<p th:text="${student.age}"></p>
<p th:text="${student.email}"></p>
<p th:text="${student.getEmail()}"></p>
</body>
</html>
③修改Controller
@Controller
public class ThymeleafController {
@RequestMapping("/findStudent")
public String findStudent(Model model) {
Student student = new Student("张三",20,"[email protected]");
model.addAttribute("student",student);
model.addAttribute("data","Hello Thymeleaf");
return "index";
④开启应用,在浏览器访问 http://localhost:8080/findStudent
4.2 选择变量表达式
语法:*{key}
说明:需要配合th:object
一起使用。使用th:object
属性来绑定对象,然后使用*
来代表这个对象,后面{}
中的值是此对象中的属性。
作用:获取key对应的对象属性值
实例演示:
①在resources/templates目录下新建select.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<title>Title</title>
</head>
<h1>选择变量表达式</h1>
<div th:object="${student}">
<p th:text="*{name}"></p>
<p th:text="*{age}"></p>
<p th:text="*{email}"></p>
<p th:text="*{student.email}"></p>
</body>
</html>
②在Controller中新添对应方法
@RequestMapping("/select")
public String select(Model model) {
Student student = new Student("李四",22,"[email protected]");
model.addAttribute("student",student);
return "select";
③开启应用,在浏览器访问 http://localhost:8080/select
4.3 链接表达式
语法:@{url}
作用:主要用于链接、地址的展示,可用于<script src="...">
、<link href="...">
、<a href="...">
、<form action="...">
、<img src="">
等,可以在 URL 路径中动态获取数据
实例演示:
以入门案例为基础进行修改。
①在resources/templates目录下创建 link.html
使用Thymeleaf的链接表达式发送各种请求
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<title>Title</title>
</head>
<h1>链接表达式</h1>
<a th:href="@{http://www.baidu.com}">链接绝对地址</a>
<a th:href="@{/get}">链接相对地址,无参</a>
<a th:href="@{'/get?id=' + ${id}}">链接相对地址,有参</a>
<a th:href="@{/getStudent(id=${id},name=${name})}">传参数方式 2</a>
<form th:action="@{'/student/' + ${id}}" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="RESTful风格DELETE请求">
</form>
<form th:action="@{/student/{id}(id=${id})}" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="RESTful风格DELETE请求 方式2">
</form>
</body>
</html>
②在Controller中添加方法
@RequestMapping("/link")
public String link(Model model) {
model.addAttribute("id",1001);
model.addAttribute("name","Tom");
return "link";
@RequestMapping("/get")
@ResponseBody
public String get(Integer id) {
return "查询学生,id=" + id;
@RequestMapping("/getStudent")
@ResponseBody
public String getStudent(Integer id,String name) {
return "查询学生,id=" + id + ",name=" + name;
@DeleteMapping("/student/{id}")
@ResponseBody
public String restFul(@PathVariable Integer id) {
return "执行删除操作,id=" + id;
③在核心配置文件中开启HiddenHttpMethodFilter过滤器
#开启HiddenHttpMethodFilter过滤器
spring.mvc.hiddenmethod.filter.enabled=true
④开启应用,在浏览器发送请求 http://localhost:8080/link
5. Thymeleaf属性
Thymeleaf的大部分属性和html元素的属性一样。在html元素属性前加上th:
前缀之后,属性的作用不变,属性的值由模板引擎处理。
在Thymeleaf属性中可以使用变量表达式。
5.1 常用基本属性
1、th:action
作用同form表单标签的action属性,主要结合链接表达式,获取动态变量。
<form th:action="@{/tmp/doSome(id=${id})}" th:method="post" >...</form>
2、th:method
同form表单的method属性。
<form th:action="@{/tmp/doSome(id=${id})}" th:method="post" >...</form>
3、th:href
定义超链接,主要结合链接表达式,获取动态变量。
<a th:href="@{/getStudent(id=${id},name=${name})}">传参数方式 2</a>
4、th:src
同html的src属性,用于引入外部资源,常与链接表达式结合使用。
在Spring Boot项目中,所有静态资源都放到resources的static目录下,引如此目录下的资源时,路径上不需要写上static。
<script type="text/javascript" th:src="@{js/jquery.min.js}"></script>
5、th:text
用于文本的显示,该属性显示的文本在标签体中,如果是文本框,数据会在文本框外显示,要想显示在文本框内显示,需要使用 th:value。
<h3 th:text="${title}"></h3>
<input type="text" th:text="${preText}" name="username" th:value="${username}" >
6、 th:style
<div th:style="'color: red'"></div>
实例演示使用:
新建一个Spring Boot项目,添加web和thymeleaf起步依赖
①在templates目录下创建模板 test.html
在html元素属性前添加th:
前缀
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../static/js/jquery.min.js"></script>
<script type="text/javascript">
function fun() {
alert("hello");
</script>
</head>
<center>
<h3 th:text="${title}"></h3>
<div th:style="'color: red'">
<form th:action="@{/tmp/doSome(id=${id})}" th:method="post" >
<input type="text" th:name="username" th:value="${username}"><br>
<input type="submit" th:value="提交"><br>
</form>
<input type="button" th:attr="value='按钮'" th:onclick="fun()"><br>
</center>
</body>
</html>
②创建Controller方法
@RequestMapping("/test")
public String test(Model model) {
model.addAttribute("title","Thymeleaf属性");
model.addAttribute("id","1001");
model.addAttribute("username","Tom");
return "test";
③启动应用,访问 http://localhost:8080/test
5.2 th:each 遍历集合
th:each用于遍历后台传过来的集合。
①创建Controller方法
向请求作用域中存入一个List集合,集合中的元素是Student对象。
@RequestMapping("/each")
public String each(Model model) {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("张三",19,"[email protected]"));
studentList.add(new Student("李四",20,"[email protected]"));
studentList.add(new Student("王五",21,"[email protected]"));
studentList.add(new Student("赵六",22,"[email protected]"));
model.addAttribute("studentList",studentList);
return "each";
②创建页面
在templates目录下创建each.html,使用Thymeleaf的th:each
属性将集合中的数据遍历展示在表格中。
<center>
<table cellpadding="0" cellspacing="0" border="1" width="300px">
<thead>
<th>序号</th>
<th>下标</th>
<th>姓名</th>
<th>年龄</th>
<th>邮箱地址</th>
<th>是否为第一行</th>
<th>姓名2</th>
</thead>
<tbody th:each="student,itemStat:${studentList}">
<td th:text="${itemStat.count + '/' + itemStat.size}"></td>
<td th:text="${itemStat.index}"></td>
<td th:text="${student.name}"></td>
<td th:text="${student.age}"></td>
<td th:text="${student.email}"></td>
<td th:text="${itemStat.first}"></td>
<td th:text="${itemStat.current.name}"></td>
</tbody>
</table>
</center>
语法说明:th:each="student,itemStat:${studentList}"
${studentList}
:后台传过来的待遍历集合。
student
:自定义变量,代表当前迭代元素。
itemStat
:自定义变量,代表当前循环体的信息,通过该变量可以获取如下信息
index
:当前迭代元素的下标(从0开始算)
count
:当前迭代元素的个数(从1开始算)
size
:集合中元素的数量
even/odd
:布尔值,当前循环是否是偶数/奇数(从0开始算)
first
:布尔值,当前迭代元素是否是集合中第一个元素
last
:布尔值,当前迭代元素是否是集合中最后一个元素
current
:获取当前迭代元素,即student
注意:上面代表循环体信息的itemStat
也可以不自己命名,默认采用自定义迭代元素名加上Stat后缀,即studentStat
。
③开启应用,在浏览器访问 http://localhost:8080/each
④遍历map
当遍历value为对象的map集合时,需要根据迭代元素的value属性获取对象的数据
<div th:each="m,mSt:${users}">
<p th:text="${mSt.count}"></p>
<p th:text="${m.key}"> </p>
<p th:text="${m.value.id}"></p>
<p th:text="${m.value.name}"></p>
5.3 if 条件判断
th:if="boolean条件"
,条件为true时显示所修饰标签里的内容
th:unless="boolean条件"
,条件为false时显示所修饰标签里的内容
①写Controller方法
@RequestMapping("/if")
public String iF(Model model) {
model.addAttribute("age",20);
model.addAttribute("sex",0);
model.addAttribute("name","");
model.addAttribute("result",null);
return "ifUnless";
②在templates目录下创建ifUnless.html
<p th:if="${age} >= 18">
1、当年龄大于等于18
<p th:unless="${age} >= 18">
2、当年龄小于18
<p th:if="${sex} == 0">
3、当性别为女
<p th:if="${sex} == 1">
4、当性别为男
<p th:if="${result}">
5、当值为null
<p th:if="${name}">
6、当值为空字符串“”
</body>
③开启应用,在浏览器访问 http://localhost:8080/if
可见,当值为""时,也表示true。
5.4 switch case 条件判断
①模板页面
<div th:switch="${sex}">
<p th:case="0">女</p>
<p th:case="1">男</p>
<p th:case="*">人中龙凤</p>
一旦某个case匹配为true,其他case都会被当作false。
*
表示默认的case,当其他所有case都匹配失败时,执行*
。
②处理器方法
@RequestMapping("/switch")
public String switchCase(Model model) {
model.addAttribute("sex",null);
return "switch";
③浏览器访问
5.5 inline 内联
th:inline有三个取值类型:text、javascript、none。
5.5.1 内联text
内联text可以让Thymeleaf表达式不依赖于html标签,单独使用。类似于vue的{{data}}
。
<div th:inline="text"><!--要求在父级标签上加 th:inline="text" 属性,但不加也能用-->
<p>你好,[[${name}]]</p>
实例演示:
①模板页面
<h3>内联text</h3>
<div th:inline="text">
<p>你好,[[${name}]]</p>
<h3>不加 th:inline="text"也能用</h3>
<p>你好,[[${name}]]</p>
②处理器方法
@RequestMapping("/inlineText")
public String inlineText(Model model) {
model.addAttribute("name","阿伟");
return "inlineText";
③页面显示
5.5.2 内联javascript
内联javascript可以在js语句中,通过模板语法获取请求作用域中的数据。
①模板页面
<button onclick="fun1()">按钮</button>
<script type="text/javascript" th:inline="javascript">
var name = [[${name}]];
function fun1() {
alert(name);
</script>
②处理器方法
@RequestMapping("/inlineJs")
public String inlineJs(Model model) {
model.addAttribute("name","杰哥");
return "inlineJs";
③页面显示
5.6 字符串连接
在Thymeleaf中,有两种方式连接字符串:
1)使用单引号把字符串括起来,用+
连接其他字符串或表达式
<p th:text="'a' + ${data} + 'b'"></p>
2)使用双竖线将所有字符串和表达式括起来,字符串无需加单引号
<p th:text="|a${data}b|"></p>
实例演示:
①模板页面
<h3>字符串连接方式1:'a' + ${data} + 'b'</h3>
<p th:text="'我的名字是' + ${name} + ',年龄' + ${age}"></p>
<h3>字符串连接方式2:|a${data}b|</h3>
<p th:text="|我的名字是${name},年龄${age}|"></p>
②处理器方法
@RequestMapping("/connStr")
public String connStr(Model model) {
model.addAttribute("name","汤姆");
model.addAttribute("age","20");
return "connStr";
③页面显示
5.7 内置对象
Thymeleaf模板引擎内置了一组内置的对象,常用的如下:
#request
表示HttpServletRequest 请求作用域对象
#session
表示HttpSession 会话作用域对象
session
表示Map对象的, 是#session
的简单表示方式, 用来获取session中指定的key的值
实例演示:
①处理器方法
@RequestMapping("/innerObj")
public String innerObj(HttpServletRequest request, HttpSession session) {
request.setAttribute("reqData","请求作用域中的数据");
session.setAttribute("sessionData","会话作用域中的数据");
return "innerObj";
②模板页面
<h3>获取作用域中的数据</h3>
<p th:text="${#request.getAttribute('reqData')}"></p>
<p th:text="${#session.getAttribute('sessionData')}"></p>
<p th:text="${session.sessionData}"></p>
<h3>使用内置对象的方法</h3>
getRequestURL=<span th:text="${#request.getRequestURL()}"></span><br/>
getRequestURI=<span th:text="${#request.getRequestURI()}"></span><br/>
getQueryString=<span th:text="${#request.getQueryString()}"></span><br/>
getContextPath=<span th:text="${#request.getContextPath()}"></span><br/>
getServerName=<span th:text="${#request.getServerName()}"></span><br/>
getServerPort=<span th:text="${#request.getServerPort()}"></span><br/>
③页面展示
5.8 内置工具类对象
模板引擎提供了一组功能性内置对象,可以在模板中直接使用这些对象提供的功能方法。工作中常用的数据类型,如集合、时间、数值等都可以使用这些工具类对象来处理。
官方手册:http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
工具类对象前都需要加#
,一般都以s结尾。
常用的工具类对象:
#dates
: 处理日器的工具类
#numbers
:处理数字
#lists
: 处理list集合
实例演示:
①处理器方法
@RequestMapping("/innerUtil")
public String innerUtil(Model model) {
model.addAttribute("myDate",new Date());//日期
model.addAttribute("myNum",26.695);//数字
model.addAttribute("myStr","tsccg");//字符串
//list集合
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
model.addAttribute("myList",list);
//对象,带有null属性
Student student = new Student();
student.setName("张三");
model.addAttribute("student",student);
return "innerUtil";
②模板页面
<h3>日期类对象 #dates</h3>
#dates.format(myDate) == <span th:text="${#dates.format(myDate)}"></span><br>
#dates.format(myDate,'yyyy-MM-dd') == <span th:text="${#dates.format(myDate,'yyyy-MM-dd')}"></span><br>
#dates.format(myDate,'yyyy-MM-dd HH:mm:ss') == <span th:text="${#dates.format(myDate,'yyyy-MM-dd HH:mm:ss')}"></span><br>
#dates.year(myDate) == <span th:text="${#dates.year(myDate)}"></span><br>
#dates.month(myDate) == <span th:text="${#dates.month(myDate)}"></span><br>
#dates.monthName(myDate) == <span th:text="${#dates.monthName(myDate)}"></span><br>
#dates.createNow() == <span th:text="${#dates.createNow()}"></span>
<h3>内置工具类#numbers,操作数字的</h3>
#numbers.formatCurrency(myNum) == <span th:text="${#numbers.formatCurrency(myNum)}"></span><br>
#numbers.formatDecimal(myNum,5,2) == <span th:text="${#numbers.formatDecimal(myNum,5,2)}"></span>
<h3>内置工具类#strings,操作字符串</h3>
#strings.toUpperCase(myStr) == <span th:text="${#strings.toUpperCase(myStr)}"></span><br>
#strings.indexOf(myStr,'b') == <span th:text="${#strings.indexOf(myStr,'s')}"></span><br>
#strings.substring(myStr,2,5) == <span th:text="${#strings.substring(myStr,2,5)}"></span><br>
#strings.substring(myStr,2) == <span th:text="${#strings.substring(myStr,2)}"></span><br>
#strings.concat(myStr,'---Hello String---') == <span th:text="${#strings.concat(myStr,'---Hello String---')}"></span><br>
#strings.length(myStr) == <span th:text="${#strings.length(myStr)}"></span><br>
#strings.length('hello') == <span th:text="${#strings.length('hello')}"></span><br>
#strings.isEmpty(myStr) == <span th:unless="${#strings.isEmpty(myStr)}"> myStr不是空字符串 </span>
<h3>内置工具类#lists,操作list集合</h3>
#lists.size(myList) == <span th:text="${#lists.size(myList)}"></span><br>
#lists.contains(myList,'a') == <span th:if="${#lists.contains(myList,'a')}">有成员a</span><br>
!${#lists.isEmpty(myList)} == <span th:if="!${#lists.isEmpty(myList)}"> list 集合有多个成员</span>
<h3>处理null</h3>
student?.name == <span th:text="${student?.name}"></span><br>
student?.email == <span th:text="${student?.email}"></span><br>
③页面展示
5.9 内容复用
为了将页面中重复的内容提取出来,我们可以创建自定义模板,用Thymeleaf的语法在其它页面中引用模板内容。
1.定义模板
语法:th:fragment="top"
,自定义模板名称为 top
如:head.html
<div th:fragment="top">
www.upanddown.com
2.引用模板
引用语法:
~{templateName :: selector}
,引用selector所包含的部分
templateName :: selector
,引用selector所包含的部分
templateName :: html
,引用整个模板页面
templdate
,引用整个模板页面
其中,templdateName指的是模板文件名称,selector指的是自定义模板名称
有两种引用方式:
插入模板:<div th:insert="~{head :: top}"></div>
包含模板:<div th:include="head :: top"></div>
3.实例演示
①定义模板
在templates目录下分别创建如下页面:
1)head.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<meta charset="UTF-8">
<title>Title</title>
</head>
<div th:fragment="top" style="color: red">
<center>
www.upanddown.com
</center>
</body>
</html>
2)footer.html
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Title</title>
</head>
<div align="center">
www.upanddown.com
@copy; tsccg 2021
</body>
</html>
3)创建目录common,在其中创建left.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<meta charset="UTF-8">
<title>Title</title>
</head>
<div align="left" th:fragment="menu">
<p>会员管理</p>
<p>商品管理</p>
<p>销售管理</p>
</body>
</html>
②在另一个页面中引用自定义模板
main.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<meta charset="UTF-8">
<title>Title</title>
</head>
<div align="center">
<h2>插入模板 th:insert</h2>
<div th:insert="~{head :: top}">
第一种方式
<div th:insert="head :: top">
第二种方式
<h2>包含模板 th:include</h2>
<div th:include="~{head :: top}">
<div th:include="head :: top">
<h2>引用多级目录的模板文件</h2>
<div th:insert="common/left :: menu"></div>
<h2>引用整个模板文件</h2>
<div th:include="footer :: html"></div>
<div th:include="footer"></div>
</body>
</html>
③处理器方法
@RequestMapping("/useModel")
public String useModel() {
return "main";
④查看页面
查看页面源代码:
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Title</title>
</head>
<div align="center">
<h2>插入模板 th:insert</h2>
<div style="color: red">
<center>
www.upanddown.com
</center>
<div style="color: red">
<center>
www.upanddown.com
</center>
<h2>包含模板 th:include</h2>
<center>
www.upanddown.com
</center>
<center>
www.upanddown.com
</center>
<h2>引用多级目录的模板文件</h2>
<div align="left">
<p>会员管理</p>
<p>商品管理</p>
<p>销售管理</p>
<h2>引用整个模板文件</h2>
<meta charset="UTF-8">
<title>Title</title>
</head>
<div align="center">
www.upanddown.com
@copy; tsccg 2021
</body>
<div><!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Title</title>
</head>
<div align="center">
www.upanddown.com
@copy; tsccg 2021
</body>
</html>
</body>
</html>