Thymeleafのfragment引数の渡し方とサンプルコードを初心者向けに解説!
新人
「Webページの共通部分を再利用する方法ってあるんですか?」
先輩
「それなら、Thymeleafのfragment機能を使うと便利だよ。共通部品として定義して、どこからでも呼び出せるんだ。」
新人
「便利そうですね。でも引数とか渡せるんですか?」
先輩
「もちろん。th:fragmentに引数を定義すれば、呼び出し時に値を渡せるんだ。仕組みを一緒に学んでいこう!」
1. Thymeleafとは?
Thymeleaf(タイムリーフ)は、HTMLファイルにJavaの変数や条件分岐を埋め込めるテンプレートエンジンです。Spring Bootと一緒に使われることが多く、サーバーサイドでHTMLを動的に生成したいときに活躍します。
たとえば、ログインしたユーザー名を表示したいときや、リストの内容を繰り返して表示したいときなど、JavaとHTMLを組み合わせて効率的に開発できます。
Thymeleafを使えば、デザイナーとエンジニアが同じHTMLを共有できる点も大きな魅力です。Springアプリケーションと統合しやすく、公式ドキュメントも豊富です。
2. fragment(フラグメント)とは何か?
fragmentとは、Thymeleafで使える共通部品の仕組みです。たとえば、ページのヘッダーやフッター、サイドメニューなど、複数ページで共通する要素をまとめて再利用することができます。
HTMLファイルにth:fragment属性を使って定義し、必要な場所からth:replaceやth:includeで呼び出す形になります。
下記は、共通のヘッダー部分をfragmentとして定義した例です。
<!-- templates/common/header.html -->
<div th:fragment="siteHeader">
<header>
<h1>サイト共通ヘッダー</h1>
</header>
</div>
そして、これを他のHTMLファイルから呼び出すと、同じヘッダーが表示されます。
<!-- templates/index.html -->
<div th:replace="common/header :: siteHeader"></div>
このように、共通レイアウトを一箇所にまとめて管理できるため、保守性が高まり、作業効率も大幅にアップします。
fragmentは単に表示するだけでなく、引数を渡して柔軟に表示内容を変えることも可能です。次のセクションでは、その引数の渡し方について詳しく解説します。
3. fragmentへの引数の基本的な渡し方
Thymeleaf fragmentに引数を渡すには、th:fragmentで引数名を定義し、th:replaceやth:includeで呼び出す際に値を渡します。
例えば、ページタイトルを渡したい場合、以下のようにfragmentを定義します。
<!-- templates/common/title.html -->
<div th:fragment="pageTitle(title)">
<title th:text="${title}">デフォルトタイトル</title>
</div>
このfragmentを呼び出す側では、titleに値を渡して使います。
<!-- templates/index.html -->
<head th:replace="common/title :: pageTitle('ホームページ')"></head>
このようにすることで、ページごとにタイトルを変えながら共通のHTML構造を使うことができます。
呼び出し時にシングルクォーテーションで文字列を囲むことを忘れないようにしましょう。数字や変数なども渡すことができます。
4. th:fragment と th:replace / th:include の使い分け
Thymeleaf fragmentを使って定義した部品は、th:replaceまたはth:includeを使って呼び出します。それぞれの違いは以下の通りです。
th:replace の特徴
th:replaceは、指定した要素全体をfragmentで置き換える処理を行います。タグごとfragmentの中身に差し替わるのが特徴です。
<!-- 呼び出し側 -->
<div th:replace="common/header :: siteHeader"></div>
この場合、上記の<div>自体もfragmentの内容に置き換わります。
th:include の特徴
th:includeは、指定した要素の中にfragmentの内容を含める処理です。親のHTML要素は残り、その中に読み込まれたfragmentが入る形になります。
<!-- 呼び出し側 -->
<div th:include="common/header :: siteHeader"></div>
この違いを理解しておかないと、レイアウト崩れや意図しないHTML構造になることがあるため、使い分けが重要です。
共通レイアウト全体を置き換えたい場合はth:replace、一部だけ埋め込みたい場合はth:includeが適しています。
5. 複数引数の渡し方と注意点
Thymeleaf fragmentでは、複数の引数を渡すことも可能です。たとえば、見出しのテキストと説明文の両方を渡したいとき、次のように記述します。
<!-- templates/common/section.html -->
<section th:fragment="info(title, description)">
<h2 th:text="${title}">見出し</h2>
<p th:text="${description}">説明文</p>
</section>
呼び出す際は、引数をカンマ区切りで指定します。
<!-- 呼び出し側 -->
<div th:replace="common/section :: info('会社概要', '当社は〜')"></div>
注意点として、引数にnullを渡すと、th:textで表示される内容が空になるか、エラーの原因になることがあります。
そのため、th:ifやth:unlessを使ってnullチェックを行うようにすると安全です。
<p th:if="${description}" th:text="${description}"></p>
<p th:unless="${description}">説明がありません</p>
また、Java側の@Controllerでデータを用意する際に、nullにならないよう工夫することも大切です。
@Controller
public class InfoController {
@GetMapping("/about")
public String showAboutPage(Model model) {
model.addAttribute("title", "会社紹介");
model.addAttribute("description", "わたしたちの会社は創業以来...");
return "about";
}
}
このように、Thymeleaf fragmentの引数は、工夫次第で非常に柔軟に使えます。特に複数引数の使い方を覚えると、テンプレートの共通化が進み、保守性や再利用性が高まるので積極的に活用しましょう。
6. 実際のプロジェクトで使えるサンプル構成
Thymeleaf fragmentは、現場のプロジェクトでも非常によく使われます。特に、ヘッダーやフッター、ナビゲーションメニューなど、すべてのページで共通する要素を一元管理できるのは大きな利点です。
たとえば、以下のような構成が一般的です。
<!-- templates/common/header.html -->
<div th:fragment="header(title)">
<header>
<h1 th:text="${title}">デフォルトタイトル</h1>
</header>
</div>
<!-- templates/common/footer.html -->
<div th:fragment="footer">
<footer>
<p>© 2025 サンプル株式会社</p>
</footer>
</div>
これらを呼び出す側では次のように記述します。
<!-- templates/index.html -->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>トップページ</title>
</head>
<body>
<div th:replace="common/header :: header('トップページ')"></div>
<main>
<p>ようこそ、トップページへ!</p>
</main>
<div th:replace="common/footer :: footer"></div>
</body>
</html>
このように、Thymeleaf fragment 引数を活用すれば、動的にタイトルなどを差し込めて、しかも全体の構造は統一されるため、保守性が非常に高まるのです。
7. fragmentの引数が多くなった場合の対処方法
プロジェクトが大きくなってくると、fragmentに渡す引数の数が増えてきます。3つ4つと増えてくると、可読性が悪くなり、どの引数がどの項目なのか分かりにくくなります。
そのような場合は、Mapやオブジェクトを使ってまとめて渡す方法が便利です。
例えば、Java側で以下のようなオブジェクトを定義します。
public class HeaderInfo {
private String title;
private String subtitle;
// getterとsetterは省略
}
そして、コントローラでこのオブジェクトをモデルに渡します。
@Controller
public class PageController {
@GetMapping("/info")
public String showPage(Model model) {
HeaderInfo info = new HeaderInfo();
info.setTitle("お知らせ");
info.setSubtitle("重要なお知らせがあります");
model.addAttribute("headerInfo", info);
return "info";
}
}
HTML側では、th:fragmentでオブジェクト全体を受け取り、プロパティ単位で表示できます。
<!-- templates/common/header.html -->
<div th:fragment="header(info)">
<header>
<h1 th:text="${info.title}">デフォルトタイトル</h1>
<p th:text="${info.subtitle}">デフォルトサブタイトル</p>
</header>
</div>
<!-- templates/info.html -->
<div th:replace="common/header :: header(${headerInfo})"></div>
このようにすれば、fragmentの引数が増えても1つのオブジェクトとして渡すことができ、コードがすっきりします。Mapで渡すことも可能ですが、クラスの方がIDEで補完が効くため、実務ではクラス化が推奨されます。
8. fragmentと引数の使い方でよくあるエラーとその対処法
Thymeleaf fragment 引数を使っていると、初心者がつまずきやすいポイントがいくつかあります。ここでは代表的なエラーとその原因、対処法を解説します。
エラー1:引数の数が一致しない
原因:th:fragmentで定義した引数の数と、th:replaceで渡した引数の数が一致していないと、実行時にエラーになります。
対処法:fragment側の引数リストと呼び出し側の引数を見直して、数と順番を正しく合わせましょう。
エラー2:変数がnullになっている
原因:Java側でモデルに渡すのを忘れている、またはキー名が間違っていると、HTML側でnull扱いになります。
対処法:コントローラのmodel.addAttribute()を再確認し、正しい変数名とオブジェクトを渡しているかチェックしましょう。
model.addAttribute("headerInfo", info); // 名前が違うと取り出せない
エラー3:式の評価に失敗
原因:HTMLテンプレート側で存在しないプロパティを参照している場合、SpEL(Spring Expression Language)の評価に失敗します。
対処法:該当のプロパティがクラスに定義されているか確認し、スペルミスがないかチェックしてください。
<!-- 誤り -->
<h1 th:text="${info.titlee}"></h1>
<!-- 正しい -->
<h1 th:text="${info.title}"></h1>
また、テンプレートの構文エラーがある場合、アプリケーション全体が起動しなくなることもあるため、エラーのログをよく確認しましょう。
こうしたエラーを避けるためにも、開発時にはSpring Boot DevToolsを使って即時リロードを活用したり、段階的に構築して動作確認を行うのがポイントです。