thymeleaf
Thymeleaf学习笔记
SpringThymeleaf
Web开发离不开动态页面的开发,很早以前企业主要使用JSP技术来开发网页,随着技术的升级更替,目前来说最主流的方案是: Thymeleaf, Thymeleaf 是一个模板框架,它可以支持多种格式的内容动态渲染非常强大,它天然和HTML是相融合的,所以对于前端工程师来说它也是易于理解的。Thymeleaf机制:数据+模板+引擎
初始化Thymeleaf
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
代码举例:
@Controller
public class SongListControl {
@Autowired
private SongListService songListService;
@RequestMapping("/songlist")
public String index(@RequestParam("id")String id,Model model){
SongList songList = songListService.get(id);
//传递歌单对象到模板当中
//第一个 songList 是模板中使用的变量名
// 第二个 songList 是当前的对象实例
model.addAttribute("songList",songList);
return "songList";
}
}
注意:
springboot默认访问的是templates中的内容,也就是说return后面的地址接的是templates中的文件路径.想要访问static中的文件路径,就必须要在return后面接redirect重定向。
Thymeleaf变量
由于Thymeleaf是完全兼容HTML的,所以为了不破坏HTML结构,Thymeleaf 采用了自定义HTML属性的方式来生成动态内容。th:text语法的作用就是会动态替换掉作用HTML标签的内部内容。
- 代码举例:
<span th:text="${msg}">Hello</span>
- 渲染结果
<span>你好</span>
一般情况下,模板的变量都是会存放在模板上下文中,所以我们如果想要调用变量,就需要先设置变量到模板上下文中去
扩展:
上下文这个术语在编程里用的比较多,一 般泛指在某个具体的实例里,比如说模板上下文, 指的是这个模板运行的实例。
对象变量
注意:
如果对象包含对象,还是可以用.一直点出来的, 前提是这个对象是POJO类哦
- Thymeleaf循环语句
<ul th:each="song : ${songs}">
<li th:text="${song.name}">歌曲名称</li>
</ul>
打印列表的索引值
<ul th:each="song,it: ${songs}">
<li>
<span th:text="${it.count}"></span>
<span th:text="${song.name}"></span>
</li>
</ul>
这个it是作为可选参数,如果定义了就可以通过这个it对象来获取更多关于统计的需求,具体参数如
Thymeleaf表达式
1..${}
字符串处理
<span th:text="'00:00/'+${totalTime}"></span>
静态内容用单引号''包裹起来,和上下文变量之间用+连接。
字符串拼接优化
Thymeleaf做字符串拼接还做了优化工作,我们可以使用上面的代码你还可以这样|围住字符串,这样就不需要在文字后面附加'...'+'...'
<span th:text="|00:00/${totalTime}|"></span>
数据转化
Thymeleaf默认集成了大量的工具类可以方便的进行数据转化,一我们使用最多的是dates
@RequestMapping("/demo")
public String index(Model model){
Date dateVar = new Date();
model.addAttribute("dateVar",dateVar);
return "demo";
}
前台显示:
//显示年月日
<p th:text="${#dates.format(dateVar, 'yyyy-MM-dd')}"></p>
<p th:text="${#dates.format(dateVar, 'yyyy年MM月dd日')}"></p>
//显示年月日时分秒
<p th:text="${#dates.format(dateVar, 'yyyy-MM-dd HH:mm:ss')}"></p>
<p th:text="${#dates.format(dateVar, 'yyyy年MM月dd日 HH时mm分ss秒')}"></p>
在这里如果后台的日期类型是LocalDate/LocalDateTime,那么就把#date换成#temporals
注意:需要在pom.xml文件里添加依赖
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
这个库会自动添加一个新的工具类temporals
工具类的运用和变量不同,变量使用的是${变量名} ,工具类使用的是#{工具类}。
后台代码:
@RequestMapping("/demo")
public String index(Model model){
LocalDateTime dateVar = LocalDateTime.now();
model.addAttribute("dateVar",dateVar);
return "demo";
}
前台代码:
//显示年月日
<p th:text="${#temporals.format(dateVar, 'yyyy-MM-dd')}"></p>
<p th:text="${#temporals.format(dateVar, 'yyyy年MM月dd日')}"></p>
````html
//显示年月日时分秒
````html
<p th:text="${#temporals.format(dateVar, 'yyyy-MM-dd HH:mm:ss')}"></p>
<p th:text="${#temporals.format(dateVar, 'yyyy年MM月dd日 HH时mm分ss秒')}"></p>
dates/temporals工具类说明
dates和temporals支持的方法是一样的, 只是支持的类型不同,dates支持的是Date类, temporals支持的是LocalDate 和LocalDateTime。
java.util.Date类和LocalDateTime类功能是一样的,不同的是LocalDateTime是Java8才出现的,一老的应用还是用Date类的。
strings工具类
Thymeleaf里的其他工具类
各内置对象包含哪些方法可以点此看文档,也可以点这里看官方文档。
Thymeleaf内联表达式
其实就是th:text的另一种写法,直接把变量写在HTML中,格式:[[变量]]
<span>Hello [[${msg}]]</span>
<p>[[ ${#dates.format(dateVar, 'yyyy-MM-dd')} ]]</p>
<p>[[${#dates.format(dateVar, 'yyyy年MM月dd日')}]]</p>
<p>[[${#dates.format(dateVar, 'yyyy-MM-dd HH:mm:ss')}]]</p>
<p>[[${#dates.format(dateVar, 'yyyy年MM月dd日 HH时mm分ss秒')}]]</p>
注意:
内联表达式[[]]只能替代th:tex不是替代所有的th:标签。
2.*{}
选择表达式(星号表达式)。选择表达式与变量表达式有一个重要的区别:选择表达式计算的是选定的对象,而不是整个环境变量映射。也就是:只要是没有选择的对象,选择表达式与变量表达式的语法是完全一样的。那什么是选择的对象呢?是一个:th:object
对象属性绑定的对象。
例如:
<div th:object=" ${session.user}" >
<p>Name: <span th: text=" *{firstName}" >Sebastian</span>. </p>
<p>Surname: <span th: text=" *{lastName}" >Pepper</span>. </p>
<p>Nationality: <span th: text=" *{nationality}" >Saturn</span>. </p>
</div>
上例中,选择表达式选择的是th:object对象属性绑定的session. user对象中的属性。
3.#{}
消息表达式(井号表达式,资源表达式)。通常与th:text属性一起使用,指明声明了th:text的标签的文本是#{}中的key所对应的value,而标签内的文本将不会显示。
例如:
新建/WEB-INF/templates/home.html,段落
<p th: text=" #{home. welcome}" >This text will not be show! </p>
新建/WEB-INF/templates/home.properties,home.welcome:
home.welcome=this messages is from home.properties!
测试结果:
从测试结果可以看出,消息表达式通常用于显示页面静态文本,将静态文本维护在properties文件中也方便维护,做国际化等。
4.@{}
超链接url表达式。
例如:
<script th:src="@{/resources/js/jquery/jquery.json-2.4.min.js}"/>
5.#maps
工具对象表达式。常用于日期、集合、数组对象的访问。这些工具对象就像是java对象,可以访问对应java对象的方法来进行各种操作。用法在上面数据转化已经举例
-
dates
-
calendars
-
numbers
-
strings
-
objects
-
bools
-
arrays
-
lists
-
sets
Thymeleaf条件语句
th:if条件判断除了判断boolean值外,Thymeleaf 还认为如下表达式为true:
• 值非空
• 值是非0数字
• 值是字符串,但是不是false, off or no
• 值不是boolean值,数字,character或字符串
Strings逻辑判断
isEmpty
contains
- 检查字符串变量是否包含片段
${#strings.contains(name,'abc')}
-
strings判断语法的其他几个常用方法
-
strings的字符串操作函数
除了字符串判断语句外,#strings 还支持字符串的数据处理,比如
Thymeleaf表单
Springboot做了自动处理,通过post方式提交表单的时候,需要有一个实体类,去接收表单传递的数据内容,在对象的属性读取中,Thymeleaf 提供了两种方式:1、直接通过${userInfo.username} ,这种实体bean + 属性的方式;2、通过选择表达式*{username}的这种方式。表单input中的name属性值要和实体类的属性保持一致
Spring Validation
在实际的工作中对于数据的保存是离不开数据验证的,比如说name必须要输入,isbn 必须要输入等等校验规则,Spring 对于数据验证支持的也非常好,我们可以借助Spring Validation来处理表单数据的验证。
Validation注解
JSR 380定义了-些注解用于做数据校验,这些注解可以直接设置在Bean的属性上
|注解|说明|备注|
|------|----------|-----------|
|@NotNull |不允许为null对象||
|@AssertTrue|是否为true||
|@Size|约定字符串的长度||
|@Min|字符串的最小长度||
|@Max|字符串的最大长度||
|@Email|是否是邮箱格式||
|@NotEmpty|不允许为null或者为空,可以用于判断字符串、集合,比如Map、数组、List||
|@NotBlank|不允许为null和空格||
注意:
大多数情况下,建议使用NotEmpty替代NotNull、NotBlank
package com.bookstore.model;
import javax.validation.constraints.*;
public class User {
@NotEmpty(message = "名称不能为 null")
private String name;
@Min(value = 18, message = "你的年龄必须大于等于18岁")
@Max(value = 150, message = "你的年龄必须小于等于150岁")
private int age;
@NotEmpty(message = "邮箱必须输入")
@Email(message = "邮箱不正确")
private String email;
// standard setters and getters
}
校验的注解是可以累加的,如上面的@Min和@Max,系统会按顺序执行校验,任何一条校验触发就会抛出校验错误到上下文中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>添加用户</title>
<style>
.error {
color: red;
}
</style>
</head>
<body>
<h2>添加用户</h2>
<form action="/user/save" th:object="${user}" method="POST">
<div th:classappend="${#fields.hasErrors('name')} ? 'error' : ''">
<label>用户名称:</label>
<input type="text" th:field="*{name}">
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></p>
</div>
<div th:classappend="${#fields.hasErrors('age')} ? 'error' : ''">
<label>年龄:</label>
<input type="text" th:field="*{age}">
<p th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></p>
</div>
<div th:classappend="${#fields.hasErrors('email')} ? 'error' : ''">
<label>邮箱:</label>
<input type="text" th:field="*{email}">
<p th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></p>
</div>
<div th:classappend="${#fields.hasErrors('mobile')} ? 'error' : ''">
<label>手机号码:</label>
<input type="text" th:field="*{mobile}">
<p th:if="${#fields.hasErrors('mobile')}" th:errors="*{mobile}"></p>
</div>
<div>
<button type="submit">保存</button>
</div>
</form>
</body>
</html>
说明:
1.control类中的方法药先new一个实例对象传到页面去,如下代码中的addUser方法。
2.form表单里的th:object="${user}"
用于替换对象,使用了这个就不需要每次都编写user. xxx,可以直接操作XXX
3.${#fields.hasErrors('key')}
这个语法是专门为验证场景提供的,这里的 key就是对象的属性名称,比如User对象的name、age、 email 等。
4.<p></p>
标签里的th:errors
,用于显示错误信息。
5.<div></div>
里的th:classappend
支持动态管理样式
6.<input></input>
里的th:field
可以显示上一次输入的内容。
package com.bookstore.control;
import com.bookstore.model.User;
import com.bookstore.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import javax.validation.Valid;
import java.util.List;
@Controller
public class UserControl {
@Autowired
private UserService userService;
@GetMapping("/user/add.html")
public String addUser(Model model) {
User user = new User();
model.addAttribute("user", user);
return "user/addUser";
}
@PostMapping("/user/save")
public String saveUser(@Valid User user, BindingResult errors) {
if (errors.hasErrors()) {
// 如果校验不通过,返回用户编辑页面
return "user/addUser";
}
userService.saveUser(user);
// 校验通过,返回成功页面
return "redirect:/user/list.html";//跳转到@GetMapping("/user/list.html")
}
@GetMapping("/user/list.html")
public String list(Model model) {
List<User> users = userService.getUsers();
model.addAttribute("users", users);
return "user/list";
}
}
说明:
redirect:这个用于跳转到某-一个页面网址,如果是同一个域名,你可以省略域名,直接写path, 比如这里的/user/list. html你也可以跳转到某个网站,比如return "redirect: https: //www. baidu. com";大体上所有的增加、查询行为都是这样的流程。
Thymeleaf布局(Layout)
大多数的网站都有导航、底部等公共的东西,在一个网站里访问页面总是会显示相同的导航、底部之类的内容,layout解决的是模板复用的问题,比如常见的网站是下面这样的
我们推荐使用th:include + th:replace
方案来完成布局的开发
代码实现:
layout.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>布局</title>
<style>
.header {background-color: #f5f5f5;padding: 20px;}
.header a {padding: 0 20px;}
.container {padding: 20px;margin:20px auto;}
.footer {height: 40px;background-color: #f5f5f5;border-top: 1px solid #ddd;padding: 20px;}
</style>
</head>
<body>
<header class="header">
<div>
<a href="/book/list.html">图书管理</a>
<a href="/user/list.html">用户管理</a>
</div>
</header>
<div class="container" th:include="::content">页面正文内容</div>
<footer class="footer">
<div>
<p style="float: left">© heerh.com 2017</p>
<p style="float: right">
Powered by god666hrh
</p>
</div>
</footer>
</body>
user/list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
th:replace="layout">
<div th:fragment="content">
<h2>用户列表</h2>
<div>
<a href="/user/add.html">添加用户</a>
</div>
<table>
<thead>
<tr>
<th>
用户名称
</th>
<th>
用户年龄
</th>
<th>
用户邮箱
</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.name}"></td>
<td th:text="${user.age}"></td>
<td th:text="${user.email}"></td>
</tr>
</tbody>
</table>
</div>
</html>
总结:
1.在layout页面需要渲染的标签加th:include="::content"
2.在子页面的<html></html>
标签里加th:replace="layout"
,在渲染内容的标签里加th:fragment="content",
目的是为了和公共页面(layout)里的标签内容对应上。
小技巧:
由于超链接有默认的打开链接行为,所以对于想要执行onclick 的时候,我们一般设置href="javascript:;"
使用thymeleaf进行数字循环
需求:使用thymeleaf模版 根据一个数字在页面循环生成固定的标签
解决方案:需要用到thymeleaf 的#numbers.sequence()
函数
示例:
<span th:each="i:${#numbers.sequence(1, 5)}">
<span th:text="${i}"></span>
</span>
效果如下:
1 2 3 4 5
ThymeLeaf注释
1.在html里是代码,在解析时是注释
注:常用在保持静态html代码的完整性上
举例:
<table>
<tr th:each="x : ${xs}">
...
</tr>
<!--/*-->
<tr>
...
</tr>
<tr>
...
</tr>
<!--*/-->
</table>
2.在html里是注释,在解析时是代码
举例:
<span>hello!</span>
<!--/*/
<div th:text="${...}">
...
</div>
/*/-->
<span>goodbye!</span>
Thymeleaf调用对象的成员变量值
Thymeleaf调用对象的成员变量值、Map值、List值、属性的方法 、ctx对象、param、session和application
一、概述
本文会对thymeleaf以下功能进行举例说明:
• 调用对象的成员变量的属性
• 调用Map的对象的属性
• 调用List的对象的属性
• 调用属性的方法
• 使用ctx对象
• param:获取request的请求参数
• session:获取session的属性值
• application:获取application的属性值
二、实例说明
1.调用对象的成员变量的属性
- 演示如下功能
<!-- 获取family的成员变量father的属性 -->
${family.father.name} --> <input type="text" name="userName" th:value="${family.father.name}" />
- 输出:
=================== 调用对象的成员变量的属性 ===============================
${family.father.name} --> father
2.调用Map的对象的属性
- 演示如下功能
通过map的key从hashMap获取对象的属性name值: 可以使用”.”或者使用”[]”获取对象值
<!-- 通过map的key从hashMap获取对象的属性name值: 可以使用"."或者使用"[]"获取对象值 -->
${hashMap.hashMapKey.name} --> <input type="text" name="userName" th:value="${hashMap.hashMapKey.name}" /> <br />
<!-- 等价于这条语句:-->
${hashMap['hashMapKey'].name} --> <input type="text" name="userName" th:value="${hashMap['hashMapKey'].name}" /> <br />
- 输出:
=================== 调用Map的对象的属性 ===============================
${hashMap.hashMapKey.name} --> hashMap_name
等价于这条语句:
${hashMap['hashMapKey'].name} --> hashMap_name
3. 调用List的对象的属性
- 演示如下功能
通过[0]获取List的第一个对象的属性name值
<!-- 通过[0]获取List的第一个对象的属性name值 -->
${family.childList[0].name} --> <input type="text" name="userName" th:value="${family.childList[0].name}" /> <br />
<br />
- 输出:
=================== 调用List的对象的属性 ===============================
${family.childList[0].name} --> son_1
4. 调用属性的方法
- 演示如下功能
调用属性的方法
<!-- 调用属性的方法 -->
${family.father.name.toUpperCase()} --> <input type="text" name="userName" th:value="${family.father.name.toUpperCase()}" /> <br />
- 输出: “–>”的左边是语法,右边是对应的输出
=================== 调用属性的方法 ===============================
${family.father.name.toUpperCase()} --> FATHER
5. 使用ctx对象
-
演示如下功能
-
ctx.locale: 获取#ctx的locale值
-
ctx.variables: 获取#ctx的变量中的一个的值
-
ctx.variables: 变量中值的一个的值,等价于#ctx.variables
-
ctx.httpServletRequest:请求request
-
ctx.httpServletResponse:返回response
-
ctx.httpSession:返回session
-
ctx.servletContext:返回servletContext
-
在非web的环境里,ctx只是org.thymeleaf.context.IContext的一个实例,而在web的环境里org.thymeleaf.context.IWebContext的一个实例,此接口同时是org.thymeleaf.context.IContext的子类。IWebContext比IContext多了httpServletRequest,httpServletResponse,httpSession,servletContext等属性,详细可以见源代码。
<!-- #ctx是org.thymeleaf.context.IContext -->
${#ctx.locale} --> <input type="text" name="userName" th:value="${#ctx.locale}" /> <br />
<!-- #ctx获取ctx的变量中的一个的值 -->
${#ctx.variables.hashMap} --> <input th:value="${#ctx.variables.hashMap}" ></input> <br />
<!-- vars变量中值的一个的值,等价于#ctx.variables -->
${#ctx.variables.hashMap} --> <input th:value="${#vars.hashMap}" ></input> <br />
<!-- 以下#ctx是org.thymeleaf.context.IWebContext的一个实例,他也是IContext的子类 -->
<!-- #request -->
${#ctx.httpServletRequest} --> <input th:value="${#ctx.httpServletRequest}" ></input> <br />
<!-- #response -->
${#ctx.httpServletResponse} --> <input th:value="${#ctx.httpServletResponse}" ></input> <br />
<!-- #session -->
${#ctx.httpSession} --> <input th:value="${#ctx.httpSession}" ></input> <br />
<!-- #servletContext -->
${#ctx.servletContext} --> <input th:value="${#ctx.servletContext}" ></input> <br />
- 结果:
=================== 使用ctx对象: Base objects context object ===============================
${#ctx.locale} --> zh_CN
${#ctx.variables.hashMap} -->{hashMapKey=com.hry.spring.support.User@1888a92c}
${#ctx.variables.hashMap} -->{hashMapKey=com.hry.spring.support.User@1888a92c}
${#ctx.httpServletRequest} --> org.apache.catalina.connector.RequestFacade@7fdddbce
${#ctx.httpServletResponse} --> org.apache.catalina.connector.ResponseFacade@6e632047
${#ctx.httpSession} -->
${#ctx.servletContext} --> org.apache.catalina.core.ApplicationContextFacade@4149c8a4
6.param:获取request的请求参数
- 演示如下功能
param是org.thymeleaf.context.WebRequestParamsVariablesMap的子,封装了请求参数,下面演示param.size(),param.containsKey('id'),param.get('id')[0]的用法。
<!-- param是org.thymeleaf.context.WebRequestParamsVariablesMap的子类 -->
<!-- 如果 :http://localhost:8080/expressions/complex?id=1,此时有有一个参数输出1 -->
${param.size()} --> <input th:value="${param.size()}" ></input> <br />
${param.containsKey('id')} --> <input th:value="${param.containsKey('id')}" ></input> <br />
${param.get('id')[0]} --> <input th:value="${param.get('id')[0]}" ></input> <br />
等价于:<br/>
${#ctx.httpServletRequest.getParameter('id')} --> <input th:value="${#ctx.httpServletRequest.getParameter('id')}" ></input> <br />
- 结果:
================= param:获取request的请求参数 ===============================
${param.size()} --> 1
${param.containsKey('id')} --> true
${param.get('id')[0]} --> 112
- 等价于:
${#ctx.httpServletRequest.getParameter('id')} --> 112
7. session:获取session的属性值
演示如下功能
session是org.thymeleaf.context.WebSessionVariablesMap的子类
================= session:获取session的属性值 ===============================<br/>
<!-- session是org.thymeleaf.context.WebSessionVariablesMap的子类 -->
${session.size()} --> <input th:value="${session.size()}" ></input> <br />
- 结果
================= session:获取session的属性值 ===============================
${session.size()} --> 0
8. application:获取application的属性值
- 演示如下功能
application是org.thymeleaf.context.WebServletContextVariablesMap的子类
================= application:获取application的属性值 ========================<br/>
<!-- application是org.thymeleaf.context.WebServletContextVariablesMap的子类 -->
${application.size()} --> <input th:value="${application.size()}" ></input> <br />
- 结果:
================= application:获取application的属性值 ========================
${application.size()} --> 9