Thymeleafのコンポーネント
コンポーネントとは
コンポーネントは、Webアプリケーション内で再利用可能な部品または要素を指します。ページ内の異なる場所で、同じ機能や見た目を持つ部分を再利用するための仕組みです。
Thymeleafでは、コンポーネントを次のような方法で作成・利用することができます:
フラグメント
フラグメント(Fragment)はテンプレート内の一部分で、再利用する部品として分離できます。th:fragmentでフラグメントを定義し、th:replaceプロパティで他のテンプレート内でフラグメントを呼び出します。
th:fragment
th:fragmentは、再利用可能なフラグメントを定義するThymeleafのプロパティです。
フラグメントを定義するテンプレートに、th:fragmentプロパティでフラグメント名を設定します。
<div th:fragment="フラグメント名">
コンテンツ
</div>
コンポーネントのプロパティ
外部コンポーネントを読み込むのに、Thymeleafではフラグメントや外部ファイルを読み込むプロパティが用意されています。
- th:replace
- th:include ※Thymeleaf3.0から非推奨
th:replace
th:replaceはテンプレート内の要素を他のテンプレートに置き換えるためのThymeleafのプロパティです。継承元のテンプレートの一部を再利用する場合に便利です。
<div th:replace="~{ファイルパス}"></div>
th:include
th:includeは、他のテンプレートファイルを現在のテンプレート内に挿入するThymeleafのプロパティです。再利用可能なコンポーネントをファイルごと挿入することをインクルード(Include)といいます。
<div th:include="ファイルパス"></div>
~{ }
~{ } は、テンプレートファイルを参照するための記法で、複数のテンプレート間でコンテンツを読み込みます。
以下はdivタグを「templates/components/head.html」コンポーネントで置き換えます。
<div th:replace="~{components/head}"></div>
ヘッダーコンポーネント
ファイル構成
├── components/
│ ├── default/
│ │ └── navbar.html
│ └── head.html
コンポーネントファイル作成
「templates/」に「components」フォルダと「head.html」を作成します。
コンポーネントファイル「components/head.html」に、HTMLのヘッダー部分を追加します。
templates/components/head.html
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My News</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" th:href="@{/css/style.css}">
</head>
コンポーネント読み込み
レイアウトファイルのheadタグの th:replaceで、「components/head.html」コンポーネントを読み込みます。
templates/layouts/default.html
<!DOCTYPE html>
<html lang="ja">
<!-- headタグの中にコンポーネントを読み込み -->
<head th:replace="~{components/head}"></head>
<body>
....
</body>
</html>
HTMLソース確認
headタグのコンポーネントが読み込まれているか、HTMLソースを確認してみましょう。
結果
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My News</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
...
</body>
</html>
ナビゲーションコンポーネント
コンポーネントファイル作成
「components/」に「default」フォルダと「navbar.html」を作成します。
コンポーネントファイル「components/default/navbar.html」に、ナビゲーションリンクを作成します。
templates/components/default/navbar.html
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" th:href="@{/}">Top</a>
</li>
<li class="nav-item">
<a class="nav-link" th:href="@{/article/}">News</a>
</li>
</ul>
</div>
</div>
</nav>
コンポーネント読み込み
レイアウトファイルにnavタグを追加し、th:replaceで「components/default/head.html」を読み込みます。
templates/layouts/default.html
<!DOCTYPE html>
<html lang="ja">
<head th:replace="~{components/head}">
</head>
<body>
<!-- navタグをコンポーネントで置き換え -->
<nav th:replace="~{components/default/navbar}"></nav>
...
</body>
</html>
ナビゲーションが表示されるか確認しましょう。
結果

演習
問題1
「components/admin/navbar.html」を作成し、レイアウト「layouts/admin.html」のth:replaceで読み込んでみましょう。

components/admin/navbar.html
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" th:href="@{/admin/}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" th:href="@{/admin/article/}">ニュース記事</a>
</li>
</ul>
<form class="d-flex" method="post" th:action="@{/logout}">
<button class="btn btn-sm" type="submit"
onclick="return confirm('ログアウトしますか?')">ログアウト</button>
</form>
</div>
</div>
</nav>