Thymeleaf 是⾯向 Web 和独⽴环境的现代服务器端 Java 模板引擎,能够处理 HTML、XML、JavaScript、CSS 甚⾄纯⽂本。

Thymeleaf 特点

简单说,Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP。与其他的模板引擎相比较,它有如下三个极吸引人的特点。

  • Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 HTML 原型,然后在 HTML 标签里增加额外的属性来达到模板 + 数据的展示方式。浏览器解释 HTML 时会忽略未定义的标签属性,所以 Thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
  • Thymeleaf 开箱即用的特性。它支持标准方言和 Spring 方言,可以直接套用模板实现 JSTL、 OGNL 表达式效果,避免每天套模板、改 JSTL、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
  • Thymeleaf 提供 Spring 标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速地实现表单绑定、属性编辑器、国际化等功能。

 

常用语法

相关配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

在 application.properties 中添加配置:

spring.thymeleaf.cache=false

其中,propertiesspring.thymeleaf.cache=false 是关闭 Thymeleaf 的缓存,不然在开发过程中修改页面不会立刻生效需要重启,生产可配置为 true。

所有使用 Thymeleaf 的页面必须在 HTML 标签声明 Thymeleaf:

<html xmlns:th="http://www.thymeleaf.org">

赋值和拼接:

<p th:text="${userName}">neo</p>
<span th:text="'Welcome to our application, ' + ${userName} + '!'"></span>

字符串拼接还有另外一种简洁的写法:

<span th:text="|Welcome to our application, ${userName}!|"></span>

使用 ModelMap 以 KV 的方式存储传递到页面。

@RequestMapping("/string")
public String string(ModelMap map) {
    map.addAttribute("userName", "ityouknow");
    return "string";
}

条件判断 If/Unless

Thymeleaf 中使用 th:if 和 th:unless 属性进行条件判断,在下面的例子中,<a> 标签只有在 th:if 中条件成立时才显示:

<a th:if="${flag == 'yes'}"  th:href="@{http://favorites.ren/}"> home </a>
<a th:unless="${flag != 'no'}" th:href="@{http://www.ityouknow.com/}" >ityouknow</a>

th:unless 与 th:if 恰好相反,只有表达式中的条件不成立,才会显示其内容。

for 循环

按照键 users,传递到前端:

@RequestMapping("/list")
public String list(ModelMap map) {
    map.addAttribute("users", getUserList());
    return "list";
}

页面 list.html 进行数据展示:

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"></meta>
    <title>Example If/Unless </title>
</head>
<body>
<div >
    <h1>for 循环</h1>
    <table>
        <tr  th:each="user,iterStat : ${users}">
            <td th:text="${user.name}">neo</td>
            <td th:text="${user.age}">6</td>
            <td th:text="${user.pass}">213</td>
            <td th:text="${iterStat.index}">index</td>
        </tr>
    </table>
</div>
</body>
</html>

iterStat 称作状态变量,属性有:

  • index,当前迭代对象的 index(从 0 开始计算);
  • count,当前迭代对象的 index(从 1 开始计算);
  • size,被迭代对象的大小;
  • current,当前迭代变量;
  • even/odd,布尔值,当前循环是否是偶数/奇数(从 0 开始计算);
  • first,布尔值,当前循环是否是第一个;
  • last,布尔值,当前循环是否是最后一个。

URL

URL 在 Web 应用模板中占据着十分重要的地位,需要特别注意的是 Thymeleaf 对于 URL 的处理是通过语法@{...}来处理的。如果需要 Thymeleaf 对 URL 进行渲染,那么务必使用 th:href、th:src 等属性,下面是一个例子:

<a th:href="@{http://www.ityouknow.com/{type}(type=${type})}">link1</a>
<a th:href="@{http://www.ityouknow.com/{pageId}/can-use-springcloud.html(pageId=${pageId})}">view</a>

也可以使用@{...}设置背景:

<div th:style="'background:url(' + @{${img url}} + ');'">

几点说明:

  • 上例中 URL 最后的(pageId=${pageId})表示将括号内的内容作为 URL 参数处理,该语法避免使用字符串拼接,大大提高了可读性;
  • @{...}表达式中可以通过{pageId}访问 Context 中的 pageId 变量;
  • @{/order}是 Context 相关的相对路径,在渲染时会自动添加上当前 Web 应用的 Context 名字,假设 context 名字为 app,那么结果应该是 /app/order

