4 设置属性值

5 迭代器(th:each

5.1 基本的迭代

th:each将循环 array 或 list 中的元素并重复打印一组标签,语法相当于 Java foreach 表达式:

  1. <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 或 odd
  • first 判断当前迭代器是否是第一个
  • last 判断当前迭代器是否是最后

看下面的例子:

  1. <table>
  2. <tr>
  3. <th>NAME</th>
  4. <th>PRICE</th>
  5. <th>IN STOCK</th>
  6. </tr>
  7. <tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
  8. <td th:text="${prod.name}">Onions</td>
  9. <td th:text="${prod.price}">2.41</td>
  10. <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
  11. </tr>
  12. </table>

状态变量(在本示例中为“iterStat”)在th:each中定义了。

我们来看看模板的处理后的结果:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Good Thymes Virtual Grocery</title>
  5. <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
  6. <link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
  7. </head>
  8. <body>
  9. <h1>Product list</h1>
  10. <table>
  11. <tr>
  12. <th>NAME</th>
  13. <th>PRICE</th>
  14. <th>IN STOCK</th>
  15. </tr>
  16. <tr class="odd">
  17. <td>Fresh Sweet Basil</td>
  18. <td>4.99</td>
  19. <td>yes</td>
  20. </tr>
  21. <tr>
  22. <td>Italian Tomato</td>
  23. <td>1.25</td>
  24. <td>no</td>
  25. </tr>
  26. <tr class="odd">
  27. <td>Yellow Bell Pepper</td>
  28. <td>2.50</td>
  29. <td>yes</td>
  30. </tr>
  31. <tr>
  32. <td>Old Cheddar</td>
  33. <td>18.75</td>
  34. <td>yes</td>
  35. </tr>
  36. </table>
  37. <p>
  38. <a href="/gtvg/" shape="rect">Return to home</a>
  39. </p>
  40. </body>
  41. </html>

请注意,我们的迭代状态变量已经运行良好,建立只有奇数行具有 “odd” CSS 类。

如果您没有明确设置状态变量,则 Thymeleaf 将始终创建一个状态变量,可以通过后缀“Stat”获取到迭代变量的名称:

  1. <table>
  2. <tr>
  3. <th>NAME</th>
  4. <th>PRICE</th>
  5. <th>IN STOCK</th>
  6. </tr>
  7. <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
  8. <td th:text="${prod.name}">Onions</td>
  9. <td th:text="${prod.price}">2.41</td>
  10. <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
  11. </tr>
  12. </table>

5.3 示例

(1)Student实体

  1. package com.haan.thymeleafstudy.entity;
  2. import lombok.Data;
  3. @Data
  4. public class Student {
  5. private String name;
  6. private Integer age;
  7. private String message;
  8. }

(2)控制器

  1. package com.haan.thymeleafstudy;
  2. import com.haan.thymeleafstudy.entity.Student;
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.ui.Model;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. @Controller
  9. public class IteratorController {
  10. @RequestMapping("/test001")
  11. public String testIterator(Model model){
  12. List<Student> students = new ArrayList<Student>();
  13. Student stu1 = new Student();
  14. stu1.setName("libai");
  15. stu1.setAge(12);
  16. stu1.setMessage("众人皆醒我独醉");
  17. students.add(stu1);
  18. Student stu2 = new Student();
  19. stu2.setName("llw");
  20. stu2.setAge(12);
  21. stu2.setMessage("开渔节苦于温热人发帖人");
  22. students.add(stu2);
  23. Student stu3 = new Student();
  24. stu3.setName("smlz");
  25. stu3.setAge(12);
  26. stu3.setMessage("is我的速进个非常有好处");
  27. students.add(stu3);
  28. System.out.println(students);
  29. model.addAttribute("students",students);
  30. return "iterator";
  31. }
  32. }

(2)Thymeleaf 模板

  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>迭代器</title>
  6. </head>
  7. <body>
  8. <h3>Thymeleaf迭代器(th:each)学习</h3>
  9. <div th:each="student:${students}">
  10. 学生名称:<span th:text="${student.name}">111111</span><br>
  11. 学生年龄:<span th:text="${student.age}">111111</span><br>
  12. 个性签名:<span th:text="${student.message}">111111</span><br>
  13. 状态变量index:<span th:text="${studentStat.index}">111111</span><br>
  14. 状态变量count:<span th:text="${studentStat.count}">111111</span><br>
  15. 状态变量size:<span th:text="${studentStat.size}">111111</span><br>
  16. 状态变量current:<span th:text="${studentStat.current.getMessage()}">111111</span><br>
  17. 状态变量odd/even:<span th:text="${studentStat.odd}">111111</span><br>
  18. 状态变量first:<span th:text="${studentStat.first}">111111</span><br>
  19. 状态变量last:<span th:text="${studentStat.last}">111111</span><br>
  20. <hr>
  21. </div>
  22. </body>
  23. </html>

(4)效果

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>迭代器</title>
  6. </head>
  7. <body>
  8. <h3>Thymeleaf迭代器(th:each)学习</h3>
  9. <div>
  10. 学生名称:<span>libai</span><br>
  11. 学生年龄:<span>12</span><br>
  12. 个性签名:<span>众人皆醒我独醉</span><br>
  13. 状态变量index:<span>0</span><br>
  14. 状态变量count:<span>1</span><br>
  15. 状态变量size:<span>3</span><br>
  16. 状态变量current:<span>众人皆醒我独醉</span><br>
  17. 状态变量odd/even:<span>true</span><br>
  18. 状态变量first:<span>true</span><br>
  19. 状态变量last:<span>false</span><br>
  20. <hr>
  21. </div>
  22. <div>
  23. 学生名称:<span>llw</span><br>
  24. 学生年龄:<span>12</span><br>
  25. 个性签名:<span>开渔节苦于温热人发帖人</span><br>
  26. 状态变量index:<span>1</span><br>
  27. 状态变量count:<span>2</span><br>
  28. 状态变量size:<span>3</span><br>
  29. 状态变量current:<span>开渔节苦于温热人发帖人</span><br>
  30. 状态变量odd/even:<span>false</span><br>
  31. 状态变量first:<span>false</span><br>
  32. 状态变量last:<span>false</span><br>
  33. <hr>
  34. </div>
  35. <div>
  36. 学生名称:<span>smlz</span><br>
  37. 学生年龄:<span>12</span><br>
  38. 个性签名:<span>is我的速进个非常有好处</span><br>
  39. 状态变量index:<span>2</span><br>
  40. 状态变量count:<span>3</span><br>
  41. 状态变量size:<span>3</span><br>
  42. 状态变量current:<span>is我的速进个非常有好处</span><br>
  43. 状态变量odd/even:<span>true</span><br>
  44. 状态变量first:<span>false</span><br>
  45. 状态变量last:<span>true</span><br>
  46. <hr>
  47. </div>
  48. </body>
  49. </html>

1018001.png

6 条件语句

6.1 th:ifth:unless

th:if 属性用法如下:

  1. <a href="comments.html"
  2. th:href="@{/product/comments(prodId=${prod.id})}"
  3. 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,前面的例子改为:

  1. <a href="comments.html"
  2. th:href="@{/comments(prodId=${prod.id})}"
  3. th:unless="${#lists.isEmpty(prod.comments)}">view</a>

6.2 th:switch 语句

switch 语句使用th:switch / th:case 属性集合来实现:

  1. <div th:switch="${user.role}">
  2. <p th:case="'admin'">User is an administrator</p>
  3. <p th:case="#{roles.manager}">User is a manager</p>
  4. </div>

请注意,只要一个th:case属性被评估为’true’,每个其他同一个 switch 语句中的th:case属性将被评估为false

th:case="*"来设置默认选项:

  1. <div th:switch="${user.role}">
  2. <p th:case="'admin'">User is an administrator</p>
  3. <p th:case="#{roles.manager}">User is a manager</p>
  4. <p th:case="*">User is some other thing</p>
  5. </div>

6.3 示例

(1)Person实体

  1. package com.haan.thymeleafstudy.entity;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @AllArgsConstructor
  7. @NoArgsConstructor
  8. public class Person {
  9. private String name;
  10. private Integer age;
  11. private String role;
  12. private Boolean married;
  13. }

(2)控制器

  1. package com.haan.thymeleafstudy.controller;
  2. import com.haan.thymeleafstudy.entity.Person;
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.ui.Model;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. @Controller
  9. public class ConditionController {
  10. @RequestMapping("/test002")
  11. public String test002(Model model){
  12. List<Person> persons = new ArrayList<>();
  13. Person person1 = new Person("libai",22,"teacher",true);
  14. persons.add(person1);
  15. Person person2 = new Person("zhangliang",22,"doctor",false);
  16. persons.add(person2);
  17. Person person3 = new Person("sdj",21,"student",true);
  18. persons.add(person3);
  19. model.addAttribute("persons",persons);
  20. return "conditional";
  21. }
  22. }

(3)Thymeleaf 模板

  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h3>IF 测试一</h3>
  9. <div th:each="person:${persons}">
  10. <div th:if="${person.age>21}">
  11. 姓名:<span th:text="${person.name}">111111</span><br>
  12. 年龄:<span th:text="${person.age}">111111</span><br>
  13. 角色:<span th:text="${person.role}">111111</span><br>
  14. 已婚:<span th:text="${person.married}">111111</span><br>
  15. <hr>
  16. </div>
  17. </div>
  18. <h3>IF 测试二</h3>
  19. <div th:each="persion:${persons}">
  20. <div th:if="${person.married}">
  21. 姓名:<span th:text="${person.name}">111111</span><br>
  22. 年龄:<span th:text="${person.age}">111111</span><br>
  23. 角色:<span th:text="${person.role}">111111</span><br>
  24. 已婚:<span th:text="${person.married}">111111</span><br>
  25. <hr>
  26. </div>
  27. </div>
  28. <h3>IF 测试三</h3>
  29. <div th:each="person:${persons}">
  30. <div th:if="${person.married && person.age>21 }">
  31. 姓名:<span th:text="${person.name}">111111</span><br>
  32. 年龄:<span th:text="${person.age}">111111</span><br>
  33. 角色:<span th:text="${person.role}">111111</span><br>
  34. 已婚:<span th:text="${person.married}">111111</span><br>
  35. <hr>
  36. </div>
  37. </div>
  38. <h3>IF 测试四</h3>
  39. <div th:each="person:${persons}">
  40. <div th:unless="${person.married && person.age>21 }">
  41. 姓名:<span th:text="${person.name}">111111</span><br>
  42. 年龄:<span th:text="${person.age}">111111</span><br>
  43. 角色:<span th:text="${person.role}">111111</span><br>
  44. 已婚:<span th:text="${person.married}">111111</span><br>
  45. <hr>
  46. </div>
  47. </div>
  48. <h3>SWITCH 测试一</h3>
  49. <div th:each="person:${persons}">
  50. <div th:switch="${person.role}">
  51. 姓名:<span th:text="${person.name}">111111</span><br>
  52. 年龄:<span th:text="${person.age}">111111</span><br>
  53. 角色:
  54. <span th:case="teacher">老师</span>
  55. <span th:case="doctor">医生</span>
  56. <span th:case="*">其他</span>
  57. <br>
  58. 已婚:<span th:text="${person.married}">111111</span><br>
  59. <hr>
  60. </div>
  61. </div>
  62. </body>
  63. </html>

(4)效果

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h3>IF 测试一</h3>
  9. <div>
  10. <div>
  11. 姓名:<span>libai</span><br>
  12. 年龄:<span>22</span><br>
  13. 角色:<span>teacher</span><br>
  14. 已婚:<span>true</span><br>
  15. <hr>
  16. </div>
  17. </div>
  18. <div>
  19. <div>
  20. 姓名:<span>zhangliang</span><br>
  21. 年龄:<span>22</span><br>
  22. 角色:<span>doctor</span><br>
  23. 已婚:<span>false</span><br>
  24. <hr>
  25. </div>
  26. </div>
  27. <div>
  28. </div>
  29. <h3>IF 测试二</h3>
  30. <div>
  31. <div>
  32. 姓名:<span>libai</span><br>
  33. 年龄:<span>22</span><br>
  34. 角色:<span>teacher</span><br>
  35. 已婚:<span>true</span><br>
  36. <hr>
  37. </div>
  38. </div>
  39. <div>
  40. </div>
  41. <div>
  42. <div>
  43. 姓名:<span>sdj</span><br>
  44. 年龄:<span>21</span><br>
  45. 角色:<span>student</span><br>
  46. 已婚:<span>true</span><br>
  47. <hr>
  48. </div>
  49. </div>
  50. <h3>IF 测试三</h3>
  51. <div>
  52. <div>
  53. 姓名:<span>libai</span><br>
  54. 年龄:<span>22</span><br>
  55. 角色:<span>teacher</span><br>
  56. 已婚:<span>true</span><br>
  57. <hr>
  58. </div>
  59. </div>
  60. <div>
  61. </div>
  62. <div>
  63. </div>
  64. <h3>IF 测试四</h3>
  65. <div>
  66. </div>
  67. <div>
  68. <div>
  69. 姓名:<span>zhangliang</span><br>
  70. 年龄:<span>22</span><br>
  71. 角色:<span>doctor</span><br>
  72. 已婚:<span>false</span><br>
  73. <hr>
  74. </div>
  75. </div>
  76. <div>
  77. <div>
  78. 姓名:<span>sdj</span><br>
  79. 年龄:<span>21</span><br>
  80. 角色:<span>student</span><br>
  81. 已婚:<span>true</span><br>
  82. <hr>
  83. </div>
  84. </div>
  85. <h3>SWITCH 测试一</h3>
  86. <div>
  87. <div>
  88. 姓名:<span>libai</span><br>
  89. 年龄:<span>22</span><br>
  90. 角色:
  91. <span>老师</span>
  92. <br>
  93. 已婚:<span>true</span><br>
  94. <hr>
  95. </div>
  96. </div>
  97. <div>
  98. <div>
  99. 姓名:<span>zhangliang</span><br>
  100. 年龄:<span>22</span><br>
  101. 角色:
  102. <span>医生</span>
  103. <br>
  104. 已婚:<span>false</span><br>
  105. <hr>
  106. </div>
  107. </div>
  108. <div>
  109. <div>
  110. 姓名:<span>sdj</span><br>
  111. 年龄:<span>21</span><br>
  112. 角色:
  113. <span>其他</span>
  114. <br>
  115. 已婚:<span>true</span><br>
  116. <hr>
  117. </div>
  118. </div>
  119. </body>
  120. </html>

1019001.png1019002.png

7 模板布局

7.1 包含模板片段

7.1.1 定义和引用片段

在我们的模板中,我们经常需要从其他模板中添加html页面片段,如页脚、标题、菜单…

为了做到这一点,Thymeleaf 需要我们来定义这些“片段”,可以使用th:fragment属性来完成。我们定义了/WEB-INF/templates/footer.html页面

  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org">
  3. <body>
  4. <div th:fragment="copy">
  5. &copy; 2017 <a href="https://waylau.com">waylau.com</a>
  6. </div>
  7. </body>
  8. </html>

如果,我们想引用这个 copy 代码片段,我们可以用 th:insertth:replace 属性 (th:include 也是可以,但自 Thymeleaf 3.0 以来就不再推荐):

  1. <body>
  2. ...
  3. <div th:insert="~{footer :: copy}"></div>
  4. </body>

注意th:insert需要一个片段表达式〜{...})。 在上面的例子中,非复杂片段表达式,(〜{})包围是完全可选,所以上面的代码将等效于:

  1. <body>
  2. ...
  3. <div th:insert="footer :: copy"></div>
  4. </body>

