Skip to content

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「原生模板」的设计理念。

总结

  1. 嵌套 th:object 是星号表达式的高级用法:内层 th:object="*{属性名}" 可基于外层上下文对象,选择子属性作为新的上下文,简化层级访问;
  2. *{属性名} 等价于 ${外层对象.内层对象.属性名},能大幅简化嵌套对象的属性读取;
  3. 页面综合运用了 URL 链接、日期格式化、遍历、条件样式、聚合计算等 Thymeleaf 核心语法,是典型的业务页面开发实践。