三目运算

三目运算及表单显示:

<input th:value="${name}"/>
<input th:value="${age gt 30 ? '中年':'年轻'}"/>

说明:在表单标签中显示内容使用:th:value;${age gt 30 ? '中年':'年轻'}表示如果 age 大于 30 则显示中年,否则显示年轻。

  • gt:great than(大于)
  • ge:great equal(大于等于)
  • eq:equal(等于)
  • lt:less than(小于)
  • le:less equal(小于等于)
  • ne:not equal(不等于)

结合三目运算也可以将上面的 if else 改成这样:

<a th:if="${flag eq 'yes'}"  th:href="@{http://favorites.ren/}"> favorites </a>

 

switch 选择

switch\case 多用于多条件判断的场景下,以性别举例:

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"></meta>
    <title>Example switch </title>
</head>
<body>
<div >
    <div th:switch="${sex}">
        <p th:case="'woman'">她是一个姑娘...</p>
        <p th:case="'man'">这是一个爷们!</p>
        <!-- *: case的默认的选项 -->
        <p th:case="*">未知性别的一个家伙。</p>
    </div>
</div>
</body>
</html>

 *: case的默认的选项 

 

高阶用法

内联 [ [ ] ]

如果不想通过 th 标签而是简单地访问 model 对象数据,或是想在 javascript 代码块里访问 model 中的数据,则要使用内联的方法。

内联文本:[ [...] ] 内联文本的表示方式,使用时,必须先用在 th:inline="text/javascript/none" 激活,th:inline 可以在父级标签内使用,甚至可以作为 body 的标签。内联文本比 th:text 的代码少,不利于原型显示。

页面 inline.html(文本内联):

<div>
    <h1>内联</h1>
    <div th:inline="text" >
        <p>Hello, [[${userName}]] !</p>
        <br/>
    </div>
</div>

以上代码等价于:

<div>
    <h1>不使用内联</h1>
    <p th:text="'Hello, ' + ${userName} + ' !'"></p>
    <br/>
</div>

通过以上代码可以看出使用内联语法会更简洁一些。

如果想在脚本中使用后端传递的值,则必须使用脚本内联,脚本内联可以在 js 中取到后台传过来的参数:

<script th:inline="javascript">
    var name = [[${userName}]] + ', Sebastian';
    alert(name);
</script>

这段脚本的含义是在访问页面的时候,根据后端传值拼接 name 值,并以 alert 的方式弹框展示。

后端传值:

@RequestMapping("/inline")
public String inline(ModelMap map) {
    map.addAttribute("userName", "neo");
    return "inline";
}

页面会先跳出一个 alert 提示框,然后再展示使用内联和不使用内联的页面内容。

 

基本对象

Thymeleaf 包含了一些基本对象,可以用于我们的视图中,这些基本对象使用 # 开头。

  • #ctx:上下文对象
  • #vars:上下文变量
  • #locale:区域对象
  • #request:(仅 Web 环境可用)HttpServletRequest 对象
  • #response:(仅 Web 环境可用)HttpServletResponse 对象
  • #session:(仅 Web 环境可用)HttpSession 对象
  • #servletContext:(仅 Web 环境可用)ServletContext 对象

Thymeleaf 在 Web 环境中,有一系列的快捷方式用于访问请求参数、会话属性等应用属性,以其中几个常用的对象作为示例来演示。

  • #request:直接访问与当前请求关联的 javax.servlet.http.HttpServletRequest 对象;
  • #session:直接访问与当前请求关联的 javax.servlet.http.HttpSession 对象。

后台添加方法传值:

@RequestMapping("/object")
public String object(HttpServletRequest request) {
    request.setAttribute("request","i am request");
    request.getSession().setAttribute("session","i am session");
    return "object";
}

使用 request 和 session 分别传递了一个值,再来查看页面 object.html。

<body>
    <div >
        <h1>基本对象</h1>
        <p th:text="${#request.getAttribute('request')}">
        <br/>
        <p th:text="${session.session}"></p>
         Established locale country: <span th:text="${#locale.country}">CN</span>.
    </div>
</body>