7.1.2 片段规范语法

  • "~{templatename::selector}" 名为templatename的模板上的指定标记选择器。 selector可以只是一个片段名。
  • "~{templatename}" : 包含完整的模版 templatename
  • ~{::selector}" or "~{this::selector}" 相同模版中的代码片段

7.1.3 不使用 th:fragment

不使用 th:fragment也可以引用HTML片段,比如:

  1. ...
  2. <div id="copy-section">
  3. &copy; 2017 <a href="https://waylau.com">waylau.com</a>
  4. </div>
  5. ...

通过 id 也可以引用到页面片段:

  1. <body>
  2. ...
  3. <div th:insert="~{footer :: #copy-section}"></div>
  4. </body>

7.1.4 th:insertth:replaceth:include三者区别

  • th:insert是最简单的:它将简单地插入指定的片段作为正文 的主标签。
  • th:replace用指定实际片段来替换其主标签。
  • th:include类似于th:insert,但不是插入片段它只插入此片段的内容

所以

  1. <footer th:fragment="copy">
  2. &copy; 2017 <a href="https://waylau.com">waylau.com</a>
  3. </footer>

三种方式引用该片段

  1. <body>
  2. ...
  3. <div th:insert="footer :: copy"></div>
  4. <div th:replace="footer :: copy"></div>
  5. <div th:include="footer :: copy"></div>
  6. </body>

