# SpringBoot 使用 Thymeleaf 模板渲染

# 前言

Thymeleaf 目前最新版本3.0 Thymeleaf作为Spring-Boot官方推荐模板引擎,而且支持纯HTML浏览器展现(模板表达式在脱离运行环境下不污染html结构).是时候了解一番了。

# 安装与初始化配置

与Spring-Boot集成:

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

在Spring-Boot中只需如下配置:

一下内容除了 cache=false 以外, 其他的都是默认配置, 可以省略

#thymeleaf start
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
#开发时关闭缓存,不然没法看到实时页面
spring.thymeleaf.cache=false
#thymeleaf end

具体可以配置的参数可以查看 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties 这个类,上面的配置实际上就是注入到该类中的属性值.

# 基本语法

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>hello</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<!--/*@thymesVar id="name" type="java.lang.String"*/-->
<p th:text="'Hello!, ' + ${name} + '!'" >3333</p>
</body>
</html>

# 表达式

  • Variable Expressions (变量表达式) : ${...}

    变量表达式即OGNL表达式或Spring EL表达式(在Spring术语中也叫model attributes)。

    例如:

    <span th:text="${book.author.name}">  
    <li th:each="book : ${books}">  
    
  • Selection Variable Expressions (选择表达式) : *{...}

    选择表达式很像变量表达式,不过它们用一个预先选择的对象来代替上下文变量容器(map)来执行

    例如:

    <!-- 被指定的object由th:object属性定义 -->
    <div th:object="${book}">  
        ...  
        <span th:text="*{title}">...</span>  
        ...  
    </div>  
    
  • Message Expressions(国际化表达式): #{...}

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

    <table>  
        ...  
        <th th:text="#{header.address.city}">...</th>  
        <th th:text="#{header.address.country}">...</th>  
        ...  
    </table>  
    
  • Link URL Expressions(连接表达式): @{...}

    URL表达式指的是把一个有用的上下文或回话信息添加到URL,这个过程经常被叫做URL重写。

    @{/order/list}

    URL还可以设置参数:

    @{/order/details(id=${orderId})}

    相对路径:

    @{../documents/report}

    例如:

    <form th:action="@{/createOrder}"><a href="main.html" th:href="@{/main}">
    
  • Fragment Expressions(片段表达式): ~{...}

    下面代码块引入的部分再讲

# 数据类型

  • 文本: ‘one text’,’another text’,…

    文本文字可以用单引号来包含。需要转义的话可以用\’转义

    <p>
    Now you are looking at a <span th:text="'working web application'">template file</span>.
    </p>
    
  • 数字: 1,2,1.2,…

    <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>
    
  • 布尔: true,false

    <div th:if="${user.isAdmin()} == false">
    

    注意,在上面的例子中,= = false写在括号外,因此是Thymeleaf本身负责解析解析它。如果是写在括号内,它将由OGNL负责解析:

    <div th:if="${user.isAdmin() == false}">
    
  • 空值: null

    <div th:if="${variable.something} == null">
    
  • 单词: something,main,name,…

# 字符串操作

  • 字符串拼接: +

    <p th:text="'The name of the user is ' + ${user.name}"></p>
    
  • 高级文本连接: |The name is ${name}|

    我们可以用“|”包含住想要连接的文本,替换’…’ + ‘…’方式,这样就可以省心不少。

    <span th:text="|Welcome to our application, ${user.name}!|">
    

    等价于

    <span th:text="'Welcome to our application, ' + ${user.name} + '!'">
    

    除此之外, 我们还可以这样玩

    <span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
    

    注意:${…}表达式可以被放在|….|之间,但是不能放在’….’之间哦

# 算术运算

也可以用一些算术运算符:+ , - , * , / , % .

<a th:with="isEven=(${prodStat.count} % 2 == 0)"></a>

# 比较运算符

> , < , >=<===!= 都可以用,但是 <, 这两个在必须转义。

<p th:if="${prodStat.count} &gt; 1"></p>
<p th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')"></p>

注: 别名替代方案 gt ( >), lt ( < ), ge ( >= ), le ( <= ), not ( ! ), eq ( == ), neq / ne ( != ).

