深色模式
14.2 订单详情页
接下来我们实现订单详情页面,这个页面会大量使用星号表达式(*{...})(即选择表达式),核心代码如下:
html
<!DOCTYPE html>
<html 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 th:object="${order}">
<h1>Order details</h1>
<div>
<p><b>Code:</b> <span th:text="*{id}">99</span></p>
<p>
<b>Date:</b>
<span th:text="*{#calendars.format(date,'dd MMM yyyy')}">13 jan 2011</span>
</p>
</div>
<h2>Customer</h2>
<!-- 嵌套的对象选择 -->
<div th:object="*{customer}">
<p><b>Name:</b> <span th:text="*{name}">Frederic Tomato</span></p>
<p>
<b>Since:</b>
<span th:text="*{#calendars.format(customerSince,'dd MMM yyyy')}">1 jan 2011</span>
</p>
</div>
<h2>Products</h2>
<table>
<tr>
<th>PRODUCT</th>
<th>AMOUNT</th>
<th>PURCHASE PRICE</th>
</tr>
<!-- 遍历订单行,奇偶行样式区分 -->
<tr th:each="ol,row : *{orderLines}" th:class="${row.odd}? 'odd'">
<td th:text="${ol.product.name}">Strawberries</td>
<td th:text="${ol.amount}" class="number">3</td>
<td th:text="${ol.purchasePrice}" class="number">23.32</td>
</tr>
</table>
<div>
<b>TOTAL:</b>
<!-- 聚合计算:所有订单行的 (单价 * 数量) 求和 -->
<span th:text="*{#aggregates.sum(orderLines.{purchasePrice * amount})}">35.23</span>
</div>
<p>
<a href="list.html" th:href="@{/order/list}">Return to order list</a>
</p>
</body>
</html>这个页面其实并无太多新内容,除了嵌套的对象选择(th:object):
html
<!-- 外层:绑定根对象为 ${order} -->
<body th:object="${order}">
<!-- 内层:基于外层对象,选择 customer 属性作为当前对象 -->
<div th:object="*{customer}">
<!-- *{name} 等价于 ${order.customer.name} -->
<p><b>Name:</b> <span th:text="*{name}">Frederic Tomato</span></p>
</div>
</body>使得*{name}等价于:
html
<!-- 外层:绑定根对象为 ${order} -->
<p><b>Name:</b> <span th:text="${order.customer.name}">Frederic Tomato</span></p>- 逻辑:
th:object是「当前上下文对象」的绑定,内层th:object="*{customer}"会基于外层的order对象,选择其customer属性作为新的上下文对象; - 简化:星号表达式
*{name}会直接读取当前上下文对象(即order.customer)的name属性,等价于${order.customer.name},避免了冗长的层级书写;
其他关键语法(前文知识点回顾)
| 代码片段 | 功能说明 |
|---|---|
th:href="@{/css/gtvg.css}" | 绝对路径URL,自动适配应用上下文路径,替代静态的 ../../../css/gtvg.css |
*{id} | 读取当前上下文对象(order)的 id 属性,等价于 ${order.id} |
#calendars.format(date,'dd MMM yyyy') | 日期格式化工具,将 order 的 date 属性转为指定格式的字符串 |
th:each="ol,row : *{orderLines}" | 遍历 order 的 orderLines 集合,row 是状态变量,用于区分奇偶行 |
th:class="${row.odd}? 'odd'" | 条件样式:奇数行添加 odd 类名,实现隔行变色 |
#aggregates.sum(orderLines.{purchasePrice * amount}) | 聚合工具:计算所有订单行的 purchasePrice * amount 之和(订单总价) |
th:href="@{/order/list}" | 跳转订单列表页的动态URL,替代静态的 list.html |
静态原型友好性
所有动态表达式后都保留了静态占位符(如 <span th:text="*{id}">99</span>),直接打开 HTML 文件时能看到模拟数据,符合 Thymeleaf「原生模板」的设计理念。
总结
- 嵌套
th:object是星号表达式的高级用法:内层th:object="*{属性名}"可基于外层上下文对象,选择子属性作为新的上下文,简化层级访问; *{属性名}等价于${外层对象.内层对象.属性名},能大幅简化嵌套对象的属性读取;- 页面综合运用了 URL 链接、日期格式化、遍历、条件样式、聚合计算等 Thymeleaf 核心语法,是典型的业务页面开发实践。