结果为:

  1. <body>
  2. ...
  3. <div>
  4. <footer>
  5. &copy; 2017 <a href="https://waylau.com">waylau.com</a>
  6. </footer>
  7. </div>
  8. <footer>
  9. &copy; 2017 <a href="https://waylau.com">waylau.com</a>
  10. </footer>
  11. <div>
  12. &copy; 2017 <a href="https://waylau.com">waylau.com</a>
  13. </div>
  14. </body>

7.1.5 示例

(1)控制器

  1. package com.haan.thymeleafstudy.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. @Controller
  5. public class TmlLatoutController {
  6. @RequestMapping("/test003")
  7. public String test003(){
  8. return "tpllayout01";
  9. }
  10. }

(2)Thymeleaf 模板

  1. // footer.html
  2. <!DOCTYPE html>
  3. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>Title</title>
  7. </head>
  8. <body>
  9. <div>
  10. <h2>这是一个footer模板</h2>
  11. <footer th:fragment="foot">
  12. &copy; 2019 <a href="https://baidu.com">百度一下</a>
  13. </footer>
  14. </div>
  15. <footer id="footId">
  16. &copy; 2019 <a href="https://baidu.com">百度一下002</a>
  17. </footer>
  18. <footer th:fragment="foot">
  19. &copy; 2009 <a href="https://baidu.com">百度一下003</a>
  20. </footer>
  21. </body>
  22. </html>
  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h3>测试insert、replace和include的区别</h3>
  9. <div th:include="~{footer::foot}"></div>
  10. <hr>
  11. <div th:insert="~{footer::foot}"></div>
  12. <hr>
  13. <div th:replace="~{footer::foot}"></div>
  14. <h3>测试其他</h3>
  15. <div th:insert="~{footer.html::foot}"></div>
  16. <hr>
  17. <div th:insert="footer::foot"></div>
  18. <hr>
  19. <div th:insert="footer"></div>
  20. <hr>
  21. <div th:replace="~{footer::#footId}"></div>
  22. <hr>
  23. <div th:insert="~{::foot}"></div>
  24. <div>
  25. <h2>这是一个footer模板004</h2>
  26. <footer th:fragment="foot">
  27. &copy; 2019 <a href="https://baidu.com">百度一下004</a>
  28. </footer>
  29. </div>
  30. </body>
  31. </html>