# 条件运算符

  • If-then(if 语句): (if) ? (then)

    <tr th:class="${row.even}? 'even'">
    ...
    </tr>
    
  • If-then-else(if else 语句): (if) ? (then) : (else)

    <tr th:class="${row.even} ? 'even' : 'odd'">
    ...
    </tr>
    

    也可以这样使用

    <tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">
    ...
    </tr>
    
  • Default(默认表达式): (value) ?: (defaultvalue)

    这样书写的可读性比较差, 不太建议使用

    <div th:object="${session.user}">
        ...
        <p>Age: <span th:text="*{age}?: '(no age specified)'">27</span>.</p>
    </div>
    

    可以这样表达

    <p>Age: <span th:text="*{age != null}? *{age} : '(no age specified)'">27</span>.</p>
    
  • No-Operation(用于禁止转义): _

    无操作标记(token)

    Thymeleaf 3.0 另一个新的特性就是无操作(NO-OP no-operation)标记,下划线”_”,代表什么也不做。

    <!-- 当user.name 为空的时候,直接输出标签体中的内容 -->
    <span th:text="${user.name} ?: _">no user authenticated</span>
    

# 预处理表达式

语法: __${expression}__

有的时候我们需要预处理一些信息到表达式中。比如某个变量的名字是变的,怎么办?预处理来了。

预处理表达式用 __${expression}__ 双下划线包裹,举个栗子: 我们在外部资源文件中配了这个属性:

<span article.text=@myapp.translator.Translator@translateToSpanish({0})></span>

我们可以在模板中表达式是这样子的:

<p th:text="${__#{article.text('textVar')}__}">Some text here...</p>

那么引擎会首先从资源文件中获取article.text的值,再执行它。

<p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p>

# 条件判断

if/unless

Thymeleaf中使用th:if和th:unless属性进行条件判断,标签只有在th:if中条件成立时才显示,th:unless于th:if恰好相反,只有表达式中的条件不成立,才会显示其内容。

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

switch

Thymeleaf同样支持多路选择Switch结构,默认属性default可以用*表示:

<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>
</di

# 循环

<tr th:each="prod : ${prods}">
    <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>

迭代对象必须为

  • Any object implementing java.util.Iterable、 java.util.Enumeration、java.util.Iterator
  • Any object implementing java.util.Map. When iterating maps, iter variables will be of class java.util.Map.Entry.
  • Any array.
  • Any other object will be treated as if it were a single-valued list containing the object itself.
<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>
//不过也可以直接加Stat后缀访问状态变量
<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>

th:each内置迭代状态属性:

  • index ,当前索引,从0开始。
  • count,当前数目,从1开始。
  • size,总大小
  • current,当前值
  • even/odd boolean properties.
  • first boolean property.
  • last boolean property.

# 注释

普通html注释: Thymeleaf 注释:

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

<!--/*--> 
  <div>
     you can see me only before Thymeleaf processes me!
  </div>
<!--*/-->

<!--/*/
  <div th:text="${...}">
    ...
  </div>
/*/-->

# html内联

//不会转义时
<p>The message is "[(${msg})]"</p>
//等价于
<p>The message is "This is <b>great!</b>"</p>

//转义时
<p>The message is "[[${msg}]]"</p>
//等价于
<p>The message is "This is &lt;b&gt;great!&lt;/b&gt;"</p>

//禁用内联
<p th:inline="none">A double array looks like this: [[1, 2, 3], [4, 5]]!</p>

//js内联
<script th:inline="javascript">
    ...
    var username = [[${session.user.name}]];
    ...
</script>


//css内联
<style th:inline="css">
    .[[${classname}]] {
      text-align: [[${align}]];
    }
</style>

# 片段(Fragment)表达式

Thymeleaf 3.0 引入了一个新的片段表达式。形如:~{commons::footer}。 该特性十分有用(比如解决定义通用的header和footer的问题) base.html

<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>

main.html

<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>

片段经常和th:insert or th:replace一起使用

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

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

~{::selector} or ~{this::selector}引用本模板内的片段

不使用th:fragment定义的片段的情况:

<div id="copy-section">
  &copy; 2011 The Good Thymes Virtual Grocery
</div>
 <div th:insert="~{footer :: #copy-section}"></div>

# th:insert , th:replace , th:include 的区别:

  • th:insert 插入片段本身, 包含片段的内容到当前标签内
  • th:replace 用整个片段(内容和上一层)替换当前标签(不仅仅是标签内容).
  • th:includeth:insert 不同的是,它插入的是片段解析后的内容

例如:

<footer th:fragment="copy">
&copy; 2011 The Good Thymes Virtual Grocery
</footer>
<body>
    ...
    <div th:include="footer :: copy"></div>
    <div th:replace="footer :: copy"></div>
</body>

编译后:

<body>
	...
    <div>
        &copy; 2011 The Good Thymes Virtual Grocery
    </div>
    <footer>
        &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