启动项目后在浏览器中输入该网址:http://localhost:8080/object,则会出现下面的结果:

基本对象

i am request

i am session

Established locale country: CN.

第一个展示了 request 如何使用参数,第二行展示了 session 的使用,session 直接使用 . 即可获取到 session 中的值,最后展示了 locale 的用法。

内嵌变量

为了模板更加易用,Thymeleaf 还提供了一系列 Utility 对象(内置于 Context 中),可以通过 # 直接访问。

  • dates:java.util.Date 的功能方法类
  • calendars:类似 #dates,面向 java.util.Calendar
  • numbers:格式化数字的功能方法类
  • strings:字符串对象的功能类,contains、startWiths、prepending/appending 等
  • objects:对 objects 的功能类操作
  • bools:对布尔值求值的功能方法
  • arrays:对数组的功能类方法
  • lists:对 lists 的功能类方法
  • sets:set 的实用方法
  • maps:map 的实用方法

下面用一段代码来举例说明一些常用的方法,页面是 utility.html。

1. dates

可以使用 dates 对日期格式化,创建当前时间等操作。

<!--格式化时间-->
<p th:text="${#dates.format(date, 'yyyy-MM-dd HH:mm:ss')}">neo</p>
<!--创建当前时间 精确到天-->
<p th:text="${#dates.createToday()}">neo</p>
<!--创建当前时间 精确到秒-->
<p th:text="${#dates.createNow()}">neo</p>

2. strings

strings 内置了一些对字符串经常使用的函数。

<!--判断是否为空-->
<p th:text="${#strings.isEmpty(userName)}">userName</p>
<!--判断 list 是否为空-->
<p th:text="${#strings.listIsEmpty(users)}">userName</p>
<!--输出字符串长度-->
<p th:text="${#strings.length(userName)}">userName</p>
<!--拼接字符串-->
<p th:text="${#strings.concat(userName,userName,userName)}"></p>
<!--创建自定长度的字符串-->
<p th:text="${#strings.randomAlphanumeric(count)}">userName</p>

后端传值:

@RequestMapping("/utility")
public String utility(ModelMap map) {
    map.addAttribute("userName", "neo");
    map.addAttribute("users", getUserList());
    map.addAttribute("count", 12);
    map.addAttribute("date", new Date());
    return "utility";
}

启动项目后在浏览器中输入该网址:http://localhost:8080/utility,则会出现下面的结果:

内嵌变量

2018-09-26 20:49:07

Wed Sep 26 00:00:00 CST 2018

Wed Sep 26 20:49:07 CST 2018

false

[false, false, false]

3

neoneoneo

QKERBVPRHFS9

接下来总结一下 Thymeleaf 表达式。

表达式

表达式共分为以下五类。

  • 变量表达式:${...}
  • 选择或星号表达式:*{...}
  • 文字国际化表达式:#{...}
  • URL 表达式:@{...}
  • 片段表达式:~{...}

变量表达式

变量表达式即 OGNL 表达式或 Spring EL 表达式(在 Spring 术语中也叫 model attributes),类似 ${session.user.name}

它们将以 HTML 标签的一个属性来表示:

<span th:text="${book.author.name}">  
<li th:each="book : ${books}">

选择(星号)表达式

选择表达式很像变量表达式,不过它们用一个预先选择的对象来代替上下文变量容器(map)来执行,类似:*{customer.name}

被指定的 object 由 th:object 属性定义:

<div th:object="${book}">  
  ...  
  <span th:text="*{title}">...</span>  
  ...  
</div>

title 即为 book 的属性。

文字国际化表达式

文字国际化表达式允许我们从一个外部文件获取区域文字信息(.properties),用 Key 索引 Value,还可以提供一组参数(可选)。

#{main.title}  
#{message.entrycreated(${entryId})}

可以在模板文件中找到这样的表达式代码:

<table>  
  ...  
  <th th:text="#{header.address.city}">...</th>  
  <th th:text="#{header.address.country}">...</th>  
  ...  
</table>

URL 表达式

URL 表达式指的是把一个有用的上下文或回话信息添加到 URL,这个过程经常被叫做 URL 重写,比如@{/order/list}

  • URL 还可以设置参数:@{/order/details(id=${orderId})}
  • 相对路径:@{../documents/report}