(3)执行效果

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h3>测试insert、replace和include的区别</h3>
  9. <div>
  10. &copy; 2019 <a href="https://baidu.com">百度一下</a>
  11. &copy; 2009 <a href="https://baidu.com">百度一下003</a>
  12. </div>
  13. <hr>
  14. <div><footer>
  15. &copy; 2019 <a href="https://baidu.com">百度一下</a>
  16. </footer><footer>
  17. &copy; 2009 <a href="https://baidu.com">百度一下003</a>
  18. </footer></div>
  19. <hr>
  20. <footer>
  21. &copy; 2019 <a href="https://baidu.com">百度一下</a>
  22. </footer><footer>
  23. &copy; 2009 <a href="https://baidu.com">百度一下003</a>
  24. </footer>
  25. <h3>测试其他</h3>
  26. <div><footer>
  27. &copy; 2019 <a href="https://baidu.com">百度一下</a>
  28. </footer><footer>
  29. &copy; 2009 <a href="https://baidu.com">百度一下003</a>
  30. </footer></div>
  31. <hr>
  32. <div><footer>
  33. &copy; 2019 <a href="https://baidu.com">百度一下</a>
  34. </footer><footer>
  35. &copy; 2009 <a href="https://baidu.com">百度一下003</a>
  36. </footer></div>
  37. <hr>
  38. <div><!DOCTYPE html>
  39. <html lang="en">
  40. <head>
  41. <meta charset="UTF-8">
  42. <title>Title</title>
  43. </head>
  44. <body>
  45. <div>
  46. <h2>这是一个footer模板</h2>
  47. <footer>
  48. &copy; 2019 <a href="https://baidu.com">百度一下</a>
  49. </footer>
  50. </div>
  51. <footer id="footId">
  52. &copy; 2019 <a href="https://baidu.com">百度一下002</a>
  53. </footer>
  54. <footer>
  55. &copy; 2009 <a href="https://baidu.com">百度一下003</a>
  56. </footer>
  57. </body>
  58. </html></div>
  59. <hr>
  60. <footer id="footId">
  61. &copy; 2019 <a href="https://baidu.com">百度一下002</a>
  62. </footer>
  63. <hr>
  64. <div><footer>
  65. &copy; 2019 <a href="https://baidu.com">百度一下004</a>
  66. </footer></div>
  67. <div>
  68. <h2>这是一个footer模板</h2>
  69. <footer>
  70. &copy; 2019 <a href="https://baidu.com">百度一下004</a>
  71. </footer>
  72. </div>
  73. </body>
  74. </html>

