Content Table

Thymeleaf 语法

Thymeleaf 使用 HTML 元素的属性获取 model 中的数据,属性的前缀是 th:,例如 th:text, th:src

变量访问

1
2
3
4
5
6
7
<!-- 使用属性的方式访问变量 -->
<span th:text="${name}">Thymeleaf 解析后会被覆盖</span>
<span th:text="|Welcome ${name}|">Thymeleaf 解析后会被覆盖</span>
<span th:text="'Welcome ' + ${name}">Thymeleaf 解析后会被覆盖</span>

<!-- 非属性的方式访问变量 -->
<span>[[${name}]]</span>

字符串拼接时 |...| 的方式更简洁,但是里面不能包含表达式,第三种方式功能强大,可以包含表达式。

变量访问也可以使用级联的方式: ${user.name}

null 处理

表达式 ${foo}? 当 foo 为 null 时返回 false;级联调用时变量访问前先用 ? 进行判断可以减少一个一个的条件判断

1
2
<div th:text="${foo}?'Alt'"></div>
<div th:text="${foo?.bar?.fix}"></div>

使用 URL

1
<a th:href="@{/login}" th:if="${session.user == null}">Login</a>

使用 th:href="@{/uri}" 引入 URL,/ 开头时会在 URI 前面加上项目的 context path

运算符

1
2
3
4
5
<!-- model 中 age 为 30 -->
<span th:text="|${age} 大于 20|" th:if="${age} > 20">显示</span>
<span th:text="|${age} 小于 20|" th:if="${age} < 20">不显示</span>
<span th:text="4%2==0 ? '偶数' : '素数'"></span>
<span th:text="3%2==0 ? '偶数' : '素数'"></span>

逻辑运算符 ><<=>===!=三元表达式 等都可以使用

if else

1
2
3
4
5
6
7
8
<body>
<div th:if="${age} > 20">
大于 20
</div>
<div th:unless="${age} > 20">
小于 20
</div>
</body>

使用 if unless 实现 if else 的功能,unless 是除了的意思。

Thymeleaf 里没有啥好办法实现 if, else if, eles 的功能。

switch

1
2
3
4
5
<div th:switch="${role}">
<p th:case="user">User</p>
<p th:case="${admin}">Admin</p>
<p th:case="*">Unknown</p>
</div>

默认属性 default 可以用 * 表示

循环

1
2
3
<ul>
<li th:each="animal : ${animals}" th:text="${animal}">小动物 - 此处文本会被覆盖</li>
</ul>

循环使用 th:each,上面会重复生成多个 li

格式化时间

1
2
<!-- Controller 中 model.put("date", new Date()); -->
<p th:text="${#dates.format(date, 'yyyy-MM-dd HH:mm:ss')}"></p>

Utility

为了模板更加易用,Thymeleaf 内置了一些 utility 函数,可以通过 # 直接访问,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
${#dates.setFormat(datesSet)}
${#dates.format(date)}
${#dates.format(date, 'dd/MMM/yyyy HH:mm')}

${#strings.isEmpty(name)}
${#strings.contains(name,'ez')}
${#strings.startsWith(name,'Don')}
${#strings.trim(str)}

${#numbers.formatInteger(num,3)}
${#numbers.formatDecimal(num,3,2)}
${#numbers.setFormatInteger(numSet,3,'POINT')}
${#numbers.setFormatDecimal(numSet,3,2)}
${#numbers.setFormatDecimal(numSet,3,2,'COMMA')}

...

常用 th

  • th:attr: 可以同时设置很多属性,也可以设置自定义属性,偷懒的时候就不用记忆很多 th: 开头的属性了

    1
    2
    <p th:attr="src=@{/images/gtvglogo.png}, title=${logo}, alt=${logo}"></p>
    <p th:attr="data-level=${level}, title=${level}" th:text="${level}"></p>
  • th:text

  • th:utext

  • th:name

  • th:value

  • th:class

  • th:href

  • th:src

  • th:alt

  • th:title

  • th:field

  • Appending and prepending

    • th:attrappend

    • th:attrprepend

      1
      2
      3
      4
      5
      6
      <body>
      <!-- 以下表达式效果都一样 -->
      <p class="foo" th:attrappend="class=${' '+level}">Hello</p>
      <p class="foo" th:attrappend="class=' '+${level}">Hello</p>
      <p class="foo" th:attrappend="class=| ${level}|">Hello</p>
      </body>
  • th:checked

  • th:selected

  • th:object

  • 具体请参考 Setting value to specific attributes

文件包含

很多页面的头部和尾部都是一样的,可以用 th:fragment 把公共部分的代码定义到文件里,然后在模板文件中使用 th:replace 或者 th:insert 引用需要的 fragment。

在 fragments.html 中定义公共部分

1
2
3
4
5
6
7
<div th:fragment="header" class="header">
Header
</div>

<div th:fragment="footer" class="footer">
Footer
</div>

一个文件里可以定义多个 fragment,每个 fragment 有自己的名字,按 fragment 的名字进行引用。

在其他文件如 index.html 中使用

1
2
3
4
5
...
<div th:replace="templateDir/fragments.html::header">头部</div>
...
<div th:replace="templateDir/fragments.html::footer">尾部</div>
...

th:replace 的值为: fragment 文件的模板路径::fragmentName

引用 fragment 有以下 3 种方式:

  • th:insert: 保留自己的主标签,保留 th:fragment 的主标签
  • th:replace: 不要自己的主标签,保留 th:fragment 的主标签
  • th:include: 保留自己的主标签,不要 th:fragment 的主标签 (官方 3.0 后不推荐)

动态计算 fragments 文件的路径

  1. Controller 中计算好路径放到 model 中:

    1
    model.put("fragments", portalService.fragmentsPath(host));
  2. 页面中通过 ${fragments} 应用:

    1
    <div th:replace="${fragments} :: header">头部</div>

这种情况在一个服务器,支持不同域名的网站定义自己的页面时很有用。

参考资料