4 设置属性值
5 迭代器(th:each
)
5.1 基本的迭代
th:each
将循环 array 或 list 中的元素并重复打印一组标签,语法相当于 Java foreach 表达式:
<li th:each="book : ${books}" th:text="${book.title}">En las Orillas del Sar</li>
可以使用th:each
属性进行遍历的对象包括:
- 任何实现
java.util.Iterable
的对象 - 任何实现
java.util.Enumeration
的对象 - 任何实现
java.util.Iterator
的对象,其值将被迭代器返回,而不需要在内存中缓存所有的值 - 任何实现
java.util.Map
的对象。 迭代映射时,迭代变量 将是java.util.Map.Entry
类 - 任何数组
- 任何其他对象将被视为包含对象本身的单值列表
5.2 状态变量
Thymeleaf 提供 状态变量(status variable) 来跟踪迭代器的状态。
th:each
属性中,定义了如下状态变量:
index
属性是当前 迭代器索引(iteration index),从0开始count
属性是当前 迭代器索引(iteration index),从1开始size
属性是迭代器元素的总数current
是当前 迭代变量(iter variable)even/odd
判断当前迭代器是否是 even 或 oddfirst
判断当前迭代器是否是第一个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
中定义了。
我们来看看模板的处理后的结果:
<!DOCTYPE html>
<html>
<head>
<title>Good Thymes Virtual Grocery</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
</head>
<body>
<h1>Product list</h1>
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr class="odd">
<td>Fresh Sweet Basil</td>
<td>4.99</td>
<td>yes</td>
</tr>
<tr>
<td>Italian Tomato</td>
<td>1.25</td>
<td>no</td>
</tr>
<tr class="odd">
<td>Yellow Bell Pepper</td>
<td>2.50</td>
<td>yes</td>
</tr>
<tr>
<td>Old Cheddar</td>
<td>18.75</td>
<td>yes</td>
</tr>
</table>
<p>
<a href="/gtvg/" shape="rect">Return to home</a>
</p>
</body>
</html>
请注意,我们的迭代状态变量已经运行良好,建立只有奇数行具有 “odd” CSS 类。
如果您没有明确设置状态变量,则 Thymeleaf 将始终创建一个状态变量,可以通过后缀“Stat”获取到迭代变量的名称:
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.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>
5.3 示例
(1)Student
实体
package com.haan.thymeleafstudy.entity;
import lombok.Data;
@Data
public class Student {
private String name;
private Integer age;
private String message;
}
(2)控制器
package com.haan.thymeleafstudy;
import com.haan.thymeleafstudy.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.List;
@Controller
public class IteratorController {
@RequestMapping("/test001")
public String testIterator(Model model){
List<Student> students = new ArrayList<Student>();
Student stu1 = new Student();
stu1.setName("libai");
stu1.setAge(12);
stu1.setMessage("众人皆醒我独醉");
students.add(stu1);
Student stu2 = new Student();
stu2.setName("llw");
stu2.setAge(12);
stu2.setMessage("开渔节苦于温热人发帖人");
students.add(stu2);
Student stu3 = new Student();
stu3.setName("smlz");
stu3.setAge(12);
stu3.setMessage("is我的速进个非常有好处");
students.add(stu3);
System.out.println(students);
model.addAttribute("students",students);
return "iterator";
}
}
(2)Thymeleaf 模板
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>迭代器</title>
</head>
<body>
<h3>Thymeleaf迭代器(th:each)学习</h3>
<div th:each="student:${students}">
学生名称:<span th:text="${student.name}">111111</span><br>
学生年龄:<span th:text="${student.age}">111111</span><br>
个性签名:<span th:text="${student.message}">111111</span><br>
状态变量index:<span th:text="${studentStat.index}">111111</span><br>
状态变量count:<span th:text="${studentStat.count}">111111</span><br>
状态变量size:<span th:text="${studentStat.size}">111111</span><br>
状态变量current:<span th:text="${studentStat.current.getMessage()}">111111</span><br>
状态变量odd/even:<span th:text="${studentStat.odd}">111111</span><br>
状态变量first:<span th:text="${studentStat.first}">111111</span><br>
状态变量last:<span th:text="${studentStat.last}">111111</span><br>
<hr>
</div>
</body>
</html>
(4)效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>迭代器</title>
</head>
<body>
<h3>Thymeleaf迭代器(th:each)学习</h3>
<div>
学生名称:<span>libai</span><br>
学生年龄:<span>12</span><br>
个性签名:<span>众人皆醒我独醉</span><br>
状态变量index:<span>0</span><br>
状态变量count:<span>1</span><br>
状态变量size:<span>3</span><br>
状态变量current:<span>众人皆醒我独醉</span><br>
状态变量odd/even:<span>true</span><br>
状态变量first:<span>true</span><br>
状态变量last:<span>false</span><br>
<hr>
</div>
<div>
学生名称:<span>llw</span><br>
学生年龄:<span>12</span><br>
个性签名:<span>开渔节苦于温热人发帖人</span><br>
状态变量index:<span>1</span><br>
状态变量count:<span>2</span><br>
状态变量size:<span>3</span><br>
状态变量current:<span>开渔节苦于温热人发帖人</span><br>
状态变量odd/even:<span>false</span><br>
状态变量first:<span>false</span><br>
状态变量last:<span>false</span><br>
<hr>
</div>
<div>
学生名称:<span>smlz</span><br>
学生年龄:<span>12</span><br>
个性签名:<span>is我的速进个非常有好处</span><br>
状态变量index:<span>2</span><br>
状态变量count:<span>3</span><br>
状态变量size:<span>3</span><br>
状态变量current:<span>is我的速进个非常有好处</span><br>
状态变量odd/even:<span>true</span><br>
状态变量first:<span>false</span><br>
状态变量last:<span>true</span><br>
<hr>
</div>
</body>
</html>
6 条件语句
6.1 th:if
和 th:unless
th:if
属性用法如下:
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
请注意,th:if
属性不仅是将评估布尔条件。 它的功能有点超出这一点,它将按照这些规则评估指定的表达式:
- 如果值不为 null:
- 如果值为布尔值,则为true。
- 如果值是数字,并且不为零
- 如果值是一个字符且不为零
- 如果value是String,而不是“false”,“off”或“no”
- 如果值不是布尔值,数字,字符或字符串。
- 如果值为null,则th:if 将为 false。
另外,th:if
有一个相反的属性th:unless
,前面的例子改为:
<a href="comments.html"
th:href="@{/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
6.2 th:switch
语句
switch 语句使用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’,每个其他同一个 switch 语句中的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>
6.3 示例
(1)Person
实体
package com.haan.thymeleafstudy.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
private String role;
private Boolean married;
}
(2)控制器
package com.haan.thymeleafstudy.controller;
import com.haan.thymeleafstudy.entity.Person;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.List;
@Controller
public class ConditionController {
@RequestMapping("/test002")
public String test002(Model model){
List<Person> persons = new ArrayList<>();
Person person1 = new Person("libai",22,"teacher",true);
persons.add(person1);
Person person2 = new Person("zhangliang",22,"doctor",false);
persons.add(person2);
Person person3 = new Person("sdj",21,"student",true);
persons.add(person3);
model.addAttribute("persons",persons);
return "conditional";
}
}
(3)Thymeleaf 模板
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>IF 测试一</h3>
<div th:each="person:${persons}">
<div th:if="${person.age>21}">
姓名:<span th:text="${person.name}">111111</span><br>
年龄:<span th:text="${person.age}">111111</span><br>
角色:<span th:text="${person.role}">111111</span><br>
已婚:<span th:text="${person.married}">111111</span><br>
<hr>
</div>
</div>
<h3>IF 测试二</h3>
<div th:each="persion:${persons}">
<div th:if="${person.married}">
姓名:<span th:text="${person.name}">111111</span><br>
年龄:<span th:text="${person.age}">111111</span><br>
角色:<span th:text="${person.role}">111111</span><br>
已婚:<span th:text="${person.married}">111111</span><br>
<hr>
</div>
</div>
<h3>IF 测试三</h3>
<div th:each="person:${persons}">
<div th:if="${person.married && person.age>21 }">
姓名:<span th:text="${person.name}">111111</span><br>
年龄:<span th:text="${person.age}">111111</span><br>
角色:<span th:text="${person.role}">111111</span><br>
已婚:<span th:text="${person.married}">111111</span><br>
<hr>
</div>
</div>
<h3>IF 测试四</h3>
<div th:each="person:${persons}">
<div th:unless="${person.married && person.age>21 }">
姓名:<span th:text="${person.name}">111111</span><br>
年龄:<span th:text="${person.age}">111111</span><br>
角色:<span th:text="${person.role}">111111</span><br>
已婚:<span th:text="${person.married}">111111</span><br>
<hr>
</div>
</div>
<h3>SWITCH 测试一</h3>
<div th:each="person:${persons}">
<div th:switch="${person.role}">
姓名:<span th:text="${person.name}">111111</span><br>
年龄:<span th:text="${person.age}">111111</span><br>
角色:
<span th:case="teacher">老师</span>
<span th:case="doctor">医生</span>
<span th:case="*">其他</span>
<br>
已婚:<span th:text="${person.married}">111111</span><br>
<hr>
</div>
</div>
</body>
</html>
(4)效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>IF 测试一</h3>
<div>
<div>
姓名:<span>libai</span><br>
年龄:<span>22</span><br>
角色:<span>teacher</span><br>
已婚:<span>true</span><br>
<hr>
</div>
</div>
<div>
<div>
姓名:<span>zhangliang</span><br>
年龄:<span>22</span><br>
角色:<span>doctor</span><br>
已婚:<span>false</span><br>
<hr>
</div>
</div>
<div>
</div>
<h3>IF 测试二</h3>
<div>
<div>
姓名:<span>libai</span><br>
年龄:<span>22</span><br>
角色:<span>teacher</span><br>
已婚:<span>true</span><br>
<hr>
</div>
</div>
<div>
</div>
<div>
<div>
姓名:<span>sdj</span><br>
年龄:<span>21</span><br>
角色:<span>student</span><br>
已婚:<span>true</span><br>
<hr>
</div>
</div>
<h3>IF 测试三</h3>
<div>
<div>
姓名:<span>libai</span><br>
年龄:<span>22</span><br>
角色:<span>teacher</span><br>
已婚:<span>true</span><br>
<hr>
</div>
</div>
<div>
</div>
<div>
</div>
<h3>IF 测试四</h3>
<div>
</div>
<div>
<div>
姓名:<span>zhangliang</span><br>
年龄:<span>22</span><br>
角色:<span>doctor</span><br>
已婚:<span>false</span><br>
<hr>
</div>
</div>
<div>
<div>
姓名:<span>sdj</span><br>
年龄:<span>21</span><br>
角色:<span>student</span><br>
已婚:<span>true</span><br>
<hr>
</div>
</div>
<h3>SWITCH 测试一</h3>
<div>
<div>
姓名:<span>libai</span><br>
年龄:<span>22</span><br>
角色:
<span>老师</span>
<br>
已婚:<span>true</span><br>
<hr>
</div>
</div>
<div>
<div>
姓名:<span>zhangliang</span><br>
年龄:<span>22</span><br>
角色:
<span>医生</span>
<br>
已婚:<span>false</span><br>
<hr>
</div>
</div>
<div>
<div>
姓名:<span>sdj</span><br>
年龄:<span>21</span><br>
角色:
<span>其他</span>
<br>
已婚:<span>true</span><br>
<hr>
</div>
</div>
</body>
</html>
7 模板布局
7.1 包含模板片段
7.1.1 定义和引用片段
在我们的模板中,我们经常需要从其他模板中添加html页面片段,如页脚、标题、菜单…
为了做到这一点,Thymeleaf 需要我们来定义这些“片段”,可以使用th:fragment
属性来完成。我们定义了/WEB-INF/templates/footer.html
页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="copy">
© 2017 <a href="https://waylau.com">waylau.com</a>
</div>
</body>
</html>
如果,我们想引用这个 copy
代码片段,我们可以用 th:insert
或 th:replace
属性 (th:include
也是可以,但自 Thymeleaf 3.0 以来就不再推荐):
<body>
...
<div th:insert="~{footer :: copy}"></div>
</body>
注意th:insert
需要一个片段表达式(〜{...}
)。 在上面的例子中,非复杂片段表达式,(〜{
,}
)包围是完全可选,所以上面的代码将等效于:
<body>
...
<div th:insert="footer :: copy"></div>
</body>
7.1.2 片段规范语法
"~{templatename::selector}"
名为templatename
的模板上的指定标记选择器。selector
可以只是一个片段名。"~{templatename}"
: 包含完整的模版templatename
~{::selector}"
or"~{this::selector}"
相同模版中的代码片段
7.1.3 不使用 th:fragment
不使用 th:fragment
也可以引用HTML片段,比如:
...
<div id="copy-section">
© 2017 <a href="https://waylau.com">waylau.com</a>
</div>
...
通过 id 也可以引用到页面片段:
<body>
...
<div th:insert="~{footer :: #copy-section}"></div>
</body>
7.1.4 th:insert
、 th:replace
、th:include
三者区别
th:insert
是最简单的:它将简单地插入指定的片段作为正文 的主标签。th:replace
用指定实际片段来替换其主标签。th:include
类似于th:insert
,但不是插入片段它只插入此片段的内容。
所以
<footer th:fragment="copy">
© 2017 <a href="https://waylau.com">waylau.com</a>
</footer>
三种方式引用该片段
<body>
...
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>
</body>
结果为:
<body>
...
<div>
<footer>
© 2017 <a href="https://waylau.com">waylau.com</a>
</footer>
</div>
<footer>
© 2017 <a href="https://waylau.com">waylau.com</a>
</footer>
<div>
© 2017 <a href="https://waylau.com">waylau.com</a>
</div>
</body>
7.1.5 示例
(1)控制器
package com.haan.thymeleafstudy.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TmlLatoutController {
@RequestMapping("/test003")
public String test003(){
return "tpllayout01";
}
}
(2)Thymeleaf 模板
// footer.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h2>这是一个footer模板</h2>
<footer th:fragment="foot">
© 2019 <a href="https://baidu.com">百度一下</a>
</footer>
</div>
<footer id="footId">
© 2019 <a href="https://baidu.com">百度一下002</a>
</footer>
<footer th:fragment="foot">
© 2009 <a href="https://baidu.com">百度一下003</a>
</footer>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>测试insert、replace和include的区别</h3>
<div th:include="~{footer::foot}"></div>
<hr>
<div th:insert="~{footer::foot}"></div>
<hr>
<div th:replace="~{footer::foot}"></div>
<h3>测试其他</h3>
<div th:insert="~{footer.html::foot}"></div>
<hr>
<div th:insert="footer::foot"></div>
<hr>
<div th:insert="footer"></div>
<hr>
<div th:replace="~{footer::#footId}"></div>
<hr>
<div th:insert="~{::foot}"></div>
<div>
<h2>这是一个footer模板004</h2>
<footer th:fragment="foot">
© 2019 <a href="https://baidu.com">百度一下004</a>
</footer>
</div>
</body>
</html>
(3)执行效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>测试insert、replace和include的区别</h3>
<div>
© 2019 <a href="https://baidu.com">百度一下</a>
© 2009 <a href="https://baidu.com">百度一下003</a>
</div>
<hr>
<div><footer>
© 2019 <a href="https://baidu.com">百度一下</a>
</footer><footer>
© 2009 <a href="https://baidu.com">百度一下003</a>
</footer></div>
<hr>
<footer>
© 2019 <a href="https://baidu.com">百度一下</a>
</footer><footer>
© 2009 <a href="https://baidu.com">百度一下003</a>
</footer>
<h3>测试其他</h3>
<div><footer>
© 2019 <a href="https://baidu.com">百度一下</a>
</footer><footer>
© 2009 <a href="https://baidu.com">百度一下003</a>
</footer></div>
<hr>
<div><footer>
© 2019 <a href="https://baidu.com">百度一下</a>
</footer><footer>
© 2009 <a href="https://baidu.com">百度一下003</a>
</footer></div>
<hr>
<div><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h2>这是一个footer模板</h2>
<footer>
© 2019 <a href="https://baidu.com">百度一下</a>
</footer>
</div>
<footer id="footId">
© 2019 <a href="https://baidu.com">百度一下002</a>
</footer>
<footer>
© 2009 <a href="https://baidu.com">百度一下003</a>
</footer>
</body>
</html></div>
<hr>
<footer id="footId">
© 2019 <a href="https://baidu.com">百度一下002</a>
</footer>
<hr>
<div><footer>
© 2019 <a href="https://baidu.com">百度一下004</a>
</footer></div>
<div>
<h2>这是一个footer模板</h2>
<footer>
© 2019 <a href="https://baidu.com">百度一下004</a>
</footer>
</div>
</body>
</html>
7.2 可参数化片段
声明片段的时候可以声明变量参数,在片段中使用变量参数
<div th:fragment="frag (onevar,twovar)">
<p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>
引入片段的时候把参数的值,传入进来
两种书写方式(语法):
(1)如果,可以直接写参数,按顺序一一对应
(2)如果,可以指定变量名,对应的值,此时安装变量名进行参数对应,与顺序无关
<div th:replace="::frag (${value1},${value2})">...</div>
<div th:replace="::frag (onevar=${value1},twovar=${value2})">...</div>
<div th:replace="::frag (twovar=${value2},onevar=${value1})">...</div>
7.2.1 不带片段参数的片段局部变量
即使片段定义时没有参数
<div th:fragment="frag">
...
</div>
我们可以使用上面指定的第二种语法来调用它们(并且只有第二种语法):
<div th:replace="::frag (onevar=${value1},twovar=${value2})">
这相当于th:replace
和th:with
的组合:
<div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">
请注意,无论片段是否有参数签名,这种局部变量的规范都不会导致上下文在执行之前被清空。片段仍然能够像当前一样访问调用模板中使用的每个上下文变量。
7.2.2 th:assert
对于in-template
声明
th:assert
属性可以指定一个逗号分隔的表达式列表,这些表达式应该被求值,并且每次求值要求生成true,否则会引发异常。
<div th:assert="${onevar},(${twovar} != 43)">...</div>
这对于在片段签名中验证参数是很方便的:
<header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>
7.3 灵活的布局:不仅仅是片段插入
由于⽚段表达式的强⼤功能,我们不仅可以为⽚段指定⽂本类型,数字类型,bean对象类型的参数,还可以指定标记⽚段作为参数。
这允许我们以⼀种⽅式创建我们的⽚段,使得它们可以调⽤模板的标记,从⽽产⽣⾮常灵活的模板布局机制。
请注意在下⾯的⽚段中使⽤标题和链接变量:
<head th:fragment="common_header(title,links)">
<title th:replace="${title}">The awesome application</title>
<!-- Common styles and scripts -->
<link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}">
<link rel="shortcut icon" th:href="@{/images/favicon.ico}">
<script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script>
<!--/* Per-page placeholder for additional links */-->
<th:block th:replace="${links}" />
</head>
我们像这样调⽤这个⽚段:
...
<head th:replace="base :: common_header(~{::title},~{::link})">
<title>Awesome - Main</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
</head>
...
结果将使⽤调⽤模板中的实际和标签作为标题和链接变量的值,导致我们的⽚段在插⼊期间被定制化:
<head>
<title>Awesome - Main</title>
<!-- Common styles and scripts -->
<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
<link rel="stylesheet" href="/awe/css/bootstrap.min.css">
<link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css">
</head>
7.3.1 使用空片段
⼀个特殊的⽚段表达式,空的⽚段(〜{})可以⽤于指定没有标记。 使⽤前⾯的例⼦:
<head th:replace="base :: common_header(~{::title},~{})">
<title>Awesome - Main</title>
</head>
...
注意⽚段(链接)的第⼆个参数如何设置为空⽚段,因此没有为<th:blockth:replace =“$ {links}”/>
块写⼊:
...
<head>
<title>Awesome - Main</title>
<!-- Common styles and scripts -->
<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
</head>
...
7.3.2 使用无操作符
如果我们只想让我们的⽚段将其当前标记⽤作默认值,那么no-op
也可以⽤作⽚段的参数。 再次,使⽤common_header
示例:
...
<head th:replace="base :: common_header(_,~{::link})">
<title>Awesome - Main</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
</head>
...
看看title
参数(commonheader⽚段的第⼀个参数)如何设置为`no-op(),这导致⽚段的这⼀部分不被执⾏
(title = no-operation)`:
<title th:replace="${title}">The awesome application</title>
结果如下:
...
<head>
<title>The awesome application</title>
<!-- Common styles and scripts -->
<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
<link rel="stylesheet" href="/awe/css/bootstrap.min.css">
<link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css">
</head>
...
7.33 高级条件插入片段
空⽚段和⽆操作⽚段允许我们以⾮常简单和优雅的⽅式执⾏⽚段的条件插⼊。
例如,我们可以这样做,以便只有当⽤户是管理员时插⼊我们的common:: adminhead
⽚段,如果不是管理员插⼊空⽚段:
...
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}">...</div>
...
另外,只有满⾜指定的条件,我们才可以使⽤无操作符来插⼊⽚段,但是如果不满⾜条件,则不需要修改则保留标记:
...
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : _">
Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support.
</div>
...
另外,如果我们配置了我们的模板解析器来检查模板资源的存在 - 通过它们的checkExistence
标志,我们可以使⽤⽚段本身的存在作为默认操作的条件
...
<!-- The body of the <div> will be used if the "common :: salutation" fragment -->
<!-- does not exist (or is empty). -->
<div th:insert="~{common :: salutation} ?: _">
Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support.
</div>
...
7.4 删除模版⽚段
回到我们的示例应⽤程序,让我们回顾⼀下我们的产品列表模板的最后⼀个版本:
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.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>
<td>
<span th:text="${#lists.size(prod.comments)}">2</span> comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
</table>
这个代码只是⼀个模板,但是作为⼀个静态⻚⾯(当没有Thymeleaf的浏览器直接打开它时),它不会成为⼀个很好的原型。
为什么? 因为尽管浏览器完全可以显示,该表只有⼀⾏,⽽这⾏有模拟数据。 作为⼀个原型,它根本看起来不够现实,我们应该有多个产品,我们需要更多的⾏。所以我们来补充⼀下:
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.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>
<td>
<span th:text="${#lists.size(prod.comments)}">2</span> comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
<tr class="odd">
<td>Blue Lettuce</td>
<td>9.55</td>
<td>no</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr>
<td>Mild Cinnamon</td>
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="comments.html">view</a>
</td>
</tr>
</table>
好的,现在我们有三个产品,肯定是⼀个好的原型。 但是,当我们⽤Thymeleaf
处理它会发⽣什么?
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr>
<td>Fresh Sweet Basil</td>
<td>4.99</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr class="odd">
<td>Italian Tomato</td>
<td>1.25</td>
<td>no</td>
<td>
<span>2</span> comment/s
<a href="/gtvg/product/comments?prodId=2">view</a>
</td>
</tr>
<tr>
<td>Yellow Bell Pepper</td>
<td>2.50</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr class="odd">
<td>Old Cheddar</td>
<td>18.75</td>
<td>yes</td>
<td>
<span>1</span> comment/s
<a href="/gtvg/product/comments?prodId=4">view</a>
</td>
</tr>
<tr class="odd">
<td>Blue Lettuce</td>
<td>9.55</td>
<td>no</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr>
<td>Mild Cinnamon</td>
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="comments.html">view</a>
</td>
</tr>
</table>
最后两⾏是模拟数据⾏! 那么当然它们是:迭代只适⽤于第⼀⾏,所以没有什么理由为什么Thymeleaf应该删除另外两个。
我们需要⼀种在模板处理过程中删除这两⾏的⽅法。 我们使⽤第⼆和第三个<tr>
标签中的th:remove
属性:
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.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>
<td>
<span th:text="${#lists.size(prod.comments)}">2</span> comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
<tr class="odd" th:remove="all">
<td>Blue Lettuce</td>
<td>9.55</td>
<td>no</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr th:remove="all">
<td>Mild Cinnamon</td>
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="comments.html">view</a>
</td>
</tr>
</table>
⼀旦处理,⼀切看起来应该是:
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr>
<td>Fresh Sweet Basil</td>
<td>4.99</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr class="odd">
<td>Italian Tomato</td>
<td>1.25</td>
<td>no</td>
<td>
<span>2</span> comment/s
<a href="/gtvg/product/comments?prodId=2">view</a>
</td>
</tr>
<tr>
<td>Yellow Bell Pepper</td>
<td>2.50</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr class="odd">
<td>Old Cheddar</td>
<td>18.75</td>
<td>yes</td>
<td>
<span>1</span> comment/s
<a href="/gtvg/product/comments?prodId=4">view</a>
</td>
</tr>
</table>
这个属性中的all
值是什么意思?th:remove
可以有五种不同的删除⽅式,
具体取决于它的值:
- all:删除包含标签及其所有⼦项。
- body:不要删除包含的标签,但删除所有的⼦项。
- tag:删除包含的标签,但不要删除其⼦项。
- all-but-first:删除除第⼀个包含标签之外的所有⼦代。
- none:什么都不做。 该值对于动态计算是有⽤的。
示例:
(1)Thymeleaf 模板
<!DOCTYPE html>
<!--suppress ALL-->
<html lang="en" xmlns:th="http://www.thymeleaf.org" >
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<span th:text="${stu.name}"></span><br>
<hr>
<h3>测试all</h3>
<div th:remove="all">
<p>标签111</p>
<p>标签222</p>
<p>标签333</p>
</div>
<hr>
<h3>测试boby</h3>
<div th:remove="body">
<p>标签111</p>
<p>标签222</p>
<p>标签333</p>
</div>
<hr>
<h3>测试tag</h3>
<div th:remove="tag">
<p>标签111</p>
<p>标签222</p>
<p>标签333</p>
</div>
<hr>
<h3>测试all-but-first</h3>
<div th:remove="all-but-first">
<p>标签111</p>
<p>标签222</p>
<p>标签333</p>
</div>
<hr>
<h3>测试none</h3>
<div th:remove="none">
<p>标签111</p>
<p>标签222</p>
<p>标签333</p>
</div>
</body>
</html>
(2)效果
<!DOCTYPE html>
<!--suppress ALL-->
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<span>libai</span><br>
<hr>
<h3>测试all</h3>
<hr>
<h3>测试boby</h3>
<div></div>
<hr>
<h3>测试tag</h3>
<p>标签111</p>
<p>标签222</p>
<p>标签333</p>
<hr>
<h3>测试all-but-first</h3>
<div>
<p>标签111</p>
</div>
<hr>
<h3>测试none</h3>
<div>
<p>标签111</p>
<p>标签222</p>
<p>标签333</p>
</div>
</body>
</html>
只要它返回⼀个允许的字符串值(all,tag,body,all-but-first或none)
,则th:remove
属性可以使⽤任何Thymeleaf
标准表达式。这意味着删除可能是有条件的,如:
<a href="/something" th:remove="${condition}? tag : none">Link text not to be removed</a>
另请注意,th:remove
将null
视为none
,因此以下⼯作与上述示例相同:
<a href="/something" th:remove="${condition}? tag">Link text not to be removed</a>
在这种情况下,如果$ {condition}为false,则返回null,因此不会执⾏删除。
8 局部变量
在迭代器中,我们可以使用局部变量prod
:
<tr th:each="prod : ${prods}">
...
</tr>
Thymeleaf 为您提供了一种在不使用迭代的情况下声明局部变量的方法th:with
属性,其语法与属性值类似:
<div th:with="firstPer=${persons[0]}">
<p>
The name of the first person is <span th:text="${firstPer.name}">Julius Caesar</span>.
</p>
</div>
当th:with
被处理时,firstPer
变量创建为局部变量,并添加到来自上下文的变量 map 中,以便它是可用于评估以及在上下文中声明的任何其他变量,但只能在包含<div>
标签的范围内。
可以同时定义多个变量,赋值语法为:
<div th:with="firstPer=${persons[0]},secondPer=${persons[1]}">
<p>
The name of the first person is <span th:text="${firstPer.name}">Julius Caesar</span>.
</p>
<p>
But the name of the second person is
<span th:text="${secondPer.name}">Marcus Antonius</span>.
</p>
</div>
th:with
属性允许重用在同一属性中定义的变量:
<div th:with="company=${user.company + ' Co.'},account=${accounts[company]}">...</div>
9 属性优先级
当在同一个标签中写入多个th:*
属性时,会发生什么? 对于下面的例子:
<ul>
<li th:each="item : ${items}" th:text="${item.description}">Item description here...</li>
</ul>
我们期望th:each
属性在th:text
之前执行,从而可以得到了我们想要的结果,但是鉴于 HTML/XML标准并未对标签中的属性的顺序给出任何的定义,所以必须在属性中建立优先级(precedence)机制 以确保这将按预期工作。
所以,所有的Thymeleaf属性定义一个数字优先级,它建立了它们在标签中执行的顺序。 这个是列表:
Order | Feature | Attributes |
---|---|---|
1 | Fragment inclusion | th:insert th:replace |
2 | Fragment iteration | th:each |
3 | Conditional evaluation | th:if th:unless th:switch th:case |
4 | Local variable definition | th:object th:with |
5 | General attribute modification | th:attr th:attrprepend th:attrappend |
6 | Specific attribute modification | th:value th:href th:src ... |
7 | Text (tag body modification) | th:text th:utext |
8 | Fragment specification | th:fragment |
9 | Fragment removal | th:remove |
这个优先机制意味着如果属性位置被反转,上述迭代片段将给出完全相同的结果(尽管它的可读性稍差):
<ul>
<li th:text="${item.description}" th:each="item : ${items}">Item description here...</li>
</ul>
10 注释
10.1 标准HTML/XML注释
标准HTML/XML注释<!- ... - >
可以在Thymeleaf模板中的任何地⽅使⽤。这些注释中的任何内容都不会被Thymeleaf处理,并将逐字复制到结果中:
<!-- User info follows -->
<div th:text="${...}">
...
</div>
10.2 Thymeleaf解析器级注释
解析器级注释块是当Thymeleaf解析时将简单地从模板中删除的代码。 他们看起来像这样:
<!--/* This code will be removed at Thymeleaf parsing time! */-->
Thymeleaf将删除<!--/*
和*/-->
之间的所有内容,因此当模板静态打开时,这些注释块也可⽤于显示代码,但当Thymeleaf处理它时,它将被删除:
<!--/*-->
<div>
you can see me only before Thymeleaf processes me!
</div>
<!--*/-->
对于具有很多<tr>
的设计原型表,这可能会⾮常⽅便,例如:
<table>
<tr th:each="x : ${xs}">
...
</tr>
<!--/*-->
<tr>
...
</tr>
<tr>
...
</tr>
<!--*/-->
</table>
10.3 Thymeleaf专有注释
当模板被静态打开(即作为原型)时,Thymeleaf允许定义特殊注释块,并且在执⾏模板时,Thymeleaf被认为是正常的标记。
<span>hello!</span>
<!--/*/
<div th:text="${...}">
...
</div>
/*/-->
<span>goodbye!</span>
Thymeleaf的解析系统将简单地删除<!--/*/
和/*/-->
标记,但不删除包含其中的内容,因此标记之间的内容将被保留。 所以当执⾏模板时,Thymeleaf实际上会看到这样的:
<span>hello!</span>
<div th:text="${...}">
...
</div>
<span>goodbye!</span>
与解析器级注释块⼀样,此功能与⽅⾔⽆关。
10.4 应用示例
(1)Thymeleaf 模板
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>标准HTML/XML注释</h3>
<!--标准HTML/XML注释
<p>简单测试111111</p>
-->
<hr>
<h3>Thymeleaf解析器级注释</h3>
<!--/*
Thymeleaf解析器级注释
<p>简单测试222222</p>
*/-->
<hr>
<h3>Thymeleaf专有注释</h3>
<!--/*/
<p>简单测试333333</p>
/*/-->
</body>
</html>
(2) 执行效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>标准HTML/XML注释</h3>
<!--标准HTML/XML注释
<p>简单测试111111</p>
-->
<hr>
<h3>Thymeleaf解析器级注释</h3>
<hr>
<h3>Thymeleaf专有注释</h3>
<p>简单测试333333</p>
</body>
</html>