1019003.png

7.2 可参数化片段

声明片段的时候可以声明变量参数,在片段中使用变量参数

  1. <div th:fragment="frag (onevar,twovar)">
  2. <p th:text="${onevar} + ' - ' + ${twovar}">...</p>
  3. </div>

引入片段的时候把参数的值,传入进来

两种书写方式(语法):
(1)如果,可以直接写参数,按顺序一一对应
(2)如果,可以指定变量名,对应的值,此时安装变量名进行参数对应,与顺序无关

  1. <div th:replace="::frag (${value1},${value2})">...</div>
  2. <div th:replace="::frag (onevar=${value1},twovar=${value2})">...</div>
  1. <div th:replace="::frag (twovar=${value2},onevar=${value1})">...</div>

7.2.1 不带片段参数的片段局部变量

即使片段定义时没有参数

  1. <div th:fragment="frag">
  2. ...
  3. </div>

我们可以使用上面指定的第二种语法来调用它们(并且只有第二种语法):

  1. <div th:replace="::frag (onevar=${value1},twovar=${value2})">

这相当于th:replaceth:with的组合:

  1. <div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">

请注意,无论片段是否有参数签名,这种局部变量的规范都不会导致上下文在执行之前被清空。片段仍然能够像当前一样访问调用模板中使用的每个上下文变量。

7.2.2 th:assert 对于in-template声明

th:assert属性可以指定一个逗号分隔的表达式列表,这些表达式应该被求值,并且每次求值要求生成true,否则会引发异常。

  1. <div th:assert="${onevar},(${twovar} != 43)">...</div>

这对于在片段签名中验证参数是很方便的:

  1. <header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>

7.3 灵活的布局:不仅仅是片段插入

由于⽚段表达式的强⼤功能,我们不仅可以为⽚段指定⽂本类型,数字类型,bean对象类型的参数,还可以指定标记⽚段作为参数

这允许我们以⼀种⽅式创建我们的⽚段,使得它们可以调⽤模板的标记,从⽽产⽣⾮常灵活的模板布局机制。

请注意在下⾯的⽚段中使⽤标题和链接变量:

  1. <head th:fragment="common_header(title,links)">
  2. <title th:replace="${title}">The awesome application</title>
  3. <!-- Common styles and scripts -->
  4. <link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}">
  5. <link rel="shortcut icon" th:href="@{/images/favicon.ico}">
  6. <script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script>
  7. <!--/* Per-page placeholder for additional links */-->
  8. <th:block th:replace="${links}" />
  9. </head>

我们像这样调⽤这个⽚段:

  1. ...
  2. <head th:replace="base :: common_header(~{::title},~{::link})">
  3. <title>Awesome - Main</title>
  4. <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
  5. <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
  6. </head>
  7. ...

结果将使⽤调⽤模板中的实际和标签作为标题和链接变量的值,导致我们的⽚段在插⼊期间被定制化:

  1. <head>
  2. <title>Awesome - Main</title>
  3. <!-- Common styles and scripts -->
  4. <link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
  5. <link rel="shortcut icon" href="/awe/images/favicon.ico">
  6. <script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
  7. <link rel="stylesheet" href="/awe/css/bootstrap.min.css">
  8. <link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css">
  9. </head>

7.3.1 使用空片段

⼀个特殊的⽚段表达式,空的⽚段(〜{})可以⽤于指定没有标记。 使⽤前⾯的例⼦:

  1. <head th:replace="base :: common_header(~{::title},~{})">
  2. <title>Awesome - Main</title>
  3. </head>
  4. ...

注意⽚段(链接)的第⼆个参数如何设置为空⽚段,因此没有为<th:blockth:replace =“$ {links}”/>块写⼊:

  1. ...
  2. <head>
  3. <title>Awesome - Main</title>
  4. <!-- Common styles and scripts -->
  5. <link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
  6. <link rel="shortcut icon" href="/awe/images/favicon.ico">
  7. <script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
  8. </head>
  9. ...

7.3.2 使用无操作符

如果我们只想让我们的⽚段将其当前标记⽤作默认值,那么no-op也可以⽤作⽚段的参数。 再次,使⽤common_header示例:

  1. ...
  2. <head th:replace="base :: common_header(_,~{::link})">
  3. <title>Awesome - Main</title>
  4. <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
  5. <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
  6. </head>
  7. ...

看看title参数(commonheader⽚段的第⼀个参数)如何设置为`no-op(,这导致⽚段的这⼀部分不被执⾏(title = no-operation)`:

  1. <title th:replace="${title}">The awesome application</title>

结果如下:

  1. ...
  2. <head>
  3. <title>The awesome application</title>
  4. <!-- Common styles and scripts -->
  5. <link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
  6. <link rel="shortcut icon" href="/awe/images/favicon.ico">
  7. <script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>
  8. <link rel="stylesheet" href="/awe/css/bootstrap.min.css">
  9. <link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css">
  10. </head>
  11. ...

