Thymeleaf th:withでローカル変数を定義する方法を初心者向けに解説!テンプレート内での使い方をマスターしよう
新人
「Thymeleafのテンプレートで、値を一時的に変数として使いたいときってどうすればいいですか?」
先輩
「それならth:with属性を使うと、ローカル変数を定義してテンプレート内で使い回せるよ。」
新人
「ローカル変数?それってどうやって使うんですか?」
先輩
「じゃあ、まずThymeleafの基本とth:withの使い方を順番に見ていこう!」
1. Thymeleafとは?テンプレートエンジンの概要
Thymeleaf(タイムリーフ)は、HTMLテンプレートにJavaオブジェクトを埋め込んで動的に表示を生成できるテンプレートエンジンです。Spring Frameworkと非常に相性が良く、@Controllerと連携してHTMLページを表示するのに最適です。HTMLファイルを直接ブラウザで開いても正しく表示される「ナチュラルテンプレート」構文を採用しているのも特徴です。
初心者がWebアプリケーションを作成する際には、pleiades環境でSpringプロジェクトを作成し、Gradleで依存関係を管理するのが一般的です。その際、Thymeleafを使うことで、コントローラで処理したデータをテンプレートに簡単に反映できます。
Thymeleafのテンプレート内では、属性形式で書かれるth:textやth:if、そして今回解説するth:withなど、多くの機能が用意されています。
2. th:withの基本構文と使いどころ
th:withは、Thymeleafテンプレートの中で一時的なローカル変数を定義するための属性です。繰り返し使いたい式を変数として定義することで、テンプレートの可読性とメンテナンス性を高めることができます。
構文はとてもシンプルで、以下のように使用します:
<div th:with="message='こんにちは、' + ${user.name}">
<p th:text="${message}">ここにメッセージが表示されます</p>
</div>
この例では、th:withでmessageというローカル変数を定義しています。この変数には「こんにちは、」とユーザー名が結合された文字列が格納され、th:textで表示されます。
このように、複数回使うような複雑な式や、テンプレート内で加工が必要な値は、th:withを使って先に変数にしておくと便利です。
複数のローカル変数を同時に定義する
th:withではカンマ区切りで複数のローカル変数を同時に定義することも可能です:
<div th:with="firstName=${user.firstName}, lastName=${user.lastName}">
<p th:text="'氏名:' + ${lastName} + ' ' + ${firstName}"></p>
</div>
このように書くことで、${user.firstName}や${user.lastName}を毎回書かずに済むため、テンプレートがスッキリします。
繰り返し処理の中で使うth:with
th:eachと組み合わせて、繰り返し処理の中で条件によってローカル変数を切り替えるような使い方も可能です。
<ul>
<li th:each="item : ${itemList}" th:with="highlight=${item.count > 10}">
<span th:if="${highlight}" style="color:red" th:text="${item.name}"></span>
<span th:unless="${highlight}" th:text="${item.name}"></span>
</li>
</ul>
この例では、item.countが10を超える場合にだけ赤文字で表示するようにしています。条件を一度ローカル変数に代入してから使うことで、コードの意図が明確になります。
Springの@Controllerとの連携例
実際に、Springの@Controllerから値を渡し、テンプレート内でth:withを使ってローカル変数を定義する例を見てみましょう。
@Controller
public class GreetingController {
@GetMapping("/greeting")
public String greeting(Model model) {
User user = new User("山田", "太郎");
model.addAttribute("user", user);
return "greeting";
}
}
この@Controllerから渡されたuserオブジェクトを、テンプレート側でth:withを使って加工します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>挨拶ページ</title>
</head>
<body>
<div th:with="fullName=${user.lastName} + ' ' + ${user.firstName}">
<p th:text="'ようこそ、' + ${fullName} + 'さん!'"></p>
</div>
</body>
</html>
このように、Thymeleafのth:withを活用することで、テンプレート内の式を簡潔に整理し、読みやすく保つことができます。
注意点:ローカル変数のスコープ
th:withで定義したローカル変数は、そのタグ内(スコープ)でのみ有効です。外側に出ると使えなくなるため、使用箇所に応じて適切に範囲を設定しましょう。
また、同じ変数名を内側で再定義した場合は、内側のスコープが優先されます。
3. th:withを使った繰り返し処理内での変数定義
Thymeleafでは、繰り返し処理の中でもth:withを活用することで、各ループごとのローカル変数を柔軟に定義できます。特に、ループ内で複雑な条件分岐を行いたいときや、あらかじめ加工済みのデータを準備しておきたい場合に便利です。
以下は、各商品の金額に応じて「高額」かどうかを判定するローカル変数isExpensiveを定義し、スタイルを変える例です。
<table>
<tr>
<th>商品名</th>
<th>価格</th>
</tr>
<tr th:each="item : ${itemList}" th:with="isExpensive=${item.price > 5000}">
<td th:text="${item.name}"></td>
<td th:classappend="${isExpensive} ? 'expensive' : ''" th:text="${item.price}"></td>
</tr>
</table>
このようにth:eachとth:withを組み合わせることで、テンプレート内での処理を簡潔に保ちながら、複雑な判定処理も読みやすく記述できます。
4. th:withのスコープと注意点(ネストや再定義時の動作)
th:withで定義したローカル変数にはスコープ(有効範囲)があります。これはHTMLタグの階層構造に基づいており、定義されたタグの内部のみで有効です。外側のスコープで定義した変数は、内側で再定義することも可能ですが、その場合は内側の値が優先されます。
以下に、変数のスコープと再定義の例を示します。
<div th:with="message='外側のメッセージ'">
<p th:text="${message}"></p>
<div th:with="message='内側のメッセージ'">
<p th:text="${message}"></p>
</div>
<p th:text="${message}"></p>
</div>
このテンプレートでは、1行目と3行目のmessageは「外側のメッセージ」、中央のdiv内だけは「内側のメッセージ」が出力されます。つまり、スコープごとに変数は管理されており、再定義は可能ですが、それが有効なのはそのタグの内部だけです。
初心者がよくつまずくのが、この「スコープによる上書きの影響範囲」です。予期せぬ挙動になった場合は、定義しているth:withがどのタグにあるかを必ず確認しましょう。
5. th:withで複数の変数を同時に定義する方法
Thymeleafのth:with属性では、カンマ区切りで複数のローカル変数を一度に定義できます。これはテンプレート内での処理を効率化するうえで非常に有効です。
以下に、フルネームと敬称を定義し、それらをテンプレート内で利用する例を示します。
<div th:with="fullName=${user.lastName} + ${user.firstName}, suffix='さん'">
<p th:text="'ようこそ、' + ${fullName} + ${suffix}"></p>
</div>
このように、複数の変数を一度に宣言することで、テンプレートの可読性が格段に向上します。また、同一スコープ内で定義された変数同士は参照可能なので、組み合わせて値を構築することも可能です。
実例:@Controllerとの連携で複数変数を使う
以下は、Springの@Controllerでユーザー情報を渡し、テンプレート内でth:withを使って複数の変数を定義・表示する完全な例です。
@Controller
public class UserController {
@GetMapping("/profile")
public String profile(Model model) {
User user = new User("佐藤", "花子", "東京");
model.addAttribute("user", user);
return "profile";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>プロフィール</title>
</head>
<body>
<div th:with="fullName=${user.lastName} + ' ' + ${user.firstName}, location=${user.address}">
<p th:text="'氏名:' + ${fullName}"></p>
<p th:text="'住所:' + ${location}"></p>
</div>
</body>
</html>
このテンプレートでは、フルネームと住所をローカル変数として定義しているため、複数箇所で同じ式を繰り返す必要がありません。これは、Thymeleafを用いたテンプレート設計において重要なベストプラクティスのひとつです。
変数の依存関係に注意
複数の変数を定義する際には、定義の順番にも注意が必要です。以下のように、後から定義した変数が前の変数を使っている場合、順番を逆にするとエラーの原因になります。
<!-- 正しい例 -->
<div th:with="base='こんにちは', message=${base} + ' 太郎さん'">
<p th:text="${message}"></p>
</div>
<!-- 間違った例(baseが未定義のためエラーになる) -->
<div th:with="message=${base} + ' 太郎さん', base='こんにちは'">
<p th:text="${message}"></p>
</div>
このように、th:withで定義する複数のローカル変数には、上から順に評価されるルールがあります。初心者向けのテンプレートでは、特に変数の依存関係に気を配ることで、予期せぬエラーを防ぐことができます。
6. 条件分岐と組み合わせたth:withの実用例
Thymeleafのth:withは、th:ifやth:unlessといった条件分岐と組み合わせて使うことで、テンプレート設計の自由度が大きく広がります。条件によって出力内容やスタイルを変更したい場面では、事前にth:withでロジックをローカル変数に切り出しておくと、テンプレートが非常に読みやすくなります。
以下は、ログインユーザーが管理者かどうかを判定し、表示内容を変更する例です。
<div th:with="isAdmin=${user.role} == 'ADMIN'">
<p th:if="${isAdmin}">管理者向けメニューを表示中</p>
<p th:unless="${isAdmin}">一般ユーザー向けメニューを表示中</p>
</div>
このように、条件式をあらかじめth:withで変数に格納しておくことで、複雑な条件を直接th:ifなどに書かずに済み、テンプレートの保守性が高まります。
7. th:withを使った複雑なテンプレート設計の簡略化
業務用のテンプレートでは、複数の情報を組み合わせた表示が求められる場面が多くあります。たとえば、金額に税率をかけた税込表示、氏名+敬称の組み合わせ、条件に応じた表示の切り替えなどです。こうしたケースでも、Thymeleafのth:withを使えば、ロジックを整理してテンプレートをすっきりと保つことができます。
以下は、税込価格を計算しつつ、税区分を文字列として表示するテンプレートの例です。
<tr th:each="item : ${items}" th:with="taxRate=${item.taxType} == 'REDUCED' ? 0.08 : 0.10, taxLabel=${item.taxType} == 'REDUCED' ? '軽減税率' : '標準税率', total=${item.price} * (1 + taxRate)">
<td th:text="${item.name}"></td>
<td th:text="${item.price} + '円(' + ${taxLabel} + ')'"></td>
<td th:text="${#numbers.formatDecimal(total, 1, 'POINT', 2, 'ZERO')} + '円'"></td>
</tr>
このようにth:withで税率・税区分・合計金額を変数にしておくことで、繰り返し使う値や複雑な式を簡潔に整理でき、テンプレートの可読性が飛躍的に向上します。
8. 実践例:ダッシュボードや明細画面でのローカル変数活用
ここでは、業務でよく使われるダッシュボードや売上明細画面におけるth:withの活用例を紹介します。集計処理やユーザーごとの表示切り替えなど、実践的なテンプレート設計をする際に、ローカル変数の定義が非常に有効です。
以下は、Springの@Controllerから売上データを渡す例です。
@Controller
public class SalesController {
@GetMapping("/dashboard")
public String showDashboard(Model model) {
List<Sale> salesList = List.of(
new Sale("商品A", 3000, "標準"),
new Sale("商品B", 1000, "軽減"),
new Sale("商品C", 8000, "標準")
);
model.addAttribute("salesList", salesList);
return "dashboard";
}
}
テンプレート側では、税率の計算や表示ラベルをth:withで事前にローカル変数として定義し、整形して表示します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>売上ダッシュボード</title>
</head>
<body>
<h2>売上明細</h2>
<table>
<tr>
<th>商品名</th>
<th>金額</th>
<th>税率</th>
<th>税込金額</th>
</tr>
<tr th:each="sale : ${salesList}"
th:with="tax=${sale.taxType} == '軽減' ? 0.08 : 0.10,
taxText=${sale.taxType} == '軽減' ? '軽減税率' : '標準税率',
total=${sale.amount} * (1 + tax)">
<td th:text="${sale.itemName}"></td>
<td th:text="${sale.amount} + '円'"></td>
<td th:text="${taxText}"></td>
<td th:text="${#numbers.formatDecimal(total, 1, 'POINT', 2, 'ZERO')} + '円'"></td>
</tr>
</table>
</body>
</html>
このように、th:withを使えば、税率の計算や表示形式の変更といったロジックをテンプレート内で管理しやすくなります。テンプレートエンジンとしてThymeleafを使う最大のメリットのひとつは、「デザインとロジックのバランスが取れたテンプレート設計ができる点」です。特にth:withのような機能を上手に使いこなすことで、初心者でも実務レベルのHTMLテンプレートを構築できるようになります。