</body>        

# 可带参数的片段标签

<div th:fragment="frag (onevar,twovar)">
    <p th:text="${onevar}+' - ' +${twovar}">...</p>
</div>
<div th:include="::frag(${value1},${value2})">...</div>
<div th:include="::frag(onevar=${value1},twovalue=${vaule2})"></div>
<div th:include="::frag(twovalue=${vaule2},onevar=${value1})"></div>

即使标签没有定义参数,like this:

<div th:fragment="frag">
...
</div>

我们还是可以用这句:

<div th:include="::frag(onevar=${value1},twovar=${value2})"></div>
//等价于 th:include和th:with
<div th:include="::frag" th:with="onevar=${value1},twovar=${value2}"></div>

# 内置对象

# 基本的对象

#ctx: the context object.
#vars: the context variables.
#locale: the context locale.
#request: (only in Web Contexts) the HttpServletRequest object.
#response: (only in Web Contexts) the HttpServletResponse object.
#session: (only in Web Contexts) the HttpSession object.
#servletContext: (only in Web Contexts) the ServletContext object.

# 工具对象

#execInfo: information about the template being processed.
#messages: methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
#uris: methods for escaping parts of URLs/URIs
#conversions: methods for executing the configured conversion service (if any).
#dates: methods for java.util.Date objects: formatting, component extraction, etc.
#calendars: analogous to #dates, but for java.util.Calendar objects.
#numbers: methods for formatting numeric objects.
#strings: methods for String objects: contains, startsWith, prepending/appending, etc.
#objects: methods for objects in general.
#bools: methods for boolean evaluation.
#arrays: methods for arrays.
#lists: methods for lists.
#sets: methods for sets.
#maps: methods for maps.
#aggregates: methods for creating aggregates on arrays or collections.
#ids: methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).

工具对象的使用方式见:http://www.thymeleaf.org/doc/..., 以下仅仅举几个例子

${#dates.format(date, 'dd/MMM/yyyy HH:mm')}
${#dates.arrayFormat(datesArray, 'dd/MMM/yyyy HH:mm')}
${#dates.listFormat(datesList, 'dd/MMM/yyyy HH:mm')}
${#dates.setFormat(datesSet, 'dd/MMM/yyyy HH:mm')}
${#dates.createNow()}
${#dates.createToday()} //time set to 00:00

${#strings.isEmpty(name)}  //Check whether a String is empty (or null)
${#strings.arrayIsEmpty(nameArr)}
${#strings.listIsEmpty(nameList)}
${#strings.setIsEmpty(nameSet)} 

${#strings.startsWith(name,'Don')}                  // also array*, list* and set*
${#strings.endsWith(name,endingFragment)}           // also array*, list* and set*

${#strings.length(str)}
${#strings.equals(str)}
${#strings.equalsIgnoreCase(str)}
${#strings.concat(str)}
${#strings.concatReplaceNulls(str)}

# Thymeleaf 3中的一些变化和特性

  1. 模板变化 推荐你去掉模板中的 th:inline=“text” 属性。因为在HTML或XML模板中,不再需要该属性去支持文本中内联表达式的特性。

  2. 完整的HTML5 标记支持 不在强制要求标签闭合,属性加引号等等

  3. 模板类型 Thymeleaf 3 移除了之前版本的模板类型,新的模板类型为:HTML、XML、TEXT、JAVASCRIPT、CSS、RAW

  4. 文本型模板

    文本型模板使得Thymeleaf可以支持输出CSS、Javascript和文本文件。在你想要在CSS或Javascript文件中使用服务端的变量时;或者想要输出纯文本的内容时。 在文本模式中使用Thymeleaf的特性,你需要使用一种新的语法,

    例如:

    [# th:each="item : ${items}"]
      - [# th:utext="${item}" /]
    [/]
    var a = [# th:text="${msg}"/];
    

增强的内联机制

现在可无需额外的标签,直接在文本中输出数据:

This product is called [[${product.name}]] and it's great!
var a = [[${msg}]];

# 项目源码

SpringBoot 教程: https://github.com/lixian13149999/spring-boot-learn

对应示例项目: thymeleaf

参考: http://www.thymeleaf.org/doc/...

http://www.thymeleaf.org/doc/...

http://blog.csdn.net/u0127068...

https://www.tianmaying.com/tu...

http://www.thymeleaf.org/doc/...

https://segmentfault.com/a/...

https://www.cnblogs.com/nuo...

https://blog.csdn.net/u0...

上次更新时间: 2020/6/17 下午5:44:30