7.33 高级条件插入片段

空⽚段和⽆操作⽚段允许我们以⾮常简单和优雅的⽅式执⾏⽚段的条件插⼊。
例如,我们可以这样做,以便只有当⽤户是管理员时插⼊我们的common:: adminhead⽚段,如果不是管理员插⼊空⽚段:

  1. ...
  2. <div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}">...</div>
  3. ...

另外,只有满⾜指定的条件,我们才可以使⽤无操作符来插⼊⽚段,但是如果不满⾜条件,则不需要修改则保留标记:

  1. ...
  2. <div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : _">
  3. Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support.
  4. </div>
  5. ...

另外,如果我们配置了我们的模板解析器来检查模板资源的存在 - 通过它们的checkExistence标志,我们可以使⽤⽚段本身的存在作为默认操作的条件

  1. ...
  2. <!-- The body of the <div> will be used if the "common :: salutation" fragment -->
  3. <!-- does not exist (or is empty). -->
  4. <div th:insert="~{common :: salutation} ?: _">
  5. Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support.
  6. </div>
  7. ...

7.4 删除模版⽚段

回到我们的示例应⽤程序,让我们回顾⼀下我们的产品列表模板的最后⼀个版本:

  1. <table>
  2. <tr>
  3. <th>NAME</th>
  4. <th>PRICE</th>
  5. <th>IN STOCK</th>
  6. <th>COMMENTS</th>
  7. </tr>
  8. <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
  9. <td th:text="${prod.name}">Onions</td>
  10. <td th:text="${prod.price}">2.41</td>
  11. <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
  12. <td>
  13. <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
  14. <a href="comments.html"
  15. th:href="@{/product/comments(prodId=${prod.id})}"
  16. th:unless="${#lists.isEmpty(prod.comments)}">view</a>
  17. </td>
  18. </tr>
  19. </table>

这个代码只是⼀个模板,但是作为⼀个静态⻚⾯(当没有Thymeleaf的浏览器直接打开它时),它不会成为⼀个很好的原型。

为什么? 因为尽管浏览器完全可以显示,该表只有⼀⾏,⽽这⾏有模拟数据。 作为⼀个原型,它根本看起来不够现实,我们应该有多个产品,我们需要更多的⾏。所以我们来补充⼀下:

  1. <table>
  2. <tr>
  3. <th>NAME</th>
  4. <th>PRICE</th>
  5. <th>IN STOCK</th>
  6. <th>COMMENTS</th>
  7. </tr>
  8. <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
  9. <td th:text="${prod.name}">Onions</td>
  10. <td th:text="${prod.price}">2.41</td>
  11. <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
  12. <td>
  13. <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
  14. <a href="comments.html"
  15. th:href="@{/product/comments(prodId=${prod.id})}"
  16. th:unless="${#lists.isEmpty(prod.comments)}">view</a>
  17. </td>
  18. </tr>
  19. <tr class="odd">
  20. <td>Blue Lettuce</td>
  21. <td>9.55</td>
  22. <td>no</td>
  23. <td>
  24. <span>0</span> comment/s
  25. </td>
  26. </tr>
  27. <tr>
  28. <td>Mild Cinnamon</td>
  29. <td>1.99</td>
  30. <td>yes</td>
  31. <td>
  32. <span>3</span> comment/s
  33. <a href="comments.html">view</a>
  34. </td>
  35. </tr>
  36. </table>

好的,现在我们有三个产品,肯定是⼀个好的原型。 但是,当我们⽤Thymeleaf处理它会发⽣什么?

  1. <table>
  2. <tr>
  3. <th>NAME</th>
  4. <th>PRICE</th>
  5. <th>IN STOCK</th>
  6. <th>COMMENTS</th>
  7. </tr>
  8. <tr>
  9. <td>Fresh Sweet Basil</td>
  10. <td>4.99</td>
  11. <td>yes</td>
  12. <td>
  13. <span>0</span> comment/s
  14. </td>
  15. </tr>
  16. <tr class="odd">
  17. <td>Italian Tomato</td>
  18. <td>1.25</td>
  19. <td>no</td>
  20. <td>
  21. <span>2</span> comment/s
  22. <a href="/gtvg/product/comments?prodId=2">view</a>
  23. </td>
  24. </tr>
  25. <tr>
  26. <td>Yellow Bell Pepper</td>
  27. <td>2.50</td>
  28. <td>yes</td>
  29. <td>
  30. <span>0</span> comment/s
  31. </td>
  32. </tr>
  33. <tr class="odd">
  34. <td>Old Cheddar</td>
  35. <td>18.75</td>
  36. <td>yes</td>
  37. <td>
  38. <span>1</span> comment/s
  39. <a href="/gtvg/product/comments?prodId=4">view</a>
  40. </td>
  41. </tr>
  42. <tr class="odd">
  43. <td>Blue Lettuce</td>
  44. <td>9.55</td>
  45. <td>no</td>
  46. <td>
  47. <span>0</span> comment/s
  48. </td>
  49. </tr>
  50. <tr>
  51. <td>Mild Cinnamon</td>
  52. <td>1.99</td>
  53. <td>yes</td>
  54. <td>
  55. <span>3</span> comment/s
  56. <a href="comments.html">view</a>
  57. </td>
  58. </tr>
  59. </table>

最后两⾏是模拟数据⾏! 那么当然它们是:迭代只适⽤于第⼀⾏,所以没有什么理由为什么Thymeleaf应该删除另外两个。

