Thymeleaf 模版引擎简单使用

218 阅读5分钟

文章目录


1. 引入

Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,相比于其他的模版引擎,它具有如下的优势:

  • Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 Thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示
  • Thymeleaf 开箱即用的特性。它提供标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL表达式效果,避免每天套模板、改 Jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言
  • Thymeleaf 提供 Spring 标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能

2. 表达式

Themleaf中标准的表达式可以分为四类:

  • 变量表达式
  • 选择表达式
  • 文字国际化表达式
  • URL表达式

2.1 变量表达式

使用前首先导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>nz.net.ultraq.thymeleaf</groupId>
    <artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>

变量表达式即 OGNL 表达式或 Spring EL 表达式,如果后端通过Model传过来一个count属性值,那么在前端页面使用th:text=${count}就可以获取到相应的值。此时count属性存在于上下文(context)变量容器(map)中,通过指定的键来获取对应的值。

@Controller
public class IndexController {

    @GetMapping("/hello")
    public String hello(Model model){
        model.addAttribute("count", 10);
        return "index";
    }
}
<!DOCTYPE html>
<!--导入Thymleaf的命名空间-->
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h2 th:text="${count}">20</h2>
</body>
</html>

当通过localhost:8080/hello访问时,此时通过表达式得到count的值10来代替静态的20 。因此,页面对应h1的区域输出为10。

ID	Name
10	Forlogen

2.2 选择表达式

变量表达式使用的是一个上下文变量容器,而选择表达式使用的是一个预先选择的对象,如果理解呢?假设此时程序定义了一个实体类Person:

@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public class Person {

    @Getter
    @Setter
    private Integer id;

    @Getter
    @Setter
    private String name;
}

在controller中想要往前端页面传递一个Person对象:

 @GetMapping("/person")
    public String person(Model model){
        model.addAttribute("person", Person.builder().id(10).name("Forlogen").build());
        return "index";
    }

如果使用选择表达式,前端通过Thymleaf获取传过来的person中属性值的方式为:

<table th:object="${person}">
    <tr>
        <th>ID</th>
        <th>Name</th>
    </tr>
    <tr>
        <td th:text="*{id}">20</td>
        <td th:text="*{name}">Hello</td>
    </tr>
</table>

因为选择表达式使用的是一个预先选择的对象,即上面通过th:object获取的person对象。因此,下面获取person的属性值只需要使用*{属性}的方式。如果使用的是变量表达式,那么需使用${persion.属性}的方式:

<table>
    <tr>
        <th>ID</th>
        <th>Name</th>
    </tr>
    <tr>
        <td th:text="${person.id}">20</td>
        <td th:text="${person.name}">Hello</td>
    </tr>
</table>

2.3 文字国际化表达式

文字国际化表达式可以从一个外部文件中获取区域文件信息,这里使用的同样是key-value的键值对形式。例如,此时程序中需使用国际化功能,首先需要在Spring Boot的全局配置文件中进行国际化文件的配置:

spring.messages.basename=message

spring.messages.basename的默认值就是message,如果国际化配置文件名为message.properties可以选择省略上述的配置。然后在message.properties中添加国际化属性,这里只是简单的添加一下属性:

index.address=China
index.language=Chinese

当在前端想要获取上面设置的属性值时,可以在HTML页面中使用#{属性}进行获取:

<table>
    <tr>
        <th th:text="#{index.address}"></th>
        <th th:text="#{index.language}"></th>
    </tr>
</table>

执行访问,此时页面输出

China	Chinese

2.4 URL表达式

URL表达式将一个有用的上下文或会话信息添加在URL中,前端页面中使用@{url}的方式使用。常配合th:action来进行动作的执行操作,例如

<form th:action="@{/createOrder}">  

或者通过th:href进行页面的跳转:

<h2 href="index.html" th:href="@{/index}"></h2>

3. 常用th标签

