Thymeleafのth:fragmentを使ったテンプレートの再利用方法を完全ガイド!初心者でもわかる使い方
生徒
「Thymeleafのth:fragmentって何ですか?どうやって使うんでしょうか?」
先生
「th:fragmentは、Thymeleafでよく使うテンプレートの一部分を再利用するための機能です。例えば、ヘッダーやフッターを別のHTMLファイルとして分けておき、必要なページに呼び出すことができます。」
生徒
「それなら、共通部分のメンテナンスが楽になりますね!具体的な使い方を教えてください。」
先生
「それでは、基本的なth:fragmentの使い方を見ていきましょう!」
1. th:fragmentとは?
Thymeleafのth:fragmentは、HTMLテンプレートの一部を再利用可能な「断片(フラグメント)」として名前を付けておくための属性です。共通の見出し・ヘッダー・フッター・注意書きなどを一箇所にまとめられるので、同じコードを何度も書く必要がなくなり、更新も一回で全ページへ反映されます。いわば「部品箱」を作るイメージで、サイト全体の保守性と可読性が大きく向上します。
初心者向けに、まずは小さな部品を定義する例を見てみましょう。ここではページ下部に置く短い注意書きをフラグメント化します。実際の呼び出し方法は次の章で扱いますが、「こういう部品を作っておくんだな」と雰囲気だけ掴めればOKです。
<!-- fragments/parts.html -->
<div th:fragment="note">
<small class="text-muted">
※ 本サイトの内容は学習用サンプルです。<br />
不明点があればお問い合わせページをご確認ください。
</small>
</div>
このようにth:fragment="note"と名付けておけば、各ページから同じ注意書きの“部品”を呼び出して使い回せます。将来テキストを変更しても、元ファイル(ここではfragments/parts.html)を1か所直すだけで全ページに反映され、テンプレートの再利用と共通化がスムーズになります。
2. th:fragmentの基本的な使い方
まず、th:fragmentを使ってヘッダーとフッターを分割し、再利用する方法を紹介します。以下の例では、ヘッダーとフッターを別ファイルとして定義し、それをメインのHTMLから呼び出します。
<!-- fragments/header.html -->
<div th:fragment="headerFragment">
<header>
<h1>サイトのヘッダー</h1>
<nav>
<ul>
<li><a href="/">ホーム</a></li>
<li><a href="/about">会社概要</a></li>
<li><a href="/contact">お問い合わせ</a></li>
</ul>
</nav>
</header>
</div>
<!-- fragments/footer.html -->
<div th:fragment="footerFragment">
<footer>
<p>© 2024 My Website</p>
</footer>
</div>
<!-- main.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleafのth:fragment例</title>
</head>
<body>
<!-- ヘッダーを読み込み -->
<div th:insert="fragments/header :: headerFragment"></div>
<h2>メインコンテンツ</h2>
<!-- フッターを読み込み -->
<div th:insert="fragments/footer :: footerFragment"></div>
</body>
</html>
このように、th:insertまたはth:replaceでフラグメントを呼び出すことができます。例えば、ナビゲーションメニューの変更があった場合、header.htmlを修正するだけで、すべてのページに変更が反映されます。
3. パラメータを渡して動的にフラグメントを利用する方法
Thymeleafのth:fragmentでは、パラメータを渡すこともできます。これにより、より動的で柔軟なテンプレートが作成可能です。次の例では、メッセージを渡してフラグメント内で表示します。
<!-- fragments/message.html -->
<div th:fragment="messageFragment (msg)">
<p>メッセージ: [[${msg}]]</p>
</div>
<!-- main.html -->
<div th:insert="fragments/message :: messageFragment (msg='こんにちは、Thymeleaf!')"></div>
上記のコードを実行すると、「メッセージ: こんにちは、Thymeleaf!」と表示されます。このようにパラメータを利用すれば、共通パーツをさらに汎用的に使い回すことができます。
4. th:fragmentを使うときの注意点
th:fragmentを活用する際の注意点をいくつか紹介します。
- ファイルパスが正しくないと、エラーが発生します。特にパスのスペルミスやディレクトリ構成に注意してください。
- テンプレートファイルはUTF-8で保存することを推奨します。文字化けを防ぐためにも重要です。
- 複数のページで利用されるフラグメントは、メンテナンス性を考慮して適切に管理しましょう。
5. th:fragmentを使った実践的なサンプル
最後に、th:fragmentを使った実際のWebページ構成例を紹介します。ログインフォームを再利用するケースを考えてみましょう。
<!-- fragments/login.html -->
<div th:fragment="loginForm">
<form th:action="@{/login}" method="post">
<label for="username">ユーザー名:</label>
<input type="text" id="username" name="username" />
<br/>
<label for="password">パスワード:</label>
<input type="password" id="password" name="password" />
<br/>
<button type="submit">ログイン</button>
</form>
</div>
<!-- main.html -->
<div th:insert="fragments/login :: loginForm"></div>
これにより、ログインフォームを複数のページで再利用でき、メンテナンスも簡単になります。
6. th:insert・th:replace・th:includeの違いと選び方
Thymeleafでフラグメントを再利用する際は、状況に合わせてth:insert・th:replace・th:includeを使い分けます。ホスト要素を残したいならth:insert、完全に置き換えたいならth:replace、フラグメントの「子要素」だけを取り込みたいならth:includeが便利です。
<!-- fragments/header.html -->
<header th:fragment="siteHeader">
<h1>サイトのヘッダー</h1>
</header>
<!-- fragments/menu.html -->
<ul th:fragment="menuItems">
<li><a th:href="@{/}">ホーム</a></li>
<li><a th:href="@{/about}">会社概要</a></li>
</ul>
<!-- host.html -->
<!-- insert: ホスト要素(<header>)は残る -->
<header th:insert="~{fragments/header :: siteHeader}"></header>
<!-- replace: ホスト要素ごと置き換える -->
<header th:replace="~{fragments/header :: siteHeader}"></header>
<!-- include: フラグメントの「子要素」だけを取り込む -->
<nav>
<ul th:include="~{fragments/menu :: menuItems}"></ul>
</nav>
7. レイアウト全体をth:fragmentで共通化する手順(ヘッダー・フッター込み)
ページ全体のレイアウトをフラグメント化すると、ヘッダー・フッターを一括管理でき、テンプレートの再利用性がさらに高まります。以下は「中身」を引数で受け取り、共通レイアウトに差し込む定番パターンです。
<!-- layouts/base.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>共通レイアウト</title>
</head>
<body>
<div th:fragment="layout(content)">
<div th:insert="~{fragments/header :: headerFragment}"></div>
<main th:replace="${content}"></main>
<div th:insert="~{fragments/footer :: footerFragment}"></div>
</div>
</body>
</html>
<!-- pages/home.html -->
<div th:replace="~{layouts/base :: layout(~{::content})}">
<div th:fragment="content">
<h2>ホーム</h2>
<p>ここにページごとのメインコンテンツを記述します。</p>
</div>
</div>
8. 複数パラメータ・名前付き引数で柔軟に再利用する
th:fragmentは複数パラメータや名前付き引数に対応しています。メッセージや種類を渡して表示内容を切り替えるなど、同一フラグメントを汎用化できます。
<!-- fragments/alert.html -->
<div th:fragment="alert(type, message)"
th:class="'alert ' + (${type} == 'success' ? 'alert-success' : (${type} == 'error' ? 'alert-danger' : 'alert-info'))">
<i class="bi bi-info-circle"></i> [[${message}]]
</div>
<!-- 呼び出し側 -->
<div th:replace="~{fragments/alert :: alert(type='success', message='登録に成功しました')}"></div>
<div th:replace="~{fragments/alert :: alert(type='error', message='エラーが発生しました')}"></div>
9. th:fragmentとループ・条件分岐で動的ナビゲーションを作る
フラグメント内でth:eachやth:ifを使えば、配列やログイン状態に応じたメニューを動的生成できます。現在ページに「active」クラスを付ける例です。
<!-- fragments/nav.html -->
<nav th:fragment="nav(items)">
<ul class="nav">
<li class="nav-item" th:each="item : ${items}"
th:classappend="${#httpServletRequest.requestURI.startsWith(item.href)} ? ' active' : ''">
<a class="nav-link" th:href="${item.href}" th:text="${item.label}">Link</a>
</li>
</ul>
</nav>
<!-- 呼び出し側(ControllerでmenuItemsをModelに投入しておく) -->
<div th:replace="~{fragments/nav :: nav(items=${menuItems})}"></div>
まとめ
ここまで、Thymeleafのth:fragmentを使ったテンプレートの再利用方法について詳しく解説してきました。th:fragmentを使用することで、HTMLの共通部分を効率的に管理し、コードの重複を減らすことができます。特に、ヘッダー、フッター、ナビゲーションバーなどの共通コンポーネントを独立したテンプレートファイルに分けて再利用することで、メンテナンスが非常に容易になります。
さらに、th:insertやth:replaceを利用することで、柔軟にフラグメントを読み込み、パラメータを渡して動的に表示内容を変更することが可能です。この機能は、大規模なWebアプリケーションにおいて特に有用であり、テンプレートの一元管理により開発効率が向上します。また、パラメータ付きフラグメントを利用すれば、ページごとに異なるデータを表示しつつ、共通のレイアウトを保持できます。
例えば、eコマースサイトでは商品カードのフラグメントを作成し、異なる商品情報をパラメータで渡すことで、同じレイアウトを維持しながら動的に商品情報を表示することができます。以下にそのような例を示します。
<!-- fragments/productCard.html -->
<div th:fragment="productCard (productName, price, imageUrl)">
<div class="product-card">
<img th:src="@{${imageUrl}}" alt="商品画像" class="img-fluid" />
<h3>[[${productName}]]</h3>
<p>価格: ¥[[${price}]]</p>
</div>
</div>
<!-- main.html -->
<div th:insert="fragments/productCard :: productCard
(productName='商品A', price=5000, imageUrl='/images/productA.jpg')"></div>
<div th:insert="fragments/productCard :: productCard
(productName='商品B', price=3000, imageUrl='/images/productB.jpg')"></div>
上記の例では、th:fragmentを使用して商品カードのテンプレートを定義し、異なる商品データを渡すことで、効率的に商品リストを表示することができます。これにより、HTMLコードがより簡潔になり、保守性が向上します。
さらに、Thymeleafのth:replaceは、既存の要素をフラグメントの内容で置き換えるため、特定の部分を動的に変更する際に便利です。例えば、ログイン状態によって異なるメニューを表示する場合に役立ちます。
これらのThymeleafの機能を活用することで、効率的なWebページ開発が可能になります。テンプレートの再利用は、今後のメンテナンスコストを削減し、新しい機能追加時の作業効率を向上させるための重要な手法です。
生徒
「今日はth:fragmentの使い方をしっかり学べました!テンプレートを再利用することで、コードがすごくスッキリしました。」
先生
「そうですね。th:fragmentを使うと、共通部分を管理しやすくなり、変更があった場合でも一箇所修正するだけで済みます。これが大規模なプロジェクトでの効率化につながります。」
生徒
「さらに、パラメータを渡せることで、同じフラグメントをいろいろなページで使い回すことができるんですね。」
先生
「その通りです。例えば、商品リストやユーザーのプロフィールなど、パラメータを使って動的に内容を変えられるコンテンツに最適です。理解が深まったようで良かったです。」
生徒
「これから実際のプロジェクトでもth:fragmentを積極的に使ってみます!テンプレートの管理がとても楽になりそうです。」
先生
「ぜひ挑戦してください。Thymeleafの他の機能も活用すれば、さらに効率的に開発が進められますよ。」
この記事を読んだ人からの質問
プログラミング初心者からのよくある疑問/質問を解決します
th:fragmentとは何ですか?どのような場面で使いますか?
th:fragmentは、ThymeleafでHTMLテンプレートの一部を再利用可能にするための属性です。例えば、ヘッダーやフッターなどの共通部分を定義して、複数のページで再利用する際に使用します。
th:fragmentを使用するメリットは何ですか?
HTMLコードの重複を減らし、保守性を向上させることができます。一度フラグメントを定義すれば、修正が必要な場合にその部分だけを変更すれば、関連するすべてのページに反映されます。
th:fragmentはどのように呼び出しますか?
フラグメントを呼び出すには、th:insertやth:replaceを使用します。例えば、th:insert="fragments/header :: headerFragment"のように記述すると、指定したフラグメントがページに挿入されます。
th:fragmentにパラメータを渡す方法を教えてください。
th:fragmentでは、(paramName)という形式でパラメータを受け取り、th:insertやth:replaceで(paramName='value')の形式で値を渡すことができます。これにより、フラグメントを動的にカスタマイズできます。
th:fragmentとth:replaceの違いは何ですか?
th:insertは指定したフラグメントを現在の要素の中に挿入します。一方、th:replaceは現在の要素を完全にフラグメントで置き換えます。用途に応じて使い分けます。
th:fragmentを使用する際の注意点はありますか?
ファイルパスの正確な指定やUTF-8でのファイル保存が重要です。また、フラグメントを適切に管理しないと、プロジェクトが複雑になりやすいため、ディレクトリ構造を整理しておきましょう。
th:fragmentを使ってログインフォームを再利用するにはどうすれば良いですか?
ログインフォームをフラグメントとして定義し、必要なページでth:insertを使って呼び出します。これにより、フォームの変更があった場合、一箇所の修正で全ページに反映されます。
eコマースサイトでth:fragmentをどのように活用できますか?
商品カードや検索結果の表示部分をフラグメントとして定義し、商品データをパラメータとして渡すことで、統一されたデザインを保ちながら効率的に商品情報を表示できます。
th:fragmentを使うとパフォーマンスに影響はありますか?
適切に設計すれば、コードの再利用性が向上し、保守コストが削減されるため、結果的に開発効率が向上します。ただし、過剰なフラグメント分割はパフォーマンスに影響を与える可能性があるため、適度に使用しましょう。
th:fragmentの使用例を学ぶために初心者が練習すべきことは何ですか?
まずはヘッダーやフッターをフラグメント化し、異なるページで再利用する練習をすると良いでしょう。その後、パラメータを渡して動的にフラグメントを活用する方法を試してみてください。
どんな場合にth:insertを選ぶべきですか?
ホスト要素(呼び出し側のタグ)を残したい場合に適しています。既存のclassやaria属性、データ属性を保持したまま中身だけ差し込みたいケースで使うと安全です。
th:replaceを使うときの落とし穴はありますか?
ホスト要素ごと置き換わるため、ホスト側に付けたidやclassは失われます。スタイルやテスト用セレクタが必要なら、フラグメント側にそれらを移すか、th:insertに切り替えましょう。
th:includeを選ぶメリットは何ですか?
フラグメントの「子要素」だけを取り込める点です。例えば、呼び出し側の<ul>を維持しつつ、<li>だけを差し込みたいナビゲーションやリスト構築に相性が良いです。
入れ子でinsertとreplaceを混在させても大丈夫ですか?
可能ですが、想定外の入れ子や余計なラッパー要素が生じやすくなります。フラグメントのルート要素を明確にし、DOMが二重構造にならないように設計してください。
フラグメント再利用で重複idが出るのを避ける方法は?
idは固定せず、引数で渡して動的に付与するのが安全です。例:フラグメント側でth:id="${id}"とし、呼び出し側で一意なidを渡して衝突を防ぎます。
Spring FrameworkやThymeleafを使った Webアプリ開発の全体像をやさしく理解したい人には、 この入門書が定番です。
Spring Framework超入門をAmazonで見る※ Amazon広告リンク