我们需要⼀种在模板处理过程中删除这两⾏的⽅法。 我们使⽤第⼆和第三个<tr>标签中的th:remove属性:

  1. <table>
  2. <tr>
  3. <th>NAME</th>
  4. <th>PRICE</th>
  5. <th>IN STOCK</th>
  6. <th>COMMENTS</th>
  7. </tr>
  8. <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
  9. <td th:text="${prod.name}">Onions</td>
  10. <td th:text="${prod.price}">2.41</td>
  11. <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
  12. <td>
  13. <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
  14. <a href="comments.html"
  15. th:href="@{/product/comments(prodId=${prod.id})}"
  16. th:unless="${#lists.isEmpty(prod.comments)}">view</a>
  17. </td>
  18. </tr>
  19. <tr class="odd" th:remove="all">
  20. <td>Blue Lettuce</td>
  21. <td>9.55</td>
  22. <td>no</td>
  23. <td>
  24. <span>0</span> comment/s
  25. </td>
  26. </tr>
  27. <tr th:remove="all">
  28. <td>Mild Cinnamon</td>
  29. <td>1.99</td>
  30. <td>yes</td>
  31. <td>
  32. <span>3</span> comment/s
  33. <a href="comments.html">view</a>
  34. </td>
  35. </tr>
  36. </table>

⼀旦处理,⼀切看起来应该是:

  1. <table>
  2. <tr>
  3. <th>NAME</th>
  4. <th>PRICE</th>
  5. <th>IN STOCK</th>
  6. <th>COMMENTS</th>
  7. </tr>
  8. <tr>
  9. <td>Fresh Sweet Basil</td>
  10. <td>4.99</td>
  11. <td>yes</td>
  12. <td>
  13. <span>0</span> comment/s
  14. </td>
  15. </tr>
  16. <tr class="odd">
  17. <td>Italian Tomato</td>
  18. <td>1.25</td>
  19. <td>no</td>
  20. <td>
  21. <span>2</span> comment/s
  22. <a href="/gtvg/product/comments?prodId=2">view</a>
  23. </td>
  24. </tr>
  25. <tr>
  26. <td>Yellow Bell Pepper</td>
  27. <td>2.50</td>
  28. <td>yes</td>
  29. <td>
  30. <span>0</span> comment/s
  31. </td>
  32. </tr>
  33. <tr class="odd">
  34. <td>Old Cheddar</td>
  35. <td>18.75</td>
  36. <td>yes</td>
  37. <td>
  38. <span>1</span> comment/s
  39. <a href="/gtvg/product/comments?prodId=4">view</a>
  40. </td>
  41. </tr>
  42. </table>

这个属性中的all值是什么意思?th:remove可以有五种不同的删除⽅式,
具体取决于它的值:

  • all:删除包含标签及其所有⼦项。
  • body:不要删除包含的标签,但删除所有的⼦项。
  • tag:删除包含的标签,但不要删除其⼦项。
  • all-but-first:删除除第⼀个包含标签之外的所有⼦代。
  • none:什么都不做。 该值对于动态计算是有⽤的。

示例:

(1)Thymeleaf 模板

  1. <!DOCTYPE html>
  2. <!--suppress ALL-->
  3. <html lang="en" xmlns:th="http://www.thymeleaf.org" >
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>Title</title>
  7. </head>
  8. <body>
  9. <span th:text="${stu.name}"></span><br>
  10. <hr>
  11. <h3>测试all</h3>
  12. <div th:remove="all">
  13. <p>标签111</p>
  14. <p>标签222</p>
  15. <p>标签333</p>
  16. </div>
  17. <hr>
  18. <h3>测试boby</h3>
  19. <div th:remove="body">
  20. <p>标签111</p>
  21. <p>标签222</p>
  22. <p>标签333</p>
  23. </div>
  24. <hr>
  25. <h3>测试tag</h3>
  26. <div th:remove="tag">
  27. <p>标签111</p>
  28. <p>标签222</p>
  29. <p>标签333</p>
  30. </div>
  31. <hr>
  32. <h3>测试all-but-first</h3>
  33. <div th:remove="all-but-first">
  34. <p>标签111</p>
  35. <p>标签222</p>
  36. <p>标签333</p>
  37. </div>
  38. <hr>
  39. <h3>测试none</h3>
  40. <div th:remove="none">
  41. <p>标签111</p>
  42. <p>标签222</p>
  43. <p>标签333</p>
  44. </div>
  45. </body>
  46. </html>

(2)效果

  1. <!DOCTYPE html>
  2. <!--suppress ALL-->
  3. <html lang="en" >
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>Title</title>
  7. </head>
  8. <body>
  9. <span>libai</span><br>
  10. <hr>
  11. <h3>测试all</h3>
  12. <hr>
  13. <h3>测试boby</h3>
  14. <div></div>
  15. <hr>
  16. <h3>测试tag</h3>
  17. <p>标签111</p>
  18. <p>标签222</p>
  19. <p>标签333</p>
  20. <hr>
  21. <h3>测试all-but-first</h3>
  22. <div>
  23. <p>标签111</p>
  24. </div>
  25. <hr>
  26. <h3>测试none</h3>
  27. <div>
  28. <p>标签111</p>
  29. <p>标签222</p>
  30. <p>标签333</p>
  31. </div>
  32. </body>
  33. </html>

1019004.png

