thymeleaf是服务器端模板引擎,能够处理HTML、XML、JavaScript、CSS、文本等。thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
thymeleaf官网
thymeleaf官网使用手册
1.spring配置
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
2.thymeleaf使用详细配置
#配置到properties文件中
#thymeleaf start
#spring.thymeleaf.mode的默认值是HTML5,其实是一个很严格的检查,改为LEGACYHTML5可以得到一个可能更友好亲切的格式要求
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
#开发时关闭缓存,不然没法看到实时页面
spring.thymeleaf.cache=false
#thymeleaf end
注:LEGACYHTML5需要搭配一个额外的库NekoHTML才可用,需在pom.xml文件中添加该依赖,如下:
net.sourceforge.nekohtml nekohtml 1.9.22
@Controller
public class HelloWorldController {
private static final Logger log = LoggerFactory.getLogger(HelloController.class);
@GetMapping(value = "/helloworld")
public String hello(Model model) {
String name = "thymeleaf";
model.addAttribute("name", name);
return "helloworld";
}
}
helloworld.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>hellotitle>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
head>
<body>
<p th:text="${name} + ',HelloWorld,Welcome!'">静态页面文本显示p>
body>
html>
## 四、基础语法
1.使html支持thymeleaf
<html xmlns:th="http://www.thymeleaf.org">
2.获取变量值${}
<p th:text="${name} + ',HelloWorld,Welcome!'">静态页面文本显示p>
3.选择变量表达式*{…}
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastianspan>.p>
<p>Surname: <span th:text="*{lastName}">Pepperspan>.p>
<p>Nationality: Saturnspan>.p>
div>
<div>
<p>Name: <span th:text="${session.user.firstName}">Sebastianspan>.p>
<p>Surname: <span th:text="${session.user.lastName}">Pepperspan>.p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturnspan>.p>
div>
里面的原有的值是为给前端开发时做展示用的,这样在无网络的情况下也能运行。即完全可以前端先写出页面,模拟数据展现效果,后端人员再拿此模板修改即可。
4.链接表达式: @{…}
用来配合link、src、href使用的语法,类似的标签有:th:href和th:src
<a href="userProfile.html" th:href="@{http://localhost:8080/demo/profile(userId=${user.id})}">viewa>
<a href="userProfile.html" th:href="@{/demo/profile(userId=${user.id})}">viewa>
5.消息表达式#{}
通常与th:text属性一起使用,指明声明了th:text标签的文本是#{}中的key多对应的value,而标签内的文本将不显示
welcome<p>
6.工具对象表达式 #maps
常用于日期、集合、数组对象的访问。这些工具对象就是java对象,可以访问对应java对象的方法来进行各种操作。
<div th:if="${#maps.size(studentVO.students[__${rowStat.index}__].score)!=0}">
<label>${score.key}:label><input type="text" th:value="${score.value}">
div>
其他工具对象表达式还有:#dates、#calendars、#numbers、#strings、#objects、#bools、#arrays、#lists、#sets
工具对象参考官网手册
/*
* Format date with the specified pattern
* Also works with arrays, lists or sets
*/
${#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')}
/*
* Create a date (java.util.Date) object for the current date and time
*/
${#dates.createNow()}
/*
* Create a date (java.util.Date) object for the current date (time set to 00:00)
*/
${#dates.createToday()}
/*
* Format calendar with the standard locale format
* Also works with arrays, lists or sets
*/
${#calendars.format(cal)}
${#calendars.arrayFormat(calArray)}
${#calendars.listFormat(calList)}
${#calendars.setFormat(calSet)}
/*
* Format calendar with the ISO8601 format
* Also works with arrays, lists or sets
*/
${#calendars.formatISO(cal)}
${#calendars.arrayFormatISO(calArray)}
${#calendars.listFormatISO(calList)}
${#calendars.setFormatISO(calSet)}
/*
* Format calendar with the specified pattern
* Also works with arrays, lists or sets
*/
${#calendars.format(cal, 'dd/MMM/yyyy HH:mm')}
${#calendars.arrayFormat(calArray, 'dd/MMM/yyyy HH:mm')}
${#calendars.listFormat(calList, 'dd/MMM/yyyy HH:mm')}
${#calendars.setFormat(calSet, 'dd/MMM/yyyy HH:mm')}
/*
* Obtain calendar properties
* Also works with arrays, lists or sets
*/
${#calendars.day(date)} // also arrayDay(...), listDay(...), etc.
${#calendars.month(date)} // also arrayMonth(...), listMonth(...), etc.
${#calendars.monthName(date)} // also arrayMonthName(...), listMonthName(...), etc.
${#calendars.monthNameShort(date)} // also arrayMonthNameShort(...), listMonthNameShort(...), etc.
${#calendars.year(date)} // also arrayYear(...), listYear(...), etc.
${#calendars.dayOfWeek(date)} // also arrayDayOfWeek(...), listDayOfWeek(...), etc.
${#calendars.dayOfWeekName(date)} // also arrayDayOfWeekName(...), listDayOfWeekName(...), etc.
${#calendars.dayOfWeekNameShort(date)} // also arrayDayOfWeekNameShort(...), listDayOfWeekNameShort(...), etc.
${#calendars.hour(date)} // also arrayHour(...), listHour(...), etc.
${#calendars.minute(date)} // also arrayMinute(...), listMinute(...), etc.
${#calendars.second(date)} // also arraySecond(...), listSecond(...), etc.
${#calendars.millisecond(date)} // also arrayMillisecond(...), listMillisecond(...), etc.
/*
* Create calendar (java.util.Calendar) objects from its components
*/
${#calendars.create(year,month,day)}
${#calendars.create(year,month,day,hour,minute)}
${#calendars.create(year,month,day,hour,minute,second)}
${#calendars.create(year,month,day,hour,minute,second,millisecond)}
${#calendars.createForTimeZone(year,month,day,timeZone)}
${#calendars.createForTimeZone(year,month,day,hour,minute,timeZone)}
${#calendars.createForTimeZone(year,month,day,hour,minute,second,timeZone)}
${#calendars.createForTimeZone(year,month,day,hour,minute,second,millisecond,timeZone)}
/*
* Create a calendar (java.util.Calendar) object for the current date and time
*/
${#calendars.createNow()}
${#calendars.createNowForTimeZone()}
/*
* Create a calendar (java.util.Calendar) object for the current date (time set to 00:00)
*/
${#calendars.createToday()}
${#calendars.createTodayForTimeZone()}
/*
* ==========================
* Formatting integer numbers
* ==========================
*/
/*
* Set minimum integer digits.
* Also works with arrays, lists or sets
*/
${#numbers.formatInteger(num,3)}
${#numbers.arrayFormatInteger(numArray,3)}
${#numbers.listFormatInteger(numList,3)}
${#numbers.setFormatInteger(numSet,3)}
/*
* Set minimum integer digits and thousands separator:
* 'POINT', 'COMMA', 'WHITESPACE', 'NONE' or 'DEFAULT' (by locale).
* Also works with arrays, lists or sets
*/
${#numbers.formatInteger(num,3,'POINT')}
${#numbers.arrayFormatInteger(numArray,3,'POINT')}
${#numbers.listFormatInteger(numList,3,'POINT')}
${#numbers.setFormatInteger(numSet,3,'POINT')}
/*
* ==========================
* Formatting decimal numbers
* ==========================
*/
/*
* Set minimum integer digits and (exact) decimal digits.
* Also works with arrays, lists or sets
*/
${#numbers.formatDecimal(num,3,2)}
${#numbers.arrayFormatDecimal(numArray,3,2)}
${#numbers.listFormatDecimal(numList,3,2)}
${#numbers.setFormatDecimal(numSet,3,2)}
/*
* Set minimum integer digits and (exact) decimal digits, and also decimal separator.
* Also works with arrays, lists or sets
*/
${#numbers.formatDecimal(num,3,2,'COMMA')}
${#numbers.arrayFormatDecimal(numArray,3,2,'COMMA')}
${#numbers.listFormatDecimal(numList,3,2,'COMMA')}
${#numbers.setFormatDecimal(numSet,3,2,'COMMA')}
/*
* Set minimum integer digits and (exact) decimal digits, and also thousands and
* decimal separator.
* Also works with arrays, lists or sets
*/
${#numbers.formatDecimal(num,3,'POINT',2,'COMMA')}
${#numbers.arrayFormatDecimal(numArray,3,'POINT',2,'COMMA')}
${#numbers.listFormatDecimal(numList,3,'POINT',2,'COMMA')}
${#numbers.setFormatDecimal(numSet,3,'POINT',2,'COMMA')}
/*
* ==========================
* Utility methods
* ==========================
*/
/*
* Create a sequence (array) of integer numbers going
* from x to y
*/
${#numbers.sequence(from,to)}
${#numbers.sequence(from,to,step)}
/*
* Converts to array, trying to infer array component class.
* Note that if resulting array is empty, or if the elements
* of the target object are not all of the same class,
* this method will return Object[].
*/
${#arrays.toArray(object)}
/*
* Convert to arrays of the specified component class.
*/
${#arrays.toStringArray(object)}
${#arrays.toIntegerArray(object)}
${#arrays.toLongArray(object)}
${#arrays.toDoubleArray(object)}
${#arrays.toFloatArray(object)}
${#arrays.toBooleanArray(object)}
/*
* Compute length
*/
${#arrays.length(array)}
/*
* Check whether array is empty
*/
${#arrays.isEmpty(array)}
/*
* Check if element or elements are contained in array
*/
${#arrays.contains(array, element)}
${#arrays.containsAll(array, elements)}
/*
* Converts to list
*/
${#lists.toList(object)}
/*
* Compute size
*/
${#lists.size(list)}
/*
* Check whether list is empty
*/
${#lists.isEmpty(list)}
/*
* Check if element or elements are contained in list
*/
${#lists.contains(list, element)}
${#lists.containsAll(list, elements)}
/*
* Sort a copy of the given list. The members of the list must implement
* comparable or you must define a comparator.
*/
${#lists.sort(list)}
${#lists.sort(list, comparator)}
/*
* Converts to set
*/
${#sets.toSet(object)}
/*
* Compute size
*/
${#sets.size(set)}
/*
* Check whether set is empty
*/
${#sets.isEmpty(set)}
/*
* Check if element or elements are contained in set
*/
${#sets.contains(set, element)}
${#sets.containsAll(set, elements)}
/*
* Compute size
*/
${#maps.size(map)}
/*
* Check whether map is empty
*/
${#maps.isEmpty(map)}
/*
* Check if key/s or value/s are contained in maps
*/
${#maps.containsKey(map, key)}
${#maps.containsAllKeys(map, keys)}
${#maps.containsValue(map, value)}
${#maps.containsAllValues(map, value)}
/*
* Check whether a String is empty (or null). Performs a trim()
* operation before check
* Also works with arrays, lists or sets
*/
${#strings.isEmpty(name)}
${#strings.arrayIsEmpty(nameArr)}
${#strings.listIsEmpty(nameList)}
${#strings.setIsEmpty(nameSet)}
/*
* Check whether a String starts or ends with a fragment
* Also works with arrays, lists or sets
*/
${#strings.startsWith(name,'Don')} // also array*, list* and set*
${#strings.endsWith(name,endingFragment)} // also array*, list* and set*
/*
* Compute length
* Also works with arrays, lists or sets
*/
${#strings.length(str)}
/*
* Null-safe comparison and concatenation
*/
${#strings.equals(str)}
${#strings.equalsIgnoreCase(str)}
${#strings.concat(str)}
${#strings.concatReplaceNulls(str)}
/*
* Random
*/
${#strings.randomAlphanumeric(count)}
7.文本替换
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
<span th:text="|Welcome to our application, ${user.name}!|">
8.运算符
9.条件
th:if、th:unless(th:unless于th:if恰好相反,只有表达式中的条件不成立,才会显示其内容)、th:switch
Logina>
Logina>
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administratorp>
<p th:case="#{roles.manager}">User is a managerp>
<p th:case="*">User is some other thingp>
div>
10.循环th:each
语法:th:each=“obj,iterStat:${objList}”
迭代对象可以是java.util.List,java.util.Map,数组等;
iterStat称作状态变量,属性有:
index:当前迭代对象的index(从0开始计算)
count: 当前迭代对象的index(从1开始计算)
size:被迭代对象的大小
current:当前迭代变量
even/odd:布尔值,当前循环是否是偶数/奇数(从0开始计算)
first:布尔值,当前循环是否是第一个
last:布尔值,当前循环是否是最后一个
<ol>
<li>List循环:
<table border="1">
<tr>
<th>用户名th>
<th>邮箱th>
<th>管理员th>
<th>状态变量:indexth>
<th>状态变量:countth>
<th>状态变量:sizeth>
<th>状态变量:current.userNameth>
<th>状态变量:eventh>
<th>状态变量:oddth>
<th>状态变量:firstth>
<th>状态变量:lastth>
tr>
<tr th:each="user,userStat : ${list}">
<td th:text="${user.userName}">Onionstd>
<td th:text="${user.email}">[email protected]td>
<td th:text="${user.isAdmin}">yestd>
<th th:text="${userStat.index}">当前迭代对象的index(从0开始计算)th>
<th th:text="${userStat.count}"> 当前迭代对象的index(从1开始计算)th>
<th th:text="${userStat.size}">被迭代对象的大小th>
<th th:text="${userStat.current.userName}">当前迭代变量th>
<th th:text="${userStat.even}">状态变量:even****th>
<th th:text="${userStat.odd}">布尔值,当前循环是否是偶数/奇数(从0开始计算)th>
<th th:text="${userStat.first}">布尔值,当前循环是否是第一个th>
<th th:text="${userStat.last}">布尔值,当前循环是否是最后一个th>
tr>
table>
li>
<li>Map循环:
<div th:each="mapS:${map}">
<div th:text="${mapS}">div>
div>
li>
<li>数组循环:
<div th:each="arrayS:${arrays}">
<div th:text="${arrayS}">div>
div>
li>
ol>
<form id="login-form" th:action="@{/login}">...form>
public class StudentVO{
private List<Student> students;
}
public class Student implements Serializable{
private String firstName;
private String lastName;
}
@RequestMapping(value = "/addStudent", method = RequestMethod.POST)
public String addStudent(@ModelAttribute(value = "studentVO")
StudentVO studentVO,ModelMap model) {...}
<form id="login-form" th:action="@{/addStudent}" th:object="${studentVO}" method="POST">
<div class="student" th:each="stus,rowStat:${studentVO.students}">
<input type="text" class="firstName" value="" th:field="*{students[__${rowStat.index}__].firstName}">input>
<input type="text" class="lastName" value="" th:field="*{students[__${rowStat.index}__].lastName}">input>
div>
form>
public class LoginBean implements Serializable{...
private String username;
private List<User> user;
}
public class User implements Serializable{...
private String username;;
}
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(@ModelAttribute(value = "loginBean") LoginBean loginBean,ModelMap model) {..}
<form id="login-form" th:action="@{/login}" th:object="${loginBean}">...
<input type="text" value="" th:field="*{username}">input>
<input type="text" value="" th:field="*{user[0].username}">input>
form>
<a th:href="@{/logout}" class="signOut">a>
div>
- th:if
条件判断
<div th:if="${rowStat.index} == 0">... 该div显示 ...div>
-
th:include
见th:fragment,如下
-
th:replace
见th:fragment,如下
-
th:fragment
声明定义该属性的div为模板片段,常用与头文件、页尾文件的引入。常与th:include,th:replace一起使用
<div th: fragment=" copy" >
© 2011 The Good Thymes Virtual Grocery
div>
<div th: include=" /templates/footer : : copy" >div>
<div th: replace=" /templates/footer : : copy" >div>
- th:object
用于表单数据对象绑定,将表单绑定到后台controller的一个JavaBean参数。常与th:field一起使用进行表单数据绑定
public class LoginBean implements Serializable{...}
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(@ModelAttribute(value = "loginBean") LoginBean loginBean,ModelMap model) {...}
<form id="login-form" th:action="@{/login}" th:object="${loginBean}">...form>
- th:src
用于外部资源引入,类似于
- th:text
文本显示
<td class="text" th:text="${username}" >td>
- th:value
用于标签复制,类似标签的value属性
<option th:value="Adult">Adultoption>
<input id="msg" type="hidden" th:value="${msg}" />