Thymeleaf模板复习
三大模板
-
jsp
-
html thymeleaf
-
tld freemarker
面试题: 什么是模板,为什么使用thymeleaf模板?
第一,Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。
这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
第二,Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
第三,Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
跟以前的技术做对比
Th:xxx 覆盖原有属性
Th:text/th:utext 显示文本 行内 [[{})]
Th:each
${} 取值
~{} 代码段
@{} 相对于项目的绝对路径
第一章 模板引擎-简介和比较
1.1 Spring Boot提供了大量模板引擎技术
包含FreeMarker,Groovy,Thymeleaf,Velocity和Mustache,Spring Boot中推荐使用Thymeleaf作为模板引擎.因为Thymeleaf提供了完美的SpringMVC支持.
Thymeleaf是一个java类库,他是一个xml/xhtml/html5的模板引擎,可以作为mvc的web应用的view层。
1.2 Thymeleaf还提供了额外的模块与Spring MVC集成
所以我们可以使用Thymeleaf完全替代jsp。
spring Boot通过org.springframework.boot.autoconfigure.thymeleaf包对Thymeleaf进行了自动配置。
通过ThymeleafAutoConfiguration类对集成所需要的bean进行自动配置。
包括templateResolver,templateEngine,thymeleafViewResolver的配置。
1.3 模板引擎-简介和比较
1.4 模板引擎
JSP、Velocity、Freemarker、Thymeleaf
SpringBoot推荐的Thymeleaf;语法更简单,功能更强大;
第二章 Thymeleaf使用
2.1 Thymeleaf使用
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration
ThymeleafViewResolver
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = Charset.forName("UTF‐8");
private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
…
}
只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染;
导入thymeleaf的名称空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
使用thymeleaf语法
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF‐8">
<title>Title</title>
</head>
<body>
<h1>成功!</h1>
<!‐‐th:text 将div里面的文本内容设置为 ‐‐>
<div th:text="${hello}">这是显示欢迎信息</div>
</body>
</html>
语法规则
th:text;改变当前元素里面的文本内容
th:任意html属性;来替换原生属性的值
map.put("hello","<h1>你好</h1>");
map.put("users",Arrays.asList("zhangsan","lisi","wangwu"));
<!--th:text 将div里面的文本内容设置为 -->
<div id="div01" class="myDiv" th:id="${hello}" th:class="${hello}" th:text="${hello}">这是显示欢迎信息</div>
<hr/>
<div th:text="${hello}"></div> <!--转译特殊字符,特殊符号原方不动输出-->
<div th:utext="${hello}"></div> <!--会转译字符,特殊符号被转译后输出结果-->
<hr/>
<!-- th:each每次遍历都会生成当前这个标签: 3个h4 -->
<h4 th:text="${user}" th:each="user:${users}"></h4>
<hr/>
<h4>
<span th:each="user:${users}"> [[${user}]] </span> <!--inlning 行内写法 [[]]等价于th:text [()]等价于th:utext-->
</h4>
th:text ------> [[${...}]]
th:utext -------> [(${...})]
<html xmlns:th="http://www.thymeleaf.org">
<link th:href="@{/bootstrap/css/bootstrap.min.css}" rel="stylesheet" />
<span th:text="${singlePerson.name}">姓名</span>
<li class="list-group-item" th:each="person:${people}"></li>
<script th:src="@{bootstrap/js/bootstrap.min.js}"></script>
<script th:inline="javascript">
var single=[[${singlePerson}]];
console.log(single.name+"/"+single.age);
</script>
第三章 Thymeleaf-语法
3.1 使用 th:text
3.1.1 外部文本(消息)(了解就好,ali开发几十年才走向国际)
Welcome Message
外部文本的概念: 外部文本抽取模板代码片段到模板文件外面, 使外部文本可以存在另一个文件中(比如 properties 文件). 通常把外部文本叫做消息(messages).
Thymeleaf 通过 #{...} 语法来使用消息.
在 springmvc 中使用消息要额外配置 ResourceBundleMessageSource 这个 bean:
// 用于外部文本及国际化消息
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
这个 bean 会去 classpath 根目录下去寻找 messages 为基名的 properties 文件.
比如,messages.properties, messages_en_US.properties, messages_zh_CN.properties.
th:text 外部文本会替换 p 标签内的内容.
3.1.2 使消息不转换 html 标签
如果我们在 messages.properites 中这么写: index.welcome=Welcome to SpringMVC
那么 thymeleaf 会转成 <p>Welcome to SpringMVC</p> 这显然不是我们想要的. 这时候就可以用 th:utext(for “unescaped text)
3.1.3 使用并显示变量
变量概念: 存在 java web 上下文中的变量. 比如 request, session, application, page ...
用 ${...} 语法可以用来显示变量. 花括号内使用的是 ognl 表达式语言.
springmvc 中用 spel 语言代替 ognl 语言.
3.2 标准表达式语法
-
变量表达式: ${...}
-
选择变量表达式: *{...}
-
消息表达式: #{...}
-
URL 表达式: @{...}
-
代码段表达式: ~{...}
-
-
字面量
-
文本字面量: 'some text'
-
数值字面量: 0, 34, 3.0, 12.3
-
布尔值字面量: true, false
-
Null 值字面量: null
-
Tokens 字面量: one, content, sometext, ...
-
3.3 消息
消息中也可以包含变量, 比如在 index.welcome 中, 想打印出时间:# 在 messages.properties 文件中用 {0}, {1}, {2}, ... 占位符
index.welcome=Welcome to SpringMVC, time is: {0}
那么在 index.html 模板文件中就可以这样写:<p th: utext="#{index.welcome(${date})}">Welcome message</p>
其中 ${date} 就像一个参数那样被传进去了.
3.4 变量
3.4.1变量表达式 ${...}
用的是 ognl(对象图导航语言). 在 springmvc 中用 spel(spring 表达式语言)代替. 其实两者在大部分情况下用法是相同的.
下面看几个例子就知道变量表达式的大部分用法了.
<!-- springmvc 保存了一个 model 对象: departments -->
<!-- 获取所有 departments -->
<p th:text="${departments}"></p>
<!-- 获取 departments 的第一个元素 -->
<p th:text="${departments[0]}"></p>
<!-- 获取第一个 department 对象的 name 属性 -->
<p th:text="${departments[0].name}"></p>
<!-- 也可以用 ['name'] 来获取第一个 department 对象的 name 属性 -->
<p th:text="${departments[0]['name']}"></p>
<!-- 甚至可以调用方法! -->
<p th:text="${departments[0].getId()}"></p>
<p th:text="${departments[0]['name'].substring(0, 1)}"></p>
3.4.2内置基本对象
加#号 取的是原生的servletAPI
不加# 取的是thymeleaf包装的API
下面是一些内置的基本对象, 可以用 # 符号直接使用
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.
使用例子:
country: <span th:text="${#***\*locale\****.country}"></span>
3. 4.3 内置工具对象
统统加#才能取到
除了基本对象, thymeleaf 还提供了一组工具对象.
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).
例子, 格式化时间:
<!-- 时间格式化 -->
time:<span th:text="${#dates.format(date, 'yyyy-MM-dd HH:mm:ss')}"></span><br>
4. 选择变量表达式
获取变量可以使用 ${...} 语法外, 还可以使用 {...}, 称为选择变量表达式***.
选择变量表达式与变量表达式的不同之处在于, 如果前面有一个选择对象了, 那么用它获取这个选择对象的属性或方法时, 可以不写对象名.
那么选择对象的概念是什么呢? 选择对象是用 th:object 表示的对象.
看例子:
<p>选择变量表达式</p> <div th:object="${departments[1]}"> <p th:text="*{id}"></p> <p th:text="*{name}"></p> </div> <p>等价的变量表达式</p> <div> <p th:text="${departments[1].id}"></p> <!-- 如果没有 "选择对象", 那么 * 和 $ 是等价的 --> <p th:text="*{departments[1].name}"></p> </div>
如果存在选择对象了, 那么在 ${...} 中也可以用 #object 来使用选择对象.
<p>${...} 中使用 #object 引用 "选择对象"</p> <div th:object="document[2]"> <!-- 以下三种方式在这种情况下是等价的 --> <p th:text="${#object.id}"></p> <p th:text="*{id}"></p> <p th:text="${document[2].id}"></p> </div>
5. Link URL 表达式
链接 URL 表达式语法是 @{...}
有不同类型的 URLs:
绝对路径 URLs: http://localhost:8888/demo/index
相对路径 URLs:
页面相对: user/login.html
上下文相对: /employee/emps 注意用 / 打头, 会自动把上下文路径(比如 http://localhost:8888/demo) 路径加上去.
服务器相对(不重要)
协议相对(不重要)
例子:
<!-- Common styles and scripts --> <link rel="stylesheet" type="text/css" media="all" th:href="@{/assets/css/base.css}"> <script type="text/javascript" th:src="@{/assets/ext/jquery-3.1.1-min.js}"></script> <script type="text/javascript" th:src="@{/assets/js/codergege.js}"></script> <!-- ... --> <a href="#" th:href="@{/}">返回首页</a> <br> <a href="#" th:href="@{/thymeleaf/demo1}">去 demo1 页面</a> <br> <!-- 会生成 url: http://localhost:8888/demo/thymeleaf/demo1?username=tom --> <a href="#" th:href="@{/thymeleaf/demo1(username=${employees[0].name})}">去 demo1 页面, 带参数</a> <br> <!-- 会生成 url: http://localhost:8888/demo/thymeleaf/demo1/2 RESTful 风格的 url --> <a href="#" th:href="@{/thymeleaf/demo1/{empId}(empId=${employees[1].id})}">去 demo1 页面, 带 RESTful 风格参数</a> <br>
中文会自动转码; 如果有多个参数,用逗号隔开.
restful
6代码段(fragment)
语法: ~{...}
最常见的用法是与 th:insert 或 th:replace 配合使用. 例如:
<!-- ~{...} 可以省略不写 --> <header id="header" th:replace="fragment :: header"></header> <footer id="footer" th:replace="~{fragment :: footer}"></footer>
这就是三者区别
<!--替换掉外层的div元素--> <div th:replace="~{common::foot}"></div> <!--在外层div中直接插入代码段--> <div th:insert="~{common::foot}"></div> <!--只包含代码段的文本--> <div th:include="~{common::foot}"></div>
7.字面量
8. 连接字符串
文本, 不管是文本字面量还是通过 ognl/spel 计算出来的文本, 都能用 + 操作符连接.
<span th:text="'Some literal text and ' + ${departments[0].name}"></span>
9. 条件表达式与默认表达式
条件表达式由 3 个部分组成,
condition, then, else. 每个部分自身又是一个表达式, 即它们分别可以用变量(${...}, *{...}), 消息(#{...}), URLs(@{...}), 字面量等来表示.
<!-- else 部分可以省略, 这种情况下, 如果条件为 false, null 值会被返回 -->
<p th:class="${employees[0].id % 2 == 0}? 'even'" th:text="${employees[0].name}"></p>
<p th:class="${employees[1].id % 2 == 0}? 'even': 'odd'" th:text="${employees[1].name}"></p>
<p th:class="${employees[2].id % 2 == 0}? 'even': 'odd'" th:text="${employees[2].name}"></p>
<!-- ?: 默认表达式 -->
<span th:text="${employees[0].getGender()}?: '没有指定性别'"></span> <br>
<span th:text="${employees[2].getEmail()}?: '没有指定邮箱'"></span> <br>
<!-- 可以嵌套混合, 嵌套的话用 () 包起来 -->
<span th:text="(${employees[0].getGender()} == 1 ? '男': '女')?: '没有指定性别'"></span>
10. No-Op 操作符(_)
No-Op 操作符指明期待的表达式结果不做任何事情. 比如说 th:text 中最后计算结果是 _,那么 th:text 元素根本就不会生成.
<p th:text="_">这里的内容不会被 th:text 替换</p>
<p th:text="${employees[0].email}?: _">没有指定电子邮箱</p>
11.数据转换与格式化
Thymeleaf 的变量表达式(${...}, *{...})使用 {{...}} 来表示需要进行转换. 允许我们使用自定义的数据转换服务来转换表达式返回结果.
使用 thymeleaf-spring3 和 thymeleaf-spring4 的话, 会自动调用 spring 的 ConversionService.
12.预处理表达式
用双下划线 ... 包裹普通的表达式就可以.
3.5设置属性值
本章学习 thymeleaf 如何设置或修改 html 元素属性.
3.5.1 设置任意属性 th:attr
可以用 th:attr 来设置任意属性.
<!-- 替换 action 属性 -->
<form action="#" th:attr="action=@{/suscribe}" >
<input type="text" name="name">
<input type="text" name="gender">
<input type="text" name="birthday">
<input type="text" name="email">
<!-- todo select departments -->
<!-- 一次替换多个属性值 1) submit 按钮的 value 属性; 2) class 属性 -->
<input type="submit" value="Submit" th:attr="value=#{form.submit}, class='sep'">
</form>
3.5.2 设置指定属性
一般 th:attr 很少会用到, 而是会直接使用指定的 th:*.
比如已经用过的 th:href, th:text, th:value, th:action ...
几乎所有的 html 元素属性都有对应的 th:* 版本.
3.6 迭代
3.6.1 迭代初步
使用 th:each
<ul>
<li>
<span class="list">编号</span>
<span class="list">姓名</span>
<span class="list">性别</span>
<span class="list">生日</span>
<span class="list">部门</span>
<span class="list">编辑</span>
<span class="list">删除</span>
</li>
<li th:each="emp : ${employees}">
<span class="list" th:text="${emp.id}"></span>
<span class="list" th:text="${emp.name}"></span>
<span class="list" th:text="${emp.gender == 1} ? '男': '女'"></span>
<span class="list" th:text="${{emp.birthday}}"></span>
<span class="list" th:text="${emp.department.name}"></span>
<span class="list"><a href="#">编辑</a></span>
<span class="list"><a href="#">删除</a></span>
</li>
</ul>
th:each 用起来很方便, 就像 java 中的 for 循环一样.
for(Employee emp: employees) {
// Do something
}
在 thymeleaf 中使用迭代太方便了! 回想 jstl 那些坑爹的标签, 泪都留下来...
可以用 th:each 迭代的 java 类型
th:each 不仅仅可以对 java.util.List 类型迭代, 实际上大部分的 java 集合类型都可以使用它来迭代.
实现了 java.util.Iterable 接口的对象
实现了 java.util.Enumeration 接口的对象
实现了 java.util.Iterator 接口的对象, 不会一次性读入内存, 返回一个读一个.
实现了 java.util.Map 接口的对象, 这时候迭代的值是 java.util.Map.Entry.
任何数组
3.6.2 保存迭代状态
th:each 还提供了一个变量可以保存迭代状态. 用法是 th:each="emp, status: ${employees}"
状态变量保存了以下数据:
*index* 属性, 0 开始的索引值
*count* 属性, 1 开始的索引值
*size* 属性, 集合内元素的总量
*current* 属性, 当前的迭代对象
*even/odd* 属性, boolean 类型的, 用来判断是否是偶数个还是奇数个
*first* 属性, boolean 类型, 是否是第一个
*last* 属性, boolean 类型, 是否是最后一个
看例子:
<ul>
<li>
<span class="list">编号</span>
<span class="list">姓名</span>
<span class="list">性别</span>
<span class="list">生日</span>
<span class="list">部门</span>
<span class="list">编辑</span>
<span class="list">删除</span>
<span class="list status">当前迭代状态</span>
</li>
<li th:each="emp, status: ${employees}" th:class="${status.odd} ? 'odd': 'even'">
<span class="list" th:text="${emp.id}"></span>
<span class="list" th:text="${emp.name}"></span>
<span class="list" th:text="${emp.gender == 1} ? '男': '女'"></span>
<span class="list" th:text="${{emp.birthday}}"></span>
<span class="list" th:text="${emp.department.name}"></span>
<span class="list"><a href="#">编辑</a></span>
<span class="list"><a href="#">删除</a></span>
<span class="list status" th:text="|index: ${status.index}; count: ${status.count}; size: ${status.size}; first: ${status.first}|"></span>
</li>
</ul>
也可以不显式声明 status 变量, thymeleaf 会自动创建一个, 状态变量的名称是你声明的