只要它返回⼀个允许的字符串值(all,tag,body,all-but-first或none),则th:remove属性可以使⽤任何Thymeleaf标准表达式。这意味着删除可能是有条件的,如:

  1. <a href="/something" th:remove="${condition}? tag : none">Link text not to be removed</a>

另请注意,th:removenull视为none,因此以下⼯作与上述示例相同:

  1. <a href="/something" th:remove="${condition}? tag">Link text not to be removed</a>

在这种情况下,如果$ {condition}为false,则返回null,因此不会执⾏删除。

8 局部变量

在迭代器中,我们可以使用局部变量prod

  1. <tr th:each="prod : ${prods}">
  2. ...
  3. </tr>

Thymeleaf 为您提供了一种在不使用迭代的情况下声明局部变量的方法th:with属性,其语法与属性值类似:

  1. <div th:with="firstPer=${persons[0]}">
  2. <p>
  3. The name of the first person is <span th:text="${firstPer.name}">Julius Caesar</span>.
  4. </p>
  5. </div>

th:with被处理时,firstPer变量创建为局部变量,并添加到来自上下文的变量 map 中,以便它是可用于评估以及在上下文中声明的任何其他变量,但只能在包含<div>标签的范围内。

可以同时定义多个变量,赋值语法为:

  1. <div th:with="firstPer=${persons[0]},secondPer=${persons[1]}">
  2. <p>
  3. The name of the first person is <span th:text="${firstPer.name}">Julius Caesar</span>.
  4. </p>
  5. <p>
  6. But the name of the second person is
  7. <span th:text="${secondPer.name}">Marcus Antonius</span>.
  8. </p>
  9. </div>

th:with属性允许重用在同一属性中定义的变量:

  1. <div th:with="company=${user.company + ' Co.'},account=${accounts[company]}">...</div>

9 属性优先级

当在同一个标签中写入多个th:*属性时,会发生什么? 对于下面的例子:

  1. <ul>
  2. <li th:each="item : ${items}" th:text="${item.description}">Item description here...</li>
  3. </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

这个优先机制意味着如果属性位置被反转,上述迭代片段将给出完全相同的结果(尽管它的可读性稍差):

  1. <ul>
  2. <li th:text="${item.description}" th:each="item : ${items}">Item description here...</li>
  3. </ul>

10 注释

10.1 标准HTML/XML注释

标准HTML/XML注释<!- ... - >可以在Thymeleaf模板中的任何地⽅使⽤。这些注释中的任何内容都不会被Thymeleaf处理,并将逐字复制到结果中:

  1. <!-- User info follows -->
  2. <div th:text="${...}">
  3. ...
  4. </div>

10.2 Thymeleaf解析器级注释

解析器级注释块是当Thymeleaf解析时将简单地从模板中删除的代码。 他们看起来像这样:

  1. <!--/* This code will be removed at Thymeleaf parsing time! */-->

Thymeleaf将删除<!--/**/-->之间的所有内容,因此当模板静态打开时,这些注释块也可⽤于显示代码,但当Thymeleaf处理它时,它将被删除:

  1. <!--/*-->
  2. <div>
  3. you can see me only before Thymeleaf processes me!
  4. </div>
  5. <!--*/-->

对于具有很多<tr>的设计原型表,这可能会⾮常⽅便,例如:

  1. <table>
  2. <tr th:each="x : ${xs}">
  3. ...
  4. </tr>
  5. <!--/*-->
  6. <tr>
  7. ...
  8. </tr>
  9. <tr>
  10. ...
  11. </tr>
  12. <!--*/-->
  13. </table>

10.3 Thymeleaf专有注释

当模板被静态打开(即作为原型)时,Thymeleaf允许定义特殊注释块,并且在执⾏模板时,Thymeleaf被认为是正常的标记。

  1. <span>hello!</span>
  2. <!--/*/
  3. <div th:text="${...}">
  4. ...
  5. </div>
  6. /*/-->
  7. <span>goodbye!</span>

Thymeleaf的解析系统将简单地删除<!--/*//*/-->标记,但不删除包含其中的内容,因此标记之间的内容将被保留。 所以当执⾏模板时,Thymeleaf实际上会看到这样的:

  1. <span>hello!</span>
  2. <div th:text="${...}">
  3. ...
  4. </div>
  5. <span>goodbye!</span>

与解析器级注释块⼀样,此功能与⽅⾔⽆关。

10.4 应用示例

(1)Thymeleaf 模板

  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h3>标准HTML/XML注释</h3>
  9. <!--标准HTML/XML注释
  10. <p>简单测试111111</p>
  11. -->
  12. <hr>
  13. <h3>Thymeleaf解析器级注释</h3>
  14. <!--/*
  15. Thymeleaf解析器级注释
  16. <p>简单测试222222</p>
  17. */-->
  18. <hr>
  19. <h3>Thymeleaf专有注释</h3>
  20. <!--/*/
  21. <p>简单测试333333</p>
  22. /*/-->
  23. </body>
  24. </html>

(2) 执行效果

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h3>标准HTML/XML注释</h3>
  9. <!--标准HTML/XML注释
  10. <p>简单测试111111</p>
  11. -->
  12. <hr>
  13. <h3>Thymeleaf解析器级注释</h3>
  14. <hr>
  15. <h3>Thymeleaf专有注释</h3>
  16. <p>简单测试333333</p>
  17. </body>
  18. </html>

1019005.png