让我们看这些表达式:

<form th:action="@{/createOrder}">  
<a href="main.html" th:href="@{/main}">

片段表达式

片段表达式是 3.x 版本新增的内容。片段表达式是一种标记的片段,并将其移动到模板中的方法。片段表达式的优势是,片段可以被复制或者作为参数传递给其他模板等。

最常见的用法是使用 th:insert 或 th:replace: 插入片段:

<div th:insert="~{commons :: main}">...</div>

也可以在页面的其他位置去使用:

<div th:with="frag=~{footer :: #main/text()}">
  <p th:insert="${frag}">
</div>

片段表达式可以有参数。

变量表达式和星号表达有什么区别

如果不考虑上下文的情况下,两者没有区别;星号语法是在选定对象上表达,而不是整个上下文。什么是选定对象?就是父标签的值,如下:

<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

这是完全等价于:

<div th:object="${session.user}">
  <p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>

当然,两种语法可以混合使用:

<div th:object="${session.user}">
  <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
      <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

表达式支持的语法

字面(Literals)

  • 文本文字(Text literals):'one text', 'Another one!',…
  • 数字文本(Number literals):0, 34, 3.0, 12.3,…
  • 布尔文本(Boolean literals):true, false
  • 空(Null literal):null
  • 文字标记(Literal tokens):one, sometext, main,…

文本操作(Text operations)

  • 字符串连接(String concatenation):+
  • 文本替换(Literal substitutions):|The name is ${name}|

算术运算(Arithmetic operations)

  • 二元运算符(Binary operators):+, -, *, /, %
  • 减号(单目运算符)Minus sign(unary operator):-

布尔操作(Boolean operations)

  • 二元运算符(Binary operators):and, or
  • 布尔否定(一元运算符)Boolean negation (unary operator):!, not

比较和等价(Comparisons and equality)

  • 比较(Comparators):>, <, >=, <= (gt, lt, ge, le)
  • 等值运算符(Equality operators):==, != (eq, ne)

条件运算符(Conditional operators)

  • If-then:(if) ? (then)
  • If-then-else:(if) ? (then) : (else)
  • Default: (value) ?:(defaultvalue)

所有这些特征可以被组合并嵌套:

'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

常用 th 标签

页面常用的 HTML 标签几乎都有 Thymeleaf 对应的 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:删除包含标签和所有的子节点;

2.body:不包含标记删除,但删除其所有的子节点;

3.tag:包含标记的删除,但不删除它的子节点;

4.all-but-first:删除所有包含标签的子节点,除了第一个。

5.none:什么也不做。这个值是有用的动态评估

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。

Thymeleaf 配置

我们可以通过 application.properties 文件灵活的配置 Thymeleaf 的各项特性,以下为 Thymeleaf 的配置和默认参数:

# THYMELEAF (ThymeleafAutoConfiguration)
#开启模板缓存(默认值:true)
spring.thymeleaf.cache=true 
#检查模板是否存在,然后再呈现
spring.thymeleaf.check-template=true 
#检查模板位置是否正确(默认值:true)
spring.thymeleaf.check-template-location=true
#Content-Type的值(默认值:text/html)
spring.thymeleaf.content-type=text/html
#开启MVC Thymeleaf视图解析(默认值:true)
spring.thymeleaf.enabled=true
#模板编码
spring.thymeleaf.encoding=UTF-8
#要被排除在解析之外的视图名称列表,用逗号分隔
spring.thymeleaf.excluded-view-names=
#要运用于模板之上的模板模式。另见StandardTemplate-ModeHandlers(默认值:HTML5)
spring.thymeleaf.mode=HTML5
#在构建URL时添加到视图名称前的前缀(默认值:classpath:/templates/)
spring.thymeleaf.prefix=classpath:/templates/
#在构建URL时添加到视图名称后的后缀(默认值:.html)
spring.thymeleaf.suffix=.html
#Thymeleaf 模板解析器在解析器链中的顺序,默认情况下,它排第一位,顺序从1开始,只有在定义了额外的 TemplateResolver Bean 时才需要设置这个属性。
spring.thymeleaf.template-resolver-order=
#可解析的视图名称列表,用逗号分隔
spring.thymeleaf.view-names=

在实际项目中可以根据实际使用情况来修改。