Thymeleaf の Document 読みながら Memo

Thymeleaf たいむりーふ のメモを書こっと
JAX-RS で動かすなら jersey-thymeleaf using ViewProcessor - Mitsuyuki.Shiiba が参考になるかも。

公式ドキュメントの
http://www.thymeleaf.org/doc/Tutorial%20-%20Using%20Thymeleaf%2020130224.pdf
を読みながら。好きなところだけピックアップや。

What?

Thymeleaf はJavaのテンプレートエンジンです。
HTMLを壊さないように書けるので静的にも見れて、動的にも見れて。
デザイナーさんと一緒にやるときに(゚д゚)ウマーな感じにできそうな感じです。

How?

"th" の namespace を持った属性をタグに書きます。

<p>Today is: <span th:text="${today}">13 february 2011</span></p>

静的に見たら、th: の属性は無視されて。"13 february 2011"が見えるす。
動的に見たら、"today"という変数に設定された値で span タグの中身が置換されて表示されます。こんな感じ。

<p>Today is: <span>todayに設定されてる値</span></p>

なので。エンジニアがごにょごにょしてても。
デザイナーさんが静的に見て確認できるです。(∩´∀`)∩ワーイ

変数へのアクセス

OGNLで解析するっぽいです。
なので

user.name

とかで user の name 属性にアクセスできます。
自分は public フィールド好きなので getter 書いてやったことありませんが、たぶんどっちでも user.name でアクセスできるんだと思います。

user.name にシイバと入れておくと。

<p class="abcde" th:text="${user.name}">サンプル太郎</p>

てすると、サンプル太郎じゃなくて、シイバと表示されるす。(∩´∀`)∩ワーイ

色々できるよ

<p th:text="'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))">xxxx</p>

文字列を書いたり、文字列をつなげたり、メソッド呼び出したり、三項演算子使ったりもできます。

文字列連結

+ を使います

th:text="'The name of the user is ' + ${user.name}"

そういえばサニタイズ

th:text はサニタイズしてくれます。
タグも使いたいときは th:utext を使います。

計算

こんなふうなんも可能です。

<span th:text="2011 + 2">1494</span>

+, -, *, /, % が使えるす。

比較

>, <, >, <=, =, != が使えるす。
でもXMLでは < と > がそのまま書けないので &lt; と &gt; にしないといかんです。

th:if="${prodStat.count} &gt; 1”
th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')”

オペレーターの文字列エイリアスもあるす。
gt (>), lt (<), ge (>=), le (<=), eq(==), neq (!=)

三項演算子

こんなんできる。

<tr th:class="${row.even}? 'even' : 'odd'">
...
</tr>

こんなんもできる。

<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">
...
</tr>

省略もできる。

<tr th:class="${row.even}? 'alt'">
...
</tr>

エルビスもいける

<p>Age: <span th:text="${age}?: '(no age specified)'">27</span>.</p>

好きな属性を書き換える

th:attr で好きな属性を書き換え可能。

<form action="subscribe.html" th:attr="action=@{/subscribe}">
  <fieldset>
    <input type="text" name="email" />
    <input type="submit" value="Subscribe me!" th:attr="value=# {subscribe.submit}"/>
  </fieldset>
</form>

@{...}とか#{...}とか*{...}って書き方もあるんだけど
それは今日は説明するつもりないから公式サイトのドキュメントを読むとよいです。

これを動的に動かしたときは、属性は変数の値で置換されてるす。

<form action="/gtvg/subscribe">
  <fieldset>
    <input type="text" name="email" />
    <input type="submit" value="¡Suscríbeme!"/>
  </fieldset>
</form>

直接属性を指定する

th:attr 使うとどんな属性も置換できるけど。
エイリアス的なのがあるす。

例えばこんな感じです。

  • th:action, th:class, th:type, ...

属性を置換するんじゃなくて付け足す