关键字功能介绍案例
th:id替换id<input th:id="'xxx' + ${collect.id}"/>
th:text文本替换<p th:text="${collect.description}">description</p>
th:utext支持html的文本替换<p th:utext="${htmlcontent}">conten</p>,此时htmlcontent为传递的HTML格式的文本
th:object替换对象<div th:object="${session.user}">
th:value属性赋值<input th:value="${user.name}" />
th:with变量赋值运算<div th:with="isEven=${prodStat.count}%2==0"></div>
th:style设置样式th:style="'display:' + @{(${sitrue} ? 'none' : 'inline-block')} + ''"
th:onclick点击事件th:onclick="'getCollect()'"
th:each属性赋值tr th:each="user,userStat:${users}">
th:if判断条件<a th:if="${userId == collect.userId}" >
th:unless和th:if判断相反<a th:href="@{/login}" th:unless=${session.user != null}>Login</a>
th:href链接地址<a th:href="@{/login}" th:unless=${session.user != null}>Login</a> />
th:switch多路选择 配合th:case 使用<div th:switch="${user.role}">
th:caseth:switch的一个分支<p th:case="'admin'">User is an administrator</p>
th:fragment布局标签,定义一个代码片段,方便其它地方引用<div th:fragment="alert">,常用于局部内容刷新
th:include布局标签,替换内容到引入的文件<head th:include="layout :: htmlhead" th:with="title='xx'"></head> />
th:replace布局标签,替换整个标签到引入的文件<div th:replace="fragments/header :: title"></div>
th:selectedselected选择框 选中th:selected="(${xxx.id} == ${configObj.dd})"
th:src图片类地址引入<img class="img-responsive" alt="App Logo" th:src="@{/img/logo.png}" />
th:inline定义js脚本可以使用变量<script type="text/javascript" th:inline="javascript">
th:action表单提交的地址<form action="subscribe.html" th:action="@{/subscribe}">
th:remove删除某个属性<tr th:remove="all"> 1.all:删除包含标签和所有的孩子。2.body:不包含标记删除,但删除其所有的孩子。3.tag:包含标记的删除,但不删除它的孩子。4.all-but-first:删除所有包含标签的孩子,除了第一个。5.none:什么也不做。这个值是有用的动态评估。
th:attr设置标签属性,多个属性可以用逗号分隔比如th:attr="src=@{/image/aa.jpg},title=#{logo}",此标签不太优雅,一般用的比较少。

3.1 字符串

例如使用th:text不仅可以简单的进行文本的替换,还可以进行字符串的拼接:

<span>
    <p th:text="'person id is:'  + ${person.id} + ', person name is: ' + ${person.name}">person desc</p>
    <p th:text="|person id is: ${person.id}, person name is:  ${person.name}|"></p>
</span>

上面的两种方式可以获得同样的效果

person id is:10, person name is: Forlogen
person id is: 10, person name is: Forlogen

3.2 条件判断

使用th:ifth:unless可以在前端页面中进行条件判断,th:if只有在满足条件时才怎么怎么样,而th:unless正好相反,只有在条件不成立时才怎么怎么样。

例如向前端传递两个person对象,不同之处在于id的设置:

@GetMapping("/person")
public String person(Model model){
    model.addAttribute("person", Person.builder().id(10).name("Forlogen").build());
    model.addAttribute("person1", Person.builder().id(2).name("Forlogen").build());
    return "index";
}

HTML页面中使用th:ifth:unless进行判断。因为,person.id=10 > 5 成立,页面会显示10;而person1.id =1 > 5不成立,因此页面也会显示2。

<h2 th:text="${person.id}" th:if="${person.id > 5}"></h2>
<h3 th:text="${person1.id}" th:unless="${person1.id > 5}"></h3>

3.3 遍历

使用th:each可以进行集合的遍历,例如向前端传递一个Person的list,然后在前端使用th:each来获取list中的值:

