简单说,Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点:
Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
官方教程:
https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#what-is-thymeleaf
简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点:
接下来,我们就通过入门案例来体会Thymeleaf的魅力:
引入依赖:
只需加入thymeleaf-2.1.4.RELEASE.jar(http://www.thymeleaf.org/download.html)包,若用maven,则加入如下配置
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
SpringBoot会自动为Thymeleaf注册一个视图解析器:
编写控制器:
@Controller
public class HelloController {
@RequestMapping(value = "/say")
public String hello(Model model) {
String name = "bruce";
model.addAttribute("name", name);
System.out.println(name);
return "hello";
}
}
视图页面:
<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="'Hello!, ' + ${name} + '!'">测试p>
body>
html>
测试:
访问地址:http://127.0.0.1:8082/brand/list
注意事项
可能会发现在默认配置,thymeleaf对.html的内容要求很严格,如,
如果少最后的标签封闭符号/,就会报错而转到错误页。也比如你在使用Vue.js这样的库,然后有这样的html代码,也会被thymeleaf认为不符合要求而抛出错误。
因此,建议增加下面这段:
spring.thymeleaf.mode = LEGACYHTML5
spring.thymeleaf.mode的默认值是HTML5,其实是一个很严格的检查,改为LEGACYHTML5可以得到一个可能更友好亲切的格式要求。需要注意的是,LEGACYHTML5需要搭配一个额外的库NekoHTML才可用。
<dependency>
<groupId>net.sourceforge.nekohtmlgroupId>
<artifactId>nekohtmlartifactId>
<version>1.9.22version>
dependency>
由上文也可以知道需要在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="'Hello!, ' + ${name} + '!'">测试p>
body>
html>
这样,下文才能正确使用th:*形式的标签!
通过${…}进行取值,这点和ONGL表达式语法一致!
<p th:text="'Hello!, ' + ${name} + '!'">3333p>
选择变量表达式*{…}
<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>
至于p标签里面的原有的值只是为了给前端开发时做展示用的.这样的话很好的做到了前后端分离。
这也是Thymeleaf非常好的一个特性:在无网络的情况下也能运行,也就是完全可以前端先写出页面,模拟数据展现效果,后端人员再拿此模板修改即可!
用来配合link src href使用的语法,类似的标签有:th:href和th:src
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">viewa>
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">viewa>
<a href="details.html"
th:href="@{order/{orderId}/details(orderId=${o.id})}">Content路径,默认访问static下的order文件夹a>
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
或者下面的表达方式:(只能包含表达式变量,而不能有条件判断等!)
<span th:text="|Welcome to our application, ${user.name}!|">
数学运算
二元操作:+, - , * , / , %
一元操作: - (负)
逻辑运算
一元 : and or
二元 : !,not
比较运算(为避免转义尴尬,可以使用括号中的英文进行比较运算!)
比较:> , < , >= , <= ( gt , lt , ge , le )
等于:== , != ( eq , ne )
条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
if/unless
使用th:if和th:unless属性进行条件判断,th:unless于th:if恰好相反,只有表达式中的条件不成立,才会显示其内容。
Logina>
switch
<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>
th:each
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>hellotitle>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
head>
<body>
<table>
<tr>
<th>IDth>
<th>NAMEth>
<th>AGEth>
tr>
<tr th:each="emp : ${empList}">
<td th:text="${emp.id}">1td>
<td th:text="${emp.name}">海td>
<td th:text="${emp.age}">18td>
tr>
table>
body>
html>
后台:
@GetMapping(value = "/hello")
public String hello(Model model) {
List<Emp> empList = new ArrayList<>();
empList.add(new Emp(1, "校长", 24));
empList.add(new Emp(2, "书记", 28));
empList.add(new Emp(3, "小海", 25));
model.addAttribute("empList", empList);
return "hello";
}
迭代对象可以是java.util.List,java.util.Map,数组等;
iterStat称作状态变量,属性有:
index:当前迭代对象的index(从0开始计算)
count: 当前迭代对象的index(从1开始计算)
size:被迭代对象的大小
current:当前迭代变量
even/odd:布尔值,当前循环是否是偶数/奇数(从0开始计算)
first:布尔值,当前循环是否是第一个
last:布尔值,当前循环是否是最后一个
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>hellotitle>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
head>
<body>
<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}">状态变量:indexth>
<th th:text="${userStat.count}">状态变量:countth>
<th th:text="${userStat.size}">状态变量:sizeth>
<th th:text="${userStat.current.userName}">状态变量:currentth>
<th th:text="${userStat.even}">状态变量:even****th>
<th th:text="${userStat.odd}">状态变量:oddth>
<th th:text="${userStat.first}">状态变量:firstth>
<th th:text="${userStat.last}">状态变量:lastth>
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>
body>
html>
一般不推荐在前端进行这些处理,前端页面以减少逻辑为宜
日期处理
/*
* 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()}
#dates
字符串处理
/*
* 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)}
参考手册:https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#appendix-b-expression-utility-objects
package com.yigou.manager.controller;
import com.yigou.item.pojo.Brand;
import com.yigou.manager.service.BrandService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author bruceliu
* @create 2019-09-13 16:48
* @description
*/
@Controller
@RequestMapping("brand")
public class BrandController {
@Autowired
BrandService brandService;
@RequestMapping("list")
public String getBrands(Model model){
List<Brand> brands = brandService.findBrands();
// 放入模型
model.addAttribute("brands",brands);
// 返回模板名称(就是classpath:/templates/目录下的html文件名)
return "brand";
}
}
<tbody>
<tr th:each="brand:${brands}">
<td><input type="checkbox" >td>
<td th:text="${brand.id}">1td>
<td th:text="${brand.name}">联想td>
<td th:text="${brand.firstChar}">Ltd>
<td class="text-center">
<button type="button" class="btn bg-olive btn-xs" data-toggle="modal" data-target="#editModal" th:onclick="'getById('+${brand.id}+');'">修改button>
<button type="button" class="btn bg-olive btn-xs" data-toggle="modal" th:onclick="'deleteById('+${brand.id}+');'">删除button>
td>
tr>
tbody>