1. なぜThymeleaf(テンプレートエンジン)が必要なのか?
近年のSpring BootアプリケーションではThymeleafが用いられることが多いです。
Thymeleafを使うと、HTMLファイルの中に式(EL的なもの)や制御構文を書くことで、Javaのオブジェクトの値を動的に埋め込んだり、条件分岐や繰り返し表示ができます。
なぜThymeleaf?
- デザイナーとの協業がしやすい:HTMLファイルをそのままブラウザでプレビューしやすい
- Spring Bootとの相性が良い
- JSTLやServletスクリプトレットを使わずにテンプレートを作成できる
2. Thymeleafの基本(式や変数展開)
ThymeleafではHTMLタグ属性に対してth:*
を使います。
2.1. 変数展開の基本
<p th:text="${msg}">ここにメッセージが入ります</p>
${msg}
→ コントローラからmodel.addAttribute("msg", "Hello")
された文字列などを埋め込み
Controller側(Spring Boot例)
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class Main {
@GetMapping("/sample")
public String sample(Model model) {
model.addAttribute("msg", "Hello World");
model.addAttribute("age", 20);
return "sample"; // sample.html
}
}
sample.html(抜粋)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"></head>
<body>
<p th:text="${msg}">ここにメッセージ</p>
</body>
</html>
これで /sample
にアクセスすると <p>Hello World</p>
が表示されます。
"ここにメッセージ" は、通常 表示されない 文字列です。なぜなら、Thymeleafの th:text
属性は、指定された変数 ${msg}
の値でタグの内容を置き換えるからです。つまり、msg
に値がある場合は、<p>
の中身が msg
の値に置き換えられ、元の "ここにメッセージ"
は消えてしまいます。
3. Thymeleafでの演算子・条件分岐・繰り返し
3.1. 演算子
Thymeleafの式内では、演算子が使えます。
- 算術演算子:
+ - * / %
- 比較演算子:
== != < > <= >=
- 論理演算子:
and
,or
,!
(あるいは&&
,||
も使えます)
例)演算子
<p th:text="${1+2}"></p> <!-- 3 -->
<p th:text="${5/2}"></p> <!-- 2 -->
<p th:text="${(1 == 1) and (2 == 3)}"></p> <!-- false -->
<p th:text="${(age >= 20) ? '成人' : '未成年'}"></p> <!-- 三項演算子 -->
3.2. 条件分岐
3.2.1. th:if
, th:unless
Thymeleafで単純なif/else
を実現するには、th:if
と th:unless
を使うのが最も簡単です。
<p th:if="${age >= 20}">お酒が飲めます</p>
<p th:unless="${age >= 20}">まだ飲めません</p>
th:if="${条件式}"
→ 条件式がtrue
ならタグを表示、false
なら非表示th:unless="${条件式}"
→th:if
の逆で、条件式がfalse
なら表示、true
なら非表示
3.2.2. th:switch
/ th:case
JSPの <c:choose>
に近いことをするには、Thymeleafの th:switch
/ th:case
を使えます。
<div th:switch="${bloodType}">
<p th:case="'A'">慎重な性格</p>
<p th:case="'B'">大胆な性格</p>
<p th:case="'O'">おおらかな性格</p>
<p th:case="*">不思議な性格</p> <!-- default相当 -->
</div>
th:switch="${変数}"
th:case="'A'"
→A
に一致する場合のみ表示th:case="*"
→ マッチしない場合(デフォルト)
3.3. 繰り返し
JSTLの <c:forEach>
に相当するのは、th:each
属性です。
<ul>
<li th:each="name : ${names}" th:text="${name}">名前</li>
</ul>
${names}
は、Controller側でList<String>
などをmodel.addAttribute("names", namesList)
しておく。name : ${names}
→ 変数name
にリストの要素が順番に入るth:text="${name}"
→ 文字列を表示
例)オブジェクトのリストをテーブル表示
<table>
<thead>
<tr><th>ID</th><th>名前</th><th>価格</th></tr>
</thead>
<tbody>
<tr th:each="car : ${cars}">
<td th:text="${car.id}"></td>
<td th:text="${car.name}"></td>
<td th:text="${car.price}"></td>
</tr>
</tbody>
</table>
4. ThymeleafとJavaBeans(プロパティアクセス)
4.1. JavaBeansのgetter/setterを使う
Thymeleafで ${bean.field}
と書くと、getField()
メソッドを呼び出します。
public class CustomerBean implements Serializable {
private int customerId;
private String name;
// getter/setter ...
}
<p th:text="${customer.customerId}"></p>
<p th:text="${customer.name}"></p>
これで getCustomerId()
や getName()
が呼ばれます。
nullの場合は何も表示されません(th:text
は空文字となる)。
5. 式を記述できる場所
ThymeleafではHTMLタグの属性に対して th:text
, th:if
, th:class
, th:value
など様々な形で式を入れられます。
また、単純に文字列としてタグ本文に${}
を書く場合は、[[${}]]
や [(...)]
のInline Expressions
を使う方法があります(ただしth:inline="text"
などの設定が必要)。
例)CSSに動的値を埋め込みたい場合
<body th:style="'background-color:' + ${color}">
...
</body>
例)JavaScriptに文字列を埋め込む
<script th:inline="javascript">
let userName = [[${name}]];
alert("こんにちは、" + userName + "さん");
</script>
th:inline="javascript"
と書くと[[]]
内でThymeleaf式を展開し、JavaScript文字列として安全にエスケープしてくれます。
6. 文字列のフォーマット(数値、日付など)
Thymeleafでは#numbers
や #temporals
(Spring ELのユーティリティオブジェクト)などを使って書式制御ができます。
6.1. 数値の書式
<p th:text="${#numbers.formatDecimal(price, 0, 0)}"></p>
#numbers.formatDecimal(元値, 小数最小桁, 小数最大桁)
- ここでは
0,0
→ 整数表示し、3桁区切りはLocale設定による #numbers.formatCurrency(value)
などもありますが、ロケールや通貨設定が必要です。
例)カンマ付きで円マークを付けたい場合
Thymeleaf単体で円マークを付ける簡易例:
<p th:text="'¥' + #numbers.formatDecimal(price, 0, 0)"></p>
- 先頭に
'¥' +
を付加し、#numbers.formatDecimal()
でカンマ区切り。 - Javaでより細かい制御をしたい場合は、
DecimalFormat
を使ってフォーマット済み文字列を渡す方法もあります。
6.2. 日付の書式
<p th:text="${#temporals.format(date, 'yyyy年MM月dd日(E)')}"></p>
#temporals.format(元のDate/LocalDate, "パターン")
yyyy
(年),MM
(月),dd
(日),E
(曜日) などはJSTLと同様
例)Controllerで渡す
@GetMapping("/dateSample")
public String dateSample(Model model) {
model.addAttribute("today", LocalDate.now());
return "dateSample";
}
dateSample.html
<p th:text="${#temporals.format(today, 'yyyy年MM月dd日(E)')}"></p>
まとめ
- JSP + JSTLのELやタグ機能は、Thymeleafでは
th:*
属性とSpELを使って実現 - 条件分岐:
th:if
,th:unless
,th:switch
/th:case
- 繰り返し:
th:each
(JSTL<c:forEach>
相当) - JavaBeansプロパティ:
${bean.field}
→getField()
呼び出し - 数値や日付のフォーマット:
#numbers
,#temporals
などを利用 - 式の書ける場所: HTML属性や
th:inline
したスクリプト内など様々 - JSPでの
<%@ taglib prefix="c" ...>
や<c:if>
,<c:choose>
,<fmt:formatNumber>
といった構文はThymeleafでは不要。かわりにth:*
属性やユーティリティオブジェクト#numbers
,#temporals
を使う。
このように、ThymeleafはHTMLテンプレートをベースに動的なデータを埋め込む仕組みが整っています。
参考:JSTLのコードをThymeleafに書き換える対応表イメージ
機能 | Thymeleaf (HTML) |
---|---|
変数表示 (EL) | th:text="${var}" または [[${var}]] |
if文 | <p th:if="${cond}"> … </p> |
unless(逆条件) | <p th:unless="${cond}"> … </p> |
choose/switch | <div th:switch="${var}"><p th:case="'val'">…</p> |
forEach(繰り返し) | <tr th:each="x : ${list}">…</tr> |
formatNumber(数値) | th:text="${#numbers.formatDecimal(val,0,0)}" |
formatDate(日付) | th:text="${#temporals.format(val, 'yyyy/MM/dd')}"/> |
Thymeleafの方がHTML側で可読性高く、一連の処理を属性ベースで記述できるメリットがあります。
今後のステップ
- Spring Bootでアプリケーションを作成し、ControllerクラスでModelへデータをセット
- Thymeleafテンプレートにて
th:if
,th:each
などを駆使して表示制御 - JavaBeansクラスを使い、プロパティ(getter/setter)を通じてテンプレートから値を参照
- 数値や日付、通貨などの表示フォーマットも
#numbers
,#temporals
でコントロール
これにより、JDBCを使って取得したデータベースの内容をBeansに入れ、th:each
で一覧表示する、といった処理まで完成できます。
以上が、「Spring Boot + Thymeleaf」です。