置換じゃなくて、動的に追加したいって場合がありますよね。class属性とか。
そんなときに th:attrappend(後ろに追加) と th:attrprepend(前に追加) を使うす。

これもエイリアスみたいなのがあって th:classappend とか使えるす。

<tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'">

繰り返し処理

繰り返しは th:each 使って for-each 的なのやります。

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

これで商品の数だけ行が追加されるす。
それと、Thymeleafが持ってるループ用の変数を使うならループ用の変数の次に書けば受け取れます。

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

この iterStat ですね。

  • 0 から始まる index
  • 1 から始まる count
  • 素数 size
  • 今偶数番目か奇数番目かは even, odd

明示的に定義してなかったら、ループ用変数の後ろに "Stat" をつけたものが暗黙的に定義されるみたい。

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

for ループは?

Thymeleafでfor文のようなシンプルなループを書く #Thymeleaf - コードつれづれ

なるほどー

<select name="foo">
  <option th:each="i : ${#numbers.sequence(1, 10)}" th:value="${i}" th:text="${i}+ '個'">1個</option>
</select>

条件を満たす場合だけ表示する

そんなときは th:if
th:if の値が true の場合はそのタグが表示されて false の場合は表示されないです。

<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>

boolean 型の true 以外でも true になる。

  • number, character型で 0 じゃない場合
  • 文字列型で "false", "off", "no"じゃない場合
  • boolean でも number でも character でも String でもない場合

th:if の値が null のときは false 扱いです。

th:if の反対

は、th:unlessです。自分はあんまり好きじゃないけど。th:ifで全部書きたい。

switch 文

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

最後の th:case="*" は default ですね。

テンプレートのインクルード

ヘッダーとかフッターとか共通部品とかは別のファイルに切り出したいよね。
そんなとき、やり方が2種類あるす。

th:include と th:substituteby。

自分は substituteby のほうが好きかな。他の属性もついてきてくれるから。

削除

デザイナーさんが書いてくれてて、静的に見る場合はあった方がいいけど
動的に見る場合は消さないといけない部分とか。Thymeleafに削除してもらえるす。

th:remove で。

例えばサンプル行を消したいときは

<tr th:remove="all">
  <td>Mild Cinnamon</td>
  <td>1.99</td>
  <td>yes</td>
  <td>
    <span>3</span> comment/s
    <a href="comments.html">view</a>
  </td>
</tr>

こうしとくとこの tr タグ自体が出なくなるす。

面白いのは th:remove="all-but-first"。
最初の行に変数とかを当て込んで、それを th:each で回すから
2行目以降はいらないや。ってときに。これを使うす。
すると、1行目だけは残って、2行目以降は消えます。

<table>
  <thead>
    <tr>
      <th>NAME</th>
      <th>PRICE</th>
      <th>IN STOCK</th>
      <th>COMMENTS</th>
    </tr>
  </thead>
  <tbody th:remove="all-but-first">
    <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>
      <td>
        <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
        <a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
      </td>
    </tr>
    <tr class="odd">
      <td>Blue Lettuce</td>
      <td>9.55</td>
      <td>no</td>
      <td>
        <span>0</span> comment/s
      </td>
    </tr>
    <tr>
      <td>Mild Cinnamon</td>
      <td>1.99</td>
      <td>yes</td>
      <td>
        <span>3</span> comment/s
        <a href="comments.html">view</a>
      </td>
    </tr>
  </tbody>
</table>

ユーティリティ

  • #dates, #calendars: 日付関連のユーティリティ。フォーマットしたり。とか色々便利。
  • #numbers: 数字関係。3桁カンマ入れたりとかも便利ね。
  • #strings: 文字列。これも便利すねー。

他にも、#objects, #bools, #arrays, #lists, #sets, #maps とか色々あります。

おしまいー

シンプルでよいすなー

他にも、JavascriptDartの対応、インライン展開機能、th:with、th:object、ローカル変数機能とかあるす。面白い。