模板引擎
介绍
传统的页面开发过程中通常采用的HTML + JS技术,而现在大部分网站都采用标签化+ 模块化 的设计 。模板引擎其实就是根据这种方式,使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档在原有的HTML页面中来填充数据。最终达到渲染页面的目的。
说人话就是,把数据和页面整合在一起的技术。
常用的模板引擎
- Thymleaf
- FreeMarker
- Velocity
SpringBoot整合Thymeleaf
Thymeleaf视图介绍
Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎。
Thymeleaf的主要目标是为您的开发工作流程带来优雅的自然模板 -HTML可以在浏览器中正确显示,也可以作为静态原型工作,从而可以在开发团队中加强协作。
Thymeleaf拥有适用于Spring Framework的模块,与您喜欢的工具的大量集成以及插入您自己的功能的能力,对于现代HTML5 JVM Web开发而言,Thymeleaf是理想的选择。
在SpringBoot中,SpringBoot对Thymeleaf提供了良好的支持,同时也提供了自动化配置,因此在SpringBoot中使用Thymeleaf非常快捷方便。
SpringBoot中使用Thymeleaf
- 在SpringBoot项目中引入Thymeleaf的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- 配置Thymeleaf
SpringBoot为Thymeleaf提供了自动化配置类ThymeleafAutoConfiguration,可以看出相关的配置信息是从ThymeleafProperties类中获得的,进一步查看ThymeleafProperties的源码:
//代码中指定的值为默认配置值
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
//省略
}
相关配置如下:
#是否开启缓存,默认为true
spring.thymeleaf.cache=false
#检查模板文件是否存在
spring.thymeleaf.check-template=true
#检查模本目录是否存在
spring.thymeleaf.check-template-location=true
#模板文件编码
spring.thymeleaf.encoding=UTF-8
#模板位置
spring.thymeleaf.prefix=classpath:/templates/
#模板文件后缀名
spring.thymeleaf.suffix=.html
#Content-type
spring.thymeleaf.servlet.content-type=text/html
Thymeleaf语法
需要注意的是:模版页面中的 html 上需要声明 Thymeleaf 的命名空间,具体代码如下:
<html xmlns:th="http://www.thymeleaf.org">
文本标签 th:text/th:utext
作用:用于文本内容的显示操作。
- th:text 进行文本替换 不会解析html
- th:utext 进行文本替换 会解析html
字符串拼接
作用:拼接字符串通过 + 或者 | 进行拼接
代码演示:
@RequestMapping("/th")
public String th(Model model){
model.addAttribute("a",1);
model.addAttribute("b",2);
return "/course/th";
}
模板页面:<p th:text="${a}+${b}"></p>
结果页面:<p>3</p>
模板页面:<p th:text="|${a} ${b}|"></p>
结果页面:<p>1 2</p>
模板页面:<p th:text="${a} > ${b}"></p>
结果页面:<p>false</p>
*{…}和 ${…}表达式
作用:正常情况下 *{…} 和 ${…}是一样的,但是 *{…} 一般和 th:object 进行一起使用来完成对象属性的简写。
具体使用:
name和age都是user对象的属性,具体使用如下代码
<div th:object="${user}" >
<p th:text="*{name}"></p>
<p th:text="*{age}"></p>
</div>
#{…}表达式
作用:用于国际化message.properties 属性读取
~{…}片段表达式
作用:这个一般和模版布局的语法一起使用,具体使用方式请看下面模版布局的教程。
@{…}链接网址表达式
作用:一般和 th:href、th:src进行结合使用,用于显示Web 应用中的URL链接。通过@{…}表达式Thymeleaf 可以帮助我们拼接上web应用访问的全路径,同时我们可以通过()进行参数的拼接。
代码演示:
<img th:src="@{/images/gtvglogo.png}" />
模板页面:<a th:href="@{/product/comments(prodId=${prod.id})}" >查看</a>
结果页面:<a href="/sbe/product/comments?prodId=2">查看</a>
条件判断 th:if/th:unless
- th:if 当条件为true则显示。
- th:unless 当条件为false 则显示。
代码演示:
模板页面:<p th:if="${flag}">if判断</p>
结果页面:<p>if判断</p>
switch
作用:th:switch 我们可以通过switch来完成类似的条件表达式的操作。
代码演示:
<div th:switch="${user.name}">
<p th:case="'ljk'">User is ljk</p>
<p th:case="ljk1">User is ljk1</p>
</div>
for循环
作用:th:each 遍历集合。 迭代对象可以是java.util.List,java.util.Map,数组等数据类型;
代码演示:
<table>
<thead>
<tr>
<th>用户名称</th>
<th>用户年龄</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${userList}" th:class="${userStat.odd}? 'odd'">
<td th:text="${user.name}">Onions</td>
<td th:text="${user.age}">2.41</td>
</tr>
</tbody>
</table>
结果页面:
可以通过便利的变量名+Stat 来获取索引是否是第一个或最后一个等。便利的变量名+Stat称作状态变量,其属性有:
- index:当前迭代对象的迭代索引,从0开始,这是索引属性;
- count:当前迭代对象的迭代索引,从1开始,这个是统计属性;
- size:迭代变量元素的总量,这是被迭代对象的大小属性;
- current:当前迭代变量;
- even/odd:布尔值,当前循环是否是偶数/奇数(从0开始计算);
- first:布尔值,当前循环是否是第一个;
- last:布尔值,当前循环是否是最后一个
th:href
作用:用于声明在a 标签上的href属性的链接 该语法会和@{…} 表达式一起使用。
代码演示:
模板页面:<a href="../home.html" th:href="@{/}">返回首页</a>
结果页面:<a href="/sbe/">返回首页</a>
th:class
作用:用于声明在标签上class 属性信息。
代码演示:
模板页面:<p th:class=" 'even'? 'even' : 'odd'" th:text=" 'even'? 'even' : 'odd'"></p>
结果页面:<p class="even">even</p>
th:attr
作用:用于声明html中或自定义属性信息。
代码演示:
模板页面:<img th:attr="src=@{/images/gtvglogo.png}" />
结果页面:<img src="/sbe/images/gtvglogo.png">
th:value
作用:用于声明html中value属性信息。
代码演示:
模板页面:<input type="text" th:value="${name}" />
结果页面:<input type="text" value="ljk">
th:action
作用:用于声明html from标签中action属性信息。
代码演示:
模板页面
<form action="subscribe.html" th:action="@{/subscribe}">
<input type="text" name="name" value="abc"/>
</form>
结果页面:
<form action="/sbe/subscribe">
<input type="text" name="name" value="abc">
</form>
th:id
作用:用于声明htm id属性信息。
代码演示:
模板页面:<p th:id="${id}"></p>
结果页面:<p id="123"></p>
th:inline
作用:用于内联操作,详情见下面内敛介绍。
th:onclick
作用:用于声明htm 中的onclick事件。
th:selected
作用:用于声明htm 中的selected属性信息。
代码演示:
模板页面:
<select>
<option name="sex"></option>
<option th:selected="1 == ${sex}">男</option>
<option th:selected="0 == ${sex}">女</option>
</select>
结果页面:
<select>
<option name="sex"></option>
<option selected="selected">男</option>
<option>女</option>
</select>
th:src
作用:用于声明htm 中的img中src属性信息。
th:style
作用:用于声明htm中的标签 css的样式信息。
代码演示:
模板页面:<p th:style="'display:' + @{(${isShow} ? 'none' : 'block')} + ''"></p>
结果页面:<p style="display:none"></p>
th:with
作用:用于thymeleaf 模版页面中局部变量定义的使用。
代码演示:
模板页面:
<p th:with="df='dd/MMM/yyyy HH:mm'">
Today is: <span th:text="${#dates.format(today,df)}">13 February 2011</span>
</p>
结果页面:
<span>02/六月/2019 06:52</span>
Elvis运算符
作用:Elvis运算可以理解成简单的判断是否为null的三元运算的简写,如果值为nullzhe显示默认值,如果不为null 则显示原有的值。
代码演示:
<!-- 如果后台传过来的age的值为 null-->
模板页面:<p>Age: <span th:text="${age}?: '年龄为nll'"></span></p>
结果页面:<p>Age: <span>年龄为nll</span></p>
三元表达式
作用:我们可以在thymeleaf 的语法中使用三元表达式 具体使用方法是在th:x 中通过 表达式?1选项:2选项。
代码演示:
模板页面:1、<p th:class=" 'even'? 'even' : 'odd'" th:text=" 'even'? 'even' : 'odd'"></p>
2、<p th:value="${name eq 'ljk' ? '帅哥':'丑男'}" th:text="${name eq 'ljk' ? '帅哥':'丑男'}"></p>
结果页面:1、<p class="even">even</p>
2、<p value="帅哥">帅哥</p>
条件表达式操作字符:
- gt:great than(大于)
- ge:great equal(大于等于)
- eq:equal(等于)
- lt:less than(小于)
- le:less equal(小于等于)
- ne:not equal(不等于)
No-Operation(_)什么都不做
作用:是Elvis运算符的一种特殊简写操作,当显示的值为null 是就什么都不做。
代码演示:
模板页面:<span th:text="${name} ?: _">no user authenticated</span>
结果页面:<span>no user authenticated</span>
内联
-
如何使用内联:可以通过 在父标签声明 th:inline=“text” 来开启内联操作。当然如果想整个页面使用可以直接声明在body上即可。具体使用方式如下面代码所示。
代码演示:
模板页面:
<div th:inline="text">
<p>Hello, [[${user.name}]]!</p>
</div>
结果页面:
<div>
<p>Hello,zhuoqianmingyue!</p>
</div>
等同于th:text
<div>
<p th:text="Hello,+${user.name}"></p>
</div>
[[...]]对应于th:text,[(...)]对应于th:utext
- 禁用内联操作:可以通过在父标签或者本标签上声明th:inline="none"来禁用内联的操作,如下面代码所示:
模板页面:<p th:inline="none">A double array looks like this: [[1, 2, 3], [4, 5]]!</p>
结果页面:<p>A double array looks like this: [[1, 2, 3], [4, 5]]!</p>
- JavaScript内联
如果我们想在JavaScript 中使用内联操作,需要在 script 标签上声明 th:inline=“javascript” 然后我们就可以 script 标签中使用内联操作了。具体使用方式如下面代码所示:
<script th:inline="javascript">
var username = [[${user.name}]];
</script>
- CSS内联
可以通过在 style 标签上声明 th:inline=“css” 来开启在css中使用内联的操作,具体操作方式如下:
<style th:inline="css">
.[[${classname}]] {
text-align: [[${align}]];
}
</style>
模板布局
SpringBoot2.0 使用模版模版布局需要先引入 thymeleaf的 thymeleaf-layout-dialect依赖
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>
通过 th:fragment 定义引用片段
1、首先:我们可以通过 th:fragment 来定义引用片段,然后可以在其他页面进行引用。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
</body>
</html>
2、通过 th:insert 和 ~{…}片段引用表达式 进行引入footer.html中定义的片段
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:insert="~{footer :: copy}"></div>
</body>
</html>
3、结果页面
<div>
<div>
© 2011 The Good Thymes Virtual Grocery
</div>
</div>
如果你觉得~{footer :: copy}写法比较麻烦可以采用简写的方式footer :: copy。
<div th:insert="footer :: copy"></div>
<div th:insert="~{footer :: copy}"></div>
通过id属性来声明片段
1、也可以通过在引用片段代码上声明id属性的方式进行片段的引用
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="copy-section" >
© 2011 The Good Thymes Virtual Grocery
</div>
</body>
</html>
2、引用页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:insert="~{footer :: #copy-section}"></div>
</body>
</html>
3、结果页面
<div>
<div id="copy-section">
© 2011 The Good Thymes Virtual Grocery
</div>
</div>
footer :: #copy-section和~{footer :: #copy-section} 结果是一致的。
th:insert和th:replace(和th:include)之间的区别
三者的使用方法相同:
- th:insert 是最简单的:他会将使用th:insert的标签 和引用片段的内容都显示出来
- th:replace 插入引用片段的标签和内容
- th:include类似于th:insert,只插入此片段的内容。
带参数的引用片段
看下面代码就懂了
1、定义引用片段代码模版页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div th:fragment="frag (onevar,twovar)">
<p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>
</body>
</html>
2、引用引用片段的模版页面:index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div th:insert="footer :: frag('a','b')"></div>
</body>
</html>
3、结果页面
<div>
<div>
<p>a - b</p>
</div>
</div>
总结:th:insert=“footer ::frag (onevar=‘a’,twovar=‘b’)” 和th:insert=“footer :: frag(‘a’,‘b’)效果是相等的。还有另一种写法就是使用th:with th:insert=”::frag" th:with=“onevar=‘a’,twovar=‘b’”
删除模版片段
thymeleaf 为我们提供了 th:remove
<tr class="odd" th:remove="all">
<td>Blue Lettuce</td>
<td>9.55</td>
<td>no</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr th:remove="all">
<td>Mild Cinnamon</td>
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="comments.html">view</a>
</td>
</tr>
在模拟数据上声明th:remove=“all” 后在此通过url访问 没有了我们之前的模拟数据,但是直接查看该页面还是可以查看到我们的模拟数据的。
all属性中的这个值是什么意思?th:remove可以根据其价值以五种不同的方式表现:
- all:删除包含标记及其所有子标记。
- body:不要删除包含标记,但删除其所有子标记。
- tag:删除包含标记,但不删除其子项。
- all-but-first:删除除第一个之外的所有包含标记的子项。
- none: 没做什么。此值对于动态评估很有用。
Thymeleaf的预定义的工具对象
dates
介绍:处理日期数据 生成,转换,获取日期的具体天数 年数。
format操作
模板页面:<span th:text="${#dates.format(date)}">4564546</span>
结果页面:<span>2019年5月30日 上午10时03分24秒 </span>
获取日期属性操作
date为后台传入的时间
模板页面:<p th:text="${#dates.day(date)} "></p>
结果页面:<p>30</p>
取出时间的方法介绍如下:
- day 当月的某一天
- month 哪一月(数字)
- monthName 哪一月(汉字)
- monthNameShort 哪一月(汉字)
- year 哪一年(数字)
- dayOfWeek 星期几(数字)
- dayOfWeekName 星期几(汉字)
- dayOfWeekNameShort 星期几(汉字)
- hour 小时
- minute 分钟
- millisecond 毫秒
numbers
处理数字数据的转换。包括:
- 对不够位数的数字进行补0(formatInteger )
模板代码:<p th:text="${#numbers.formatInteger('123',4)}"></p>
结果页面:<p>0123</p>
- 设置千位分隔符(formatInteger)
模板代码:<p th:text="${#numbers.formatInteger('1000',6,'POINT')}"></p>
结果页面:<p>001.000</p>
- 精确小数点(formatDecimal )
模板代码:<p th:text="${#numbers.formatDecimal('1000.123',5,'POINT',2,'COMMA')}"></p>
结果页面:<p>01.000,12</p>
- 钱显示符号操作
模板代码:<p th:text="${#numbers.formatCurrency('1000')}"></p>
结果页面:<p>¥1,000.00</p>
- 设置百分号(formatPercent )
模板代码:<p th:text="${#numbers.formatPercent('0.2',2, 4)}"></p>
结果页面:<p>20.0000%</p>
- 生成数组(sequence )
模板页面:
<div th:each="num : ${#numbers.sequence(0,4)}" >
<p th:text="${num}"></p>
</div>
结果页面:
<div><p>0</p></div>
<div><p>1</p></div>
<div><p>2</p></div>
<div><p>3</p></div>
<div><p>4</p></div>
strings
处理String的相关操作,包括:
- 字符串转换(toString)
<p th:text="${#strings.toString(object)}"></p>
- 检查字符串是否为空(isEmpty)
判断字符串是否为空
<p th:text="${#strings.isEmpty(name)}"></p>
判断集合是否为空
<p th:text="${#strings.listIsEmpty(nameList)}"></p>
- 字符串是为空替换操作(defaultString)
<p th:text="${#strings.defaultString(text,'该值为null')}"></p>
- 检查字符串中是否包含某个字符串(contains containsIgnoreCase)
判断字符串中是否含有字符
<p th:text="${#strings.contains('abcez','ez')}"></p>
判断字符串中是否含有字符(忽略大小写)
<p th:text="${#strings.containsIgnoreCase('abcEZ','ez')}"></p>
检查字符串是以片段开头还是结尾(startsWith endsWith)
<p th:text="${#strings.startsWith('Donabcez','Don')}"></p>
<p th:text="${#strings.endsWith('Donabcezn','n')}"></p>
- indexOf操作
<p th:text="${#strings.indexOf('abcefg','e')}"></p>
- 截取(substring substringAfter)
<p th:text="${#strings.substring('abcefg',3,5)}"></p>
- 替换(replace)
<p th:text="${#strings.replace('lasabce','las','ler')}"></p>
- 追加(prepend append)
模板代码:<p th:text="${#strings.prepend('abc','012')}"></p>
结果页面:<p>012abc</p>
模板代码:<p th:text="${#strings.append('abc','456')}"></p>
结果页面:<p>abc456</p>
- 变更大小写(toUpperCase toLowerCase)
<p th:text="${#strings.toUpperCase('abc')}"></p>
<p th:text="${#strings.toLowerCase('ABC')}"></p>
- length操作,获取字符串长度
<p th:text="${#strings.length('abc')}"></p>
- 拆分和组合字符串(arrayJoin arraySplit)
${#strings.arrayJoin(namesArray,',')}
${#strings.arraySplit(namesStr,',')}
- 去空格(trim)
<p th:text="${#strings.trim(' abc ')}"></p>
- 缩写文本(abbreviate)
模板页面:<p th:text="${#strings.abbreviate('12345678910',10)}"></p>
结果页面:<p>1234567...</p>
- 字符串连接(concat)
${#strings.concat(str)}
objects
处理Object对象的操作 包含obj不为空返回改值如果为空返回默认值(nullSafe)
<p th:text="${#objects.nullSafe(obj,'该对象为null')}"></p>
<p>该对象为null</p>
bools
判断对象是否为ture或者是否为false的操作。
- 数字 1 为 ture , 0 为 false;
- “on” 为 true, “off” 为false;
- “true” 为true, "false"为 false;
isTrue操作
<p th:text="${#bools.isTrue(true)} "></p>
arrays
处理数组的相关操作的内置对象,包含:
- 转换数组 toStringArray toIntegerArray,
<p th:text="${#arrays.toStringArray(object)} "></p>
<p>[Ljava.lang.String;@3cca655d</p>
- 获取数组的长度(length )
- 判断数组是否为空(isEmpty )
- 是否包含某个元素(contains)
<p th:text="${#arrays.contains(array,1)} "></p>
<p>true</p>
- 是否包含一批元素(containsAll)
<p th:text="${#arrays.containsAll(array,array2)} "></p>
<p>true</p>
lists
使用方式大致相同
处理 list 相关操作的内置对象,包括:
- 计算长度(size)
- 检查list是否为空(isEmpty)
- 检查元素是否包含在list中(contains,containsAll)
- 对给定list的副本排序(sort)
sets
使用方式大致相同
处理 set 相关操作的内置对象,包括:
- 转换为Set(toSet)
- 计算长度(size)
- 检查set是否为空(isEmpty)
- 检查元素是否包含在set中 (contains,containsAll)
maps
使用方式大致相同
处理 map相关操作的内置对象,包括:
- 计算长度(size)
- 检查map是否为空(isEmpty)
- 检查映射中是否包含键或值(containsKey,containsAllKeys,containsValue)
aggregates
用户处理集合或者数组的一些统计操作,包括:
- 求和(sum)
- 求平均值(avg)
- 处理包装类型或基本类型的数组或集合