开发学院

您的位置:首页>教程>正文

教程正文

Thymeleaf 3.0教程:6 循环

  到目前为止,我们已经创建了一个主页,一个用户个人资料页,还有一个让用户订阅我们时事通讯的页面,但是我们的产品呢?为此,我们需要一种方法来迭代集合中的项目,以构建我们的产品页面。

6.1 迭代基础

  要在我们的/WEB-INF/templates/product/list.html页面中显示产品,我们需要使用一个表格。我们的每个产品都将显示在一行中(一个<tr>元素),因此对于我们的模板,我们需要创建一个模板行,一个将举例说明我们希望如何显示每个产品的模板行—然后指示Thymeleaf为每个产品重复一次。

  标准方言为我们提供了一个属性:th:each。

使用th:each

  对于我们的产品列表页面,我们需要一个控制器方法,从服务层检索产品列表并将其添加到模板上下文中:

public void process(
        final HttpServletRequest request, final HttpServletResponse response,
        final ServletContext servletContext, final ITemplateEngine templateEngine)
        throws Exception {
    ProductService productService = new ProductService();
    List<Product> allProducts = productService.findAll(); 
    WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());
    ctx.setVariable("prods", allProducts);
    templateEngine.process("product/list", ctx, response.getWriter());
}

  然后我们将在模板中使用th:each迭代产品列表:

<!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>

    <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>

</html>

  该产品:${prods}属性值的意思是“对于评估${prods}结果中的每个元素,使用名为prod的变量中的当前元素重复该模板片段”。让我们给我们看到的每样东西起个名字:

  我们将调用${prods}迭代表达式或迭代变量。

  我们将调用prod迭代变量或简单的iter变量。

  请注意,产品变量的范围是<tr>元素,这意味着它对<td>这样的内部标签可用。

可迭代值

  java.util.List类不是Thymeleaf中唯一可用于迭代的值。有一套相当完整的对象被每个属性认为是可迭代的;

  实现java.util.Iterable的对象

  实现java.util.Enumeration的任何对象。

  实现java.util.Iterator的任何对象,其值将在迭代器返回时使用,而不需要在内存中缓存所有值。

  实现java.util.Map的任何对象。迭代映射时,iter变量属于java.util.Map.Entry类。

  任何数组。

  任何其他对象都将被视为包含该对象本身的单值列表。

6.2 保持迭代状态

  当使用th:each时,Thymeleaf提供了一种用于跟踪迭代状态的机制:状态变量。

  状态变量在每个属性中定义;并且包含以下数据:

  当前迭代索引,从0开始。这是index属性。

  当前迭代索引,从1开始。这是count属性。

  迭代变量中元素的总数。这是size属性。

  每次迭代的iter变量。这是当前属性。

  当前迭代是偶数还是奇数。这些是偶数/奇数布尔属性。

  当前迭代是否是第一次迭代。这是第一个布尔属性。

  当前迭代是否是最后一次迭代。这是最后一个布尔属性。

  让我们看看如何在前面的例子中使用它:

<table>
  <tr>
    <th>NAME</th>
    <th>PRICE</th>
    <th>IN STOCK</th>
  </tr>
  <tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
    <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>

  状态变量(本例中为iterStat)在th:each属性中定义,方法是在iter变量之后写入其名称,并用逗号分隔。就像iter变量一样,状态变量的作用域也是由持有th:each属性的标签定义的代码片段。

  让我们看看处理模板的结果:

<!DOCTYPE html>
<html>
  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
  </head>

  <body>

    <h1>Product list</h1>
  
    <table>
      <tr>
        <th>NAME</th>
        <th>PRICE</th>
        <th>IN STOCK</th>
      </tr>
      <tr class="odd">
        <td>Fresh Sweet Basil</td>
        <td>4.99</td>
        <td>yes</td>
      </tr>
      <tr>
        <td>Italian Tomato</td>
        <td>1.25</td>
        <td>no</td>
      </tr>
      <tr class="odd">
        <td>Yellow Bell Pepper</td>
        <td>2.50</td>
        <td>yes</td>
      </tr>
      <tr>
        <td>Old Cheddar</td>
        <td>18.75</td>
        <td>yes</td>
      </tr>
    </table>
  
    <p>
      <a href="/gtvg/" shape="rect">Return to home</a>
    </p>

  </body>
  
</html>

  请注意,我们的迭代状态变量工作得非常好,只为奇数行建立了奇数CSS类。

  如果您没有显式设置状态变量,Thymeleaf将始终为您创建一个状态变量,方法是在迭代变量的名称后面加上Stat:

<table>
  <tr>
    <th>NAME</th>
    <th>PRICE</th>
    <th>IN STOCK</th>
  </tr>
  <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
    <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>

6.3通过延迟检索数据进行优化

  有时,我们可能希望优化数据集合的检索(例如,从数据库中),以便只有当这些集合确实要被使用时才被检索。

  实际上,这是可以应用于任何数据的东西,但是给定内存中集合的大小,检索要迭代的集合是这种情况下最常见的情况。

  为了支持这一点,Thymeleaf提供了一种延迟加载上下文变量的机制。实现ILazyContextVariable接口的上下文变量——很可能是通过扩展其LazyContextVariable默认实现,将在执行时得到解决。例如:

context.setVariable(
     "users",
     new LazyContextVariable<List<User>>() {
         @Override
         protected List<User> loadValue() {
             return databaseRepository.findAllUsers();
         }
     });

  该变量可以在lazyness的情况下在代码中使用,例如:

<ul>
  <li th:each="u : ${users}" th:text="${u.name}">user name</li>
</ul>

  但同时,如果条件在代码中评估为false,则永远不会初始化(其loadValue()方法永远不会被调用),如:

<ul th:if="${condition}">
  <li th:each="u : ${users}" th:text="${u.name}">user name</li>
</ul>