thymeleaf 是新一代的模板引擎,在spring4.0中推荐使用thymeleaf来做前端模版引擎。
thymeleaf介绍
简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的Java模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点:
- 1.Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它遵从web标准,支持 HTML 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
- 2.Thymeleaf 开箱即用的特性,语法优雅易懂。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
- 3.Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
标准表达式语法
它们分为四类:
- 1.变量表达式
- 2.选择或星号表达式
- 3.文字国际化表达式
- 4.URL表达式
变量表达式
变量表达式即OGNL表达式或Spring EL表达式(在Spring术语中也叫model attributes)。如下所示:${session.user.name}
它们将以HTML标签的一个属性来表示:
<span th:text="${book.author.name}">
选择(星号)表达式
选择表达式很像变量表达式,不过它用一个当前选择的对象来代替整个上下文变量映射来执行,如下:*{customer.name}
被指定的object由th:object属性定义:
<div th:object="${book}">
...
<span th:text="*{title}">...</span>
...
</div>
文字国际化(外部化,i8n,消息)表达式
文字国际化表达式允许我们从一个外部文件获取区域文字信息(.properties),用Key索引Value,还可以提供一组参数(可选).
#{main.title}
#{message.entrycreated(${entryId})}
可以在模板文件中找到这样的表达式代码:
<table>
...
<th th:text="#{header.address.city}">...</th>
<th th:text="#{header.address.country}">...</th>
...
</table>
<table>
...
<th th:text="#{header.address.city}">...</th>
<th th:text="#{header.address.country}">...</th>
...
</table>
URL链接表达式
指的是把一个有用的上下文或回话信息添加到URL,这个过程经常被叫做URL重写。@{/order/list}
URL还可以设置参数: @{/order/details(id=${orderId})}
绝对路径: http://www.thymeleaf.org
让我们看这些表达式:
<form th:action="@{/createOrder}">
<a href="main.html" th:href="@{/main}">```
<form th:action="@{/createOrder}">
<a href="main.html" th:href="@{/main}">
分段表达式
变量表达式和星号表达有什么区别吗?
如果不考虑上下文的情况下,两者没有区别;星号语法评估在选定对象上表达,而不是整个上下文
什么是选定对象?就是父标签的值,如下:
<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):
<p> Now you are looking at a <span th:text="'working web application'">template file</span>. </p>
- 数字文本(Number literals):
<p>The year is <span th:text="2013">1492</span>.</p> <p>In two years, it will be <span th:text="2013 + 2">1494</span>.</p>
- 布尔文本(Boolean literals):
<div th:if="${user.isAdmin()} == false"> ...
- 空(Null literal):
<div th:if="${variable.something} == null"> ...
- 文字标记(Literal tokens):
<div th:class="content">...</div>
文本操作(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)
xml文件禁用<,>
等值运算符(Equality operators):==, != (eq, ne)
<div th:if="${prodStat.count} > 1"> <span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')">
条件运算符(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标签
还有非常多的标签,这里只列出最常用的几个,由于一个标签内可以包含多个th:x属性,其生效的优先级顺序为:
include,each,if/unless/switch/case,with,attr/attrprepend/attrappend,value/href,src ,etc,text/utext,fragment,remove。
设置属性值
1 Thymeleaf设置任何属性的值
<form action="subscribe.html">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="Subscribe!" />
</fieldset>
</form>
和Thymeleaf一样,这个模板更像一个静态的原型,而不是一个Web应用程序的模板。首先,action我们表单中的属性静态链接到模板文件本身,这样就没有有用的URL重写的地方。其次,value提交按钮中的属性使其显示英文文本,但我们希望它是国际化的。
然后输入th:attr属性,以及更改其设置标签的属性值的能力:
<form action="subscribe.html" th:attr="action=@{/subscribe}">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
</fieldset>
</form>
这个概念非常简单:th:attr只需要一个为属性赋值的表达式。创建了相应的控制器和消息文件后,处理这个文件的结果是:
<form action="/gtvg/subscribe">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="¡Suscríbe!"/>
</fieldset>
</form>
除了新的属性值之外,还可以看到应用程序上下文名称已经被自动添加到/gtvg/subscribe前面章节中介绍的URL基址的前面。
但是,如果我们想一次设置多个属性呢?XML规则不允许您在标签中设置两次属性,因此th:attr会使用逗号分隔的分配列表,如下所示:
<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
给定所需的消息文件,这将输出:
<img src="/gtgv/images/gtvglogo.png" title="Logo de Good Thymes" alt="Logo de Good Thymes" />
2 Thymeleaf将值设置为特定的属性
你可能会想这样:<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
是一个非常丑陋的标记。在一个属性值中指定一个赋值可能是非常实用的,但是如果你必须一直这样做的话,它不是创建模板的最优雅的方法。
Thymeleaf与你一致,这就是为什么th:attr在模板中很少使用。通常情况下,您将使用其他th:*任务设置特定标签属性的属性(而不仅仅是任何属性th:attr)
例如,要设置value属性,请使用th:value:
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
看起来好多了,让我们试着action对form标签中的属性做同样的事情:
<form action="subscribe.html" th:action="@{/subscribe}">
你还记得th:href我们home.html之前放的那些吗?他们正是这种属性:
<li>
<a href="product/list.html" th:href="@{/product/list}">
Product List
</a>
</li>
3 Thymeleaf固定值布尔属性
HTML具有布尔属性的概念,没有值的属性和一个意味着值是“真”的属性
例如checked:
<input type="checkbox" name="option2" checked /> <!-- HTML -->
<input type="checkbox" name="option1" checked="checked" /> <!-- XHTML -->
标准方言包含的属性允许您通过评估一个条件来设置这些属性,以便如果评估为true,则该属性将被设置为其固定值,如果评估为false,则该属性将不会被设置:
<input type="checkbox" name="active" th:checked="${user.active}" />
Thymeleaf迭代循环
迭代基础
th:each
java.util.List类不是唯一可用于Thymeleaf迭代的值。有一组相当完整的对象被认为可以被迭代
1、任何对象的实现 java.util.Iterable
2、任何对象的实现java.util.Enumeration。
3、任何实现的对象java.util.Iterator,其值将被迭代器返回使用,而不需要缓存内存中的所有值。
4、任何对象的实现java.util.Map。当迭代映射时,iter变量将是类的java.util.Map.Entry。
5、任何数组。
6、任何其他对象将被视为包含对象本身的单值列表。
状态变量
使用时th:each,Thymeleaf提供了一个有用的机制来跟踪迭代状态:状态变量。
状态变量在一个th:each属性中定义并包含以下数据:
. 当前迭代索引,从0开始。这是index属性。
. 当前迭代索引,从1开始。这是count属性。
. 迭代变量中的元素总数。这是size财产。
. 每个迭代的iter变量。这是current财产。
. 目前的迭代是偶数还是奇数。这些是even/odd布尔属性。
. 目前的迭代是否是第一个。这是first布尔属性。
. 目前的迭代是否是最后一个。这是last布尔属性。
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
</table>
状态变量(iterStat在本例中)是th:each通过在iter变量本身之后写入名称来定义的,用逗号分隔。就像iter变量一样,状态变量的范围也是由持有th:each属性的标签定义的代码片段。
条件语句
有时,如果满足某个条件,则需要模板的一部分才能显示在结果中。
例如,想象一下,我们希望在产品表中显示每个产品存在的评论数量的列,如果有任何评论,则可以链接到该产品的评论详细信息页面。
为了做到这一点,我们将使用th:if属性:
如果值不为空:
. 如果value是一个布尔值并且是true。
. 如果值是一个数字并且是非零的
. 如果值是一个字符,并且是非零的
. 如果value是一个String而不是“false”,“off”或“no”
. 如果值不是布尔值,数字,字符或字符串。
(如果值为null,则th:如果将评估为false)。
此外,th:if还有一个反向属性,th:unless我们可以在前面的示例中使用它,而不是not在OGNL表达式中使用:
还有一种方法可以在Java中使用相当于开关结构的有条件显示内容:th:switch/ th:case属性集。
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
</div>
请注意,只要一个th:case属性被评估为true,th:case同一交换机上下文中的每一个其他属性都被评估为false。
默认选项被指定为th:case="*":
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>