hymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 JSP,Velocity,FreeMaker 等,它也可以轻易的与 Spring MVC 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。
Thymeleaf 初探(原文位置 )
相比于其他的模板引擎,Thymeleaf 最大的特点是通过 HTML 的标签属性渲染标签内容,以下是一个 Thymeleaf 模板例子:
XHTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head> <title>Good Thymes Virtual Grocery</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" media="all" href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" /> </head>
<body>
<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
</body>
</html> |
这是一段标准的 HTML 代码,这也就意味着通过浏览器直接打开它是可以正确解析它的结构并看到页面的样子。相比去其他的模板引擎在指定的位置通过${}
等表达式进行渲染,Thymeleaf 则是一种针对 HTML/XML 定制的模板语言(当然它可以被扩展),它通过标签中的th:text
属性来填充该标签的一段内容。上例中,<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
意味着<p>
标签中的内容会被表达式#{home.welcome}
的值所替代,无论模板中它的内容是什么,之所以在模板中 “多此一举 “地填充它的内容,完全是为了它能够作为原型在浏览器中直接显示出来。
标准表达式语法
变量
Thymeleaf 模板引擎在进行模板渲染时,还会附带一个 Context 存放进行模板渲染的变量,在模板中定义的表达式本质上就是从 Context 中获取对应的变量的值:
XHTML
1 | <p>Today is: <span th:text="${today}">13 february 2011</span>.</p> |
假设today
的值为2015年8月14日
,那么渲染结果为:<p>Today is: 2015年8月14日.</p>
。可见 Thymeleaf 的基本变量和 JSP 一样,都使用${.}
表示获取变量的值。
URL
URL 在 Web 应用模板中占据着十分重要的地位,需要特别注意的是 Thymeleaf 对于 URL 的处理是通过语法@{...}
来处理的。Thymeleaf 支持绝对路径 URL:
XHTML
1 | <a th:href="@{http://www.thymeleaf.org}">Thymeleaf</a> |
同时也能够支持相对路径 URL:
- 当前页面相对路径 URL——
user/login.html
,通常不推荐这样写。 - Context 相关 URL——
/static/css/style.css
另外,如果需要 Thymeleaf 对 URL 进行渲染,那么务必使用th:href
,th:src
等属性,下面是一个例子
XHTML
1 2 3 4 5 6 7 8 9 | <!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) --> <a href="details.html" th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) --> <a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) --> <a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a> |
几点说明:
- 上例中 URL 最后的 (orderId=${o.id}) 表示将括号内的内容作为 URL 参数处理,该语法避免使用字符串拼接,大大提高了可读性
@{...}
表达式中可以通过{orderId}
访问 Context 中的orderId
变量@{/order}
是 Context 相关的相对路径,在渲染时会自动添加上当前 Web 应用的 Context 名字,假设 context 名字为 app,那么结果应该是/app/order
如果 URLs 写在 style 的属性里,那么
XHTML
1 | <div class="fill" style="background-image:url('../../style/images/bg_shadow_left.png');"></div> |
替换写成 style 前面加 th: 链接的前后加上 + 号运算符,如下:
XHTML
1 | <div class="fill" th:style="'background-image:url(' + @̣{/images/bg_shadow_left.png} + ');'"></div> |
字符串替换
很多时候可能我们只需要对一大段文字中的某一处地方进行替换,可以通过字符串拼接操作完成:
XHTML
1 | <span th:text="'Welcome to our application, ' + ${user.name} + '!'"> |
一种更简洁的方式是:
XHTML
1 | <span th:text="|Welcome to our application, ${user.name}!|"> |
当然这种形式限制比较多,|...|
中只能包含变量表达式${...}
,不能包含其他常量、条件表达式等。
运算符
在表达式中可以使用各类算术运算符,例如 +, -, *, /, %
XHTML
1 | th:with="isEven=(${prodStat.count} % 2 == 0)" |
逻辑运算符>
, <
, <=
,>=
,==
,!=
都可以使用,唯一需要注意的是使用<
,>
时需要用它的 HTML 转义符:
XHTML
1 2 | th:if="${prodStat.count} > 1" th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')" |
循环
渲染列表数据是一种非常常见的场景,例如现在有 n 条记录需要渲染成一个表格<table>
,该数据集合必须是可以遍历的,使用th:each
标签:
XHTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <body> <h1>Product list</h1>
<table> <tr> <th>NAME</th> <th>PRICE</th> <th>IN STOCK</th> </tr> <tr th:each="prod : ${prods}"> <td th:text="${prod.name}">Onions</td> <td th:text="${prod.price}">2.41</td> <td th:text="${prod.inStock}? #{true} : #{false}">yes</td> </tr> </table>
<p> <a href="../home.html" th:href="@{/}">Return to home</a> </p> </body> |
可以看到,需要在被循环渲染的元素(这里是<tr>
)中加入th:each
标签,其中th:each="prod : ${prods}"
意味着对集合变量prods
进行遍历,循环变量是prod
在循环体中可以通过表达式访问。
条件求值
If/Unless
Thymeleaf 中使用th:if
和th:unless
属性进行条件判断,下面的例子中,<a>
标签只有在th:if
中条件成立时才显示:
XHTML
1 | <a th:href="@{/login}" th:unless=${session.user != null}>Login</a> |
th:unless
于th:if
恰好相反,只有表达式中的条件不成立,才会显示其内容。
<a th:href="@{/login}" th:if=${session.user==null}>Login</a>
Switch
Thymeleaf 同样支持多路选择 Switch 结构:
XHTML
1 2 3 4 | <div th:switch="${user.role}"> <p th:case="'admin'">User is an administrator</p> <p th:case="#{roles.manager}">User is a manager</p> </div> |
默认属性 default 可以用*
表示:
XHTML
1 2 3 4 5 | <div th:switch="${user.role}"> <p th:case="'admin'">User is an administrator</p> <p th:case="#{roles.manager}">User is a manager</p> <p th:case="*">User is some other thing</p> </div> |
Utilities
为了模板更加易用,Thymeleaf 还提供了一系列 Utility 对象(内置于 Context 中),可以通过#
直接访问:
#dates
#calendars
#numbers
#strings
arrays
lists
sets
maps
- …
下面用一段代码来举例一些常用的方法:
#dates
XHTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* * 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()} |
#strings
XHTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /* * 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)} |
页面即原型
在 Web 开发过程中一个绕不开的话题就是前端工程师与后端工程师的协作,在传统 Java Web 开发过程中,前端工程师和后端工程师一样,也需要安装一套完整的开发环境,然后各类 Java IDE 中修改模板、静态资源文件,启动 / 重启 / 重新加载应用服务器,刷新页面查看最终效果。
但实际上前端工程师的职责更多应该关注于页面本身而非后端,使用 JSP,Velocity 等传统的 Java 模板引擎很难做到这一点,因为它们必须在应用服务器中渲染完成后才能在浏览器中看到结果,而 Thymeleaf 从根本上颠覆了这一过程,通过属性进行模板渲染不会引入任何新的浏览器不能识别的标签,例如 JSP 中的<form:input>
,不会在 Tag 内部写表达式。整个页面直接作为 HTML 文件用浏览器打开,几乎就可以看到最终的效果,这大大解放了前端工程师的生产力,它们的最终交付物就是纯的 HTML/CSS/JavaScript 文件。
Thymeleaf 之 内置对象、定义变量、URL参数及标签自定义属性
如标题所述,这篇文章主要讲述Thymeleaf中的内置对象(list解析、日期格式化、数字格式化等)、定义变量、获取URL的参数和在页面标签中自定义属性的应用。
如果对Thymeleaf的基本使用、maven依赖等不清楚的可以先阅读我的另一篇文章《Thymeleaf 之 初步使用》。
- Controller部份
@Controller
public class IndexController {
@GetMapping(value = "index")
public String index(Model model, HttpServletRequest request) {
List<String> datas = new ArrayList<String>();
datas.add("知识林");
datas.add("http://www.zslin.com");
datas.add("393156105");
model.addAttribute("datas", datas);
model.addAttribute("curDate", new Date());
model.addAttribute("money", Math.random()*100);
return "index";
}
}
在这个控制器的Model中存放了这样几个数据:一个String类型的列表、一个日期对象和一个数值,这些东西在实际应用开发过程中应用非常广泛,下面具体看一下在Thymeleaf中是如何解析这些数据的。
- 日期格式化
<span th:text="${#dates.format(curDate, 'yyyy-MM-dd HH:mm:ss')}"></span>
说明: 使用内置对象dates
的format
函数即可对日期进行格式化,在format
函数中,第一个参数是日期对象,对二两个参数为日期格式(规则跟SimpleDateFormat
一样)
需要注意的是:
· 内置对象一般都以s
结尾,如dates
、lists
、numbers
等
· 在使用内置对象是在对象名前都需要加#
号。
- 数字格式化
<span th:text="${#numbers.formatDecimal(money, 0, 2)}"></span>
说明: 此示例表示保留两位小数位,整数位自动;
<span th:text="${#numbers.formatDecimal(money, 3, 2)}"></span>
说明: 此示例表示保留两位小数位,3位整数位(不够的前加0)
- 获取列表长度
<span th:text="${#lists.size(datas)}"></span>
说明: 使用#lists.size
来获取List的长度。
- 获取URL参数值
<span th:text="${#httpServletRequest.getParameter('page')}"></span>
说明: 当访问http://localhost:1105/index?page=5
时页面将会得到page
对应的值:5
。
- 定义变量
<div th:with="curPage=${#httpServletRequest.getParameter('page')}">
<h3>当前页码:<span th:text="${curPage}"></span></h3>
</div>
说明: 同样,当访问http://localhost:1105/index?page=5
时,页面将显示:当前页码:5
,说明用th:with
来定义变量,多个用,
号隔开,使用范围在当前标签内。
- 自定义标签属性
在Thymeleaf中可以使用th:
加上标签的任何属性进行赋值,但有些时候会遇到自定义的属性,再用th:
加自定义的属性则会无效。比如:需要对<span>
标签增加objName
和objId
这样的属性,在非Thymeleaf情况下是这样:
<span objId="1" objName="知识林"></span>
变量情况是:
<span objId="${obj.id}" objName="${obj.name}"></span>
在Thymeleaf下则是:
<span th:attr="myDate=${#dates.format(curDate, 'yyyy-MM-dd')}, myMoney=${money}"></span>
说明: 在页面上查看源代码可以看到:<span myMoney="91.6059494319957" myDate="2016-31-02"></span>
,说明自定义属性用:th:attr
,多个属性用,
隔开。
- 内置对象
上面简单描述了比较常用的dates
、lists
、numbers
这几个内置对象,在Thymeleaf还有很多的内置对象,像strings
也非常常用,用法跟java.lang.String
类的用法一样。
在Thymeleaf中的内置对象有:
#dates
:日期格式化内置对象,具体方法可以参照java.util.Date
;
#calendars
:类似于#dates
,但是是java.util.Calendar
类的方法;
#numbers
: 数字格式化;
#strings
:字符串格式化,具体方法可以参照java.lang.String
,如startsWith
、contains
等;
#objects
:参照java.lang.Object
;
#bools
:判断boolean类型的工具;
#arrays
:数组操作的工具;
#lists
:列表操作的工具,参照java.util.List
;
#sets
:Set操作工具,参照java.util.Set
;
#maps
:Map操作工具,参照java.util.Map
;
#aggregates
:操作数组或集合的工具;
#messages
:操作消息的工具。
thymeleaf+bootstrap,onclick传参
<span title="删除"
th:onclick="'javascript:delCfmModel(\'/user/' + ${user.id} + '/delete\')'"
style="cursor: pointer"
class="glyphicon glyphicon-trash">
</span>
或 使用字符串拼接的另外一种简洁写法:<span th:text="|Welcome to our application, ${user.name}!|">
<span title="删除"
th:onclick="|javascript:delCfmModel('/sys/user/${user.id}/delete')|"
style="cursor: pointer"
class="glyphicon glyphicon-trash">
</span>
如果参数是字符串,需要加引号,如上图所示。否则会报错:Uncaught SyntaxError:Invalid regular expressiong flags
如果只传数字,可以不加引号,如下图所示:
<a href="#editModal"
role="button"
data-toggle="modal"
th:onclick="'javascript:editUser('+${prod.id}+');'">
Edit
</a>
<script>
function editUser(id) {
$.get("/projectName/user/edit",
{objectid: id},
function (data) {
$("#frm_container1").html(data);
});
}
</script>
https://yq.aliyun.com/articles/60696
<!-- 信息删除确认 -->
<div class="modal fade" id="deleteModal">
<div class="modal-dialog">
<div class="modal-content message_align">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">Delete</h4>
</div>
<div class="modal-body" style="text-align: center">
<p>
<i class="glyphicon glyphicon-alert"></i>
Do You Really Want to Delete This?
</p>
</div>
<div class="modal-footer">
<input type="hidden" id="modalUrl"/>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<a id="modalConfirmBtn" class="btn btn-success" data-dismiss="modal">Delete</a>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<script type="text/javascript">
function delCfmModel(url) {
$('#modalUrl').val(url);//给会话中的隐藏属性URL赋值
$('#deleteModal').modal();
}
$("#modalConfirmBtn").click(function () {
$.get($('#modalUrl').val(),function () {
window.location.reload(); //重新加载页面
});
});
</script>
如果需要弹出的Modal窗口垂直居中,水平居中,可以添加以下css样式(这个样式是覆盖了bootstrap的。tips,这个css是为上面的Modal div深度定制的,如需更改,需确定上面的html是否同步更改)
.modal {
text-align: center;
}
@media screen and (min-width: 768px) {
.modal:before {
display: inline-block;
vertical-align: middle;
content: " ";
height: 100%;
}
}
.modal-dialog {
display: inline-block;
text-align: left;
vertical-align: middle;
}