@GetMapping("/person/list")
    public String list(Model model){
        List<Person> persons = new ArrayList<>();
        Collections.addAll(persons,
                Person.builder().id(1).name("Forlgoen").build(),
                Person.builder().id(2).name("kobe").build());

        model.addAttribute("persons", persons);

        return "index :: personList";
    }

前端还使用了th:fragment来进行局部刷新

<table th:fragment="personList" th:each="person, iterStat  : ${persons}">
    <tr>
        <th>ID</th>
        <th>Name</th>
    </tr>
    <tr>
        <td th:text="${person.id}">20</td>
        <td th:text="${person.name}">Hello</td>
        <td th:text="${iterStat}"></td>
    </tr>
</table>

此时页面输出为

ID	Name
1	Forlgoen	{index = 0, count = 1, size = 2, current = Person(id=1, name=Forlgoen)}
ID	Name
2	kobe	{index = 1, count = 2, size = 2, current = Person(id=2, name=kobe)}

th:each标签中的iterStat为状态变量,属性有:

  • index:当前迭代对象的 index(从0开始计算)
  • count: 当前迭代对象的 index(从1开始计算)
  • size:被迭代对象的大小
  • current:当前迭代变量
  • even/odd:布尔值,当前循环是否是偶数/奇数(从0开始计算)
  • first:布尔值,当前循环是否是第一个
  • last:布尔值,当前循环是否是最后一个

3.4 URL

如果是静态页面实现页面的跳转,可以使用a标签:

<a href="index.html"></a>

而使用Thymleaf进行渲染时,需使用th:href:

<a  href="index.html" th:href="@{/index}"></a>

如果连接中包含参数,可使用如下的方式:

<a href="#" th:href="@{/person/{id}(id=${person.id})}"  th:text="${blog.title}"></a>

此时会使用person.id替换路径中的id。

3.5 内联JS

如果想要在script标签中内敛文本,首先需使用th:inline="javascript"进行激活,然后使用[[…]]格式进行值的获取。

<script th:inline="javascript">
    var id = /*[[${person.id}]]*/"10";
    var name = /*[[${person.name}]]*/"Forlogen";
</script>

其中使用/**/包裹表示只有在运行时才动态渲染,使用其中获取到的值。


4. 布局

当构建一个网站时,常用的导航栏、底部等常常是相同的,如果在每个页面中都单独的编写这部分的代码,不免显得有点冗余,此时就可以使用th:fragment进行相同内容的抽取,然后使用th:insertth:replace进行布局的随处引用。

例如此时在_fragments.html中定义head如下所示:

<head th:fragment="head(title)">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title th:replace="${title}">博客详情</title>
</head>

那么在其他的页面的head标签中只需要引入即可:

<head th:replace="_fragments :: head(~{::title})">
    <title>首页</title>
</head>

th:insert 和 th:replace 区别:insert 只是加载,replace 是替换。Thymeleaf 3.0 推荐使用 th:insert 替换 2.0 的 th:replace。


5. 内嵌变量

为了模板更加易用,Thymeleaf 还提供了一系列 Utility 对象(内置于 Context 中),可以通过 # 直接访问:

  • datesjava.util.Date的功能方法类。
  • calendars : 类似#dates,面向java.util.Calendar
  • numbers : 格式化数字的功能方法类
  • strings : 字符串对象的功能类,contains,startWiths,prepending/appending等等。
  • objects: 对objects的功能类操作。
  • bools: 对布尔值求值的功能方法。
  • arrays对数组的功能类方法。
  • lists: 对lists功能类方法
  • sets
  • maps

例如:

# 日期格式化
${#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()}
${#strings.isEmpty(name)}
${#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)}

${#strings.randomAlphanumeric(count)}

6. 参考

新一代Java模板引擎Thymeleaf

Tutorial: Thymeleaf + Spring

Spring Boot(四):Thymeleaf 使用详解