読者です 読者をやめる 読者になる 読者になる

M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

テンプレート・エンジンThymeleafのチュートリアルを読む(3)

f:id:m12i:20141101162746p:plain

前回に引き続きテンプレート・エンジンThymeleafのチュートリアルの訳出です。今回はイテレーション(繰り返し処理)について。

原典は"Tutorial: Using Thymeleaf"の第6章です(2014/11/1 取得)。

          * * *

6 イテレーション

ホームページをつくるという話からはじまって、プロフィール・ページ、そしてユーザにニュースレターの購読してもらうページの話まで来ました。ところで私たちの製品については? 私たちの売り物が何か、訪問者に知ってもらうためには製品リストをつくらなくてはいけないのでは? 答えは言うまでもなくYes。それではとりかかりましょう。

6.1 イテレーションの基本

/WEB-INF/templates/product/list.htmlの中で私たちの製品を一覧するため、テーブルを用意しましょう。各製品はテーブルの1行(<tr>要素)を使って表示します。テンプレート・ファイルには、〔訳注:実際にテーブルの行をレンダリングするための〕ひな形──私たちの製品をどのように表示するかを例示する──となる行をつくらなくてはなりません。そしてThymeleafに対して、そのひな形を個別の製品ごとに繰り返し使用するよう指示します。

Thymeleaf標準語ではこの目的のために th:each を用意しています。

th:eachを使う

製品リスト・ページのために、サービス・レイヤから製品のリストを取得して、テンプレートのコンテキストに追加するコントローラが必要になります:

public void process(
        HttpServletRequest request, HttpServletResponse response,
        ServletContext servletContext, TemplateEngine templateEngine) {

    ProductService productService = new ProductService();
    List<Product> allProducts = productService.findAll(); 

    WebContext ctx = new WebContext(request, servletContext, request.getLocale());
    ctx.setVariable("prods", allProducts);

    templateEngine.process("product/list", ctx, response.getWriter());
}

続いて、製品リストのイテレーション〔繰り返し処理〕のため、テンプレート内で th:each を使用します:

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

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

上記コードのなかに登場する prod : ${prods} という属性値は “${prods}の評価結果の要素ごとに、prodと呼ばれる変数に設定した上で、このテンプレート断片の処理を繰り返せ”という意味です。ここで登場する概念に名前を付けておきましょう:

Noteイテレーション変数prod<tr>要素(および<td>のような内包要素)の内側においてのみ有効です。

イテレーション対象にできる値

Thymeleaf のテンプレートでは、 java.util.List 以外のオブジェクトもイテレーションに使用できます。 th:each 属性によりイテレーション対象〔iterable〕とみなされるオブジェクトの完全な一覧をお見せしましょう:

  • java.util.Iterableを実装したオブジェクト。
  • java.util.Mapを実装したオブジェクト。マップをイテレーションした場合、イテレーション変数にはjava.util.Map.Entryクラスのオブジェクトが設定される。
  • あらゆる配列。
  • その他あらゆるオブジェクト。これらは要素を1つしか持たないリストとして取り扱われます。

6.2 イテレーション中の状態を保持する

th:each を使用している時、Thymeleafテンプレート・エンジンはイテレーションの状態を保持するためのメカニズムを提供します:ステータス変数です。

ステータス変数は th:each 属性の中で定義され、次のようなデータを〔プロパティとして〕保持します:

  • 現在のイテレーション・インデックス〔添字〕。0始まり。indexプロパティ。
  • 現在のイテレーション・インデックス。1始まり。countプロパティ。
  • イテレーション対象の変数が保持している要素の総数。 sizeプロパティ。
  • 各回のイテレーション変数。currentプロパティ。
  • 現在の回が偶数回なのか奇数回なのかを示すフラグ。真偽値のeven/oddプロパティ。
  • 現在の回が初回であることを示すフラグ。真偽値の first プロパティ。
  • 現在の回が最終回であることを示すフラグ。真偽値の last プロパティ。

先ほどの例のなかでこれらを使ってみましょう:

<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 属性の中でイテレーション変数あとにカンマで区切って名前を指定することで定義できます。イテレーション変数と同様、ステータス変数もまた th:each 属性を持つタグにより定義されたコード断片の中でのみ有効です。

このテンプレートの処理結果を見てみましょう:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

  <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 colspan="1" rowspan="1">NAME</th>
        <th colspan="1" rowspan="1">PRICE</th>
        <th colspan="1" rowspan="1">IN STOCK</th>
      </tr>
      <tr>
        <td colspan="1" rowspan="1">Fresh Sweet Basil</td>
        <td colspan="1" rowspan="1">4.99</td>
        <td colspan="1" rowspan="1">yes</td>
      </tr>
      <tr class="odd">
        <td colspan="1" rowspan="1">Italian Tomato</td>
        <td colspan="1" rowspan="1">1.25</td>
        <td colspan="1" rowspan="1">no</td>
      </tr>
      <tr>
        <td colspan="1" rowspan="1">Yellow Bell Pepper</td>
        <td colspan="1" rowspan="1">2.50</td>
        <td colspan="1" rowspan="1">yes</td>
      </tr>
      <tr class="odd">
        <td colspan="1" rowspan="1">Old Cheddar</td>
        <td colspan="1" rowspan="1">18.75</td>
        <td colspan="1" rowspan="1">yes</td>
      </tr>
    </table>
  
    <p>
      <a href="/gtvg/" shape="rect">Return to home</a>
    </p>

  </body>
  
</html>

Noteイテレーション変数のおかげで、上記コードの中のテーブルの奇数行にはoddCSSクラスが設定されています(行数のカウントは0始まりです)。


<td>タグのcolspan属性とrowspan属性は、<a>タグにおけるshape属性と同じく、Thymeleafによって自動的に追加されています。これはXHTML 1.0 Strict標準のDTDに準拠するためのもので、それぞれの属性にはデフォルト値が設定されます(私たちが作成したテンプレートではそれらの値を設定していなかったことを思い出してください)。これらについて心配はご無用です。ページの表示内容には影響がないからです。一例として、HTML5DTDを持ちません)を使っている場合、これらの属性は決して追加されません。

ステータス変数〔訳注:原文では「イテレーション変数」とあるがこれはおそらく間違い〕を明示的に宣言しなかった場合、イテレーション変数の名称の末尾に 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>


          * * *

原典は"Tutorial: Using Thymeleaf"の第5章です(2014/11/1 取得)。


テンプレート・エンジンThymeleafのチュートリアルを読む(1) - M12i.


テンプレート・エンジンThymeleafのチュートリアルを読む(2) - M12i.


Spring関連の訳出記事まとめ - M12i.