Spring Boot入門:Thymeleafのファイル分割

Spring Boot入門Thymeleafのファイル分割アイキャッチプログラム
スポンサーリンク

この記事ではThymeleafでファイルを分割して管理する方法を説明します。Spring Boot入門:Thymeleafの基本の続きです。

この記事を読むとわかるファイルの分割方法
  • 各画面共通のパーツを定義するのに便利なlayoutの使い方
  • タグを外部ファイルから読み込むth:includeとth:replace
前提条件
  • Spring Bootの開発環境が整っている
  • 解説ではPleiades All in One Eclipseを使う
  • Javaのバージョンは11
  • Spring Bootのバージョンは2.5.5
関連記事

この記事はSpring Boot入門の一部です。環境構築の方法から初めて基本的なWebアプリケーションの開発に必要なことを説明しています。

この記事のソースコード

この記事のソースコードはGithubに公開しています。

gsg0222/spring-boot-tutorial-step3
Contribute to gsg0222/spring-boot-tutorial-step3 development by creating an account on GitHub.

GithubからSpring BootプロジェクトをEclipseにインポートする方法は次の記事を参考にしてください。

Spring Bootプロジェクトを作成

Spring Bootプロジェクトを作成します。

これまでと同じなので、画像も不要でしょう。プロジェクト名やパッケージ名などに適当なものを入力してください。(この記事ではパッケージがblog.tsuchiya.tutorial.step3である前提です)

同じく、依存関係も前回と同じです。Spring Boot DevTools、Lombok、Thymeleaf、Spring Webを選択してください。

ここから先はこの記事独自の設定です。

プロジェクトを作ると直下のフォルダにpom.xmlというファイルが作られます。

pom.xmlの場所

今回使うlayout:decorateやlayout:fragmentという機能のために、このpom.xmlを編集する必要があります。

<dependencies>というタグの一番最後に、以下のタグを追加してください。

		<dependency>
			<groupId>nz.net.ultraq.thymeleaf</groupId>
			<artifactId>thymeleaf-layout-dialect</artifactId>
		</dependency>

次の画像みたいになります。

dependenciesにthymeleaf-layout-dialectの情報を追加
pom.xmlにthymeleaf-layout-dialectの情報を追加

ソースコードをダウンロードしないで手打ちする人は、この作業を忘れないようにしてください。

この記事で使うソースコード概略

Controller

今回はThymeleafの説明なので、Controllerはほとんど何もしません。Modelに適当な値を放り込んでいるだけです。説明も必要ないでしょう。

package blog.tsuchiya.tutorial.step3.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class TestController {

	@GetMapping
	public String index(Model model) {
		model.addAttribute("title", "コンテンツページ");
		return "contents";
	}

	@GetMapping("other")
	public String other(Model model) {
		model.addAttribute("title", "別ページ");
		return "other";
	}
}

Thymeleaf

ファイルの分割を行う関係上、Thymeleafテンプレートは数が多いです。

まず、それぞれのファイルの関係を図で示します。矢印の通りにファイルを呼び出す流れです。

各ファイルの相関関係
各htmlファイルの相関関係

左の方、他のファイルを呼び出す側からコードを張っていきます。細かい説明は後で行うので、今はなんとなく眺めてみてください。

なお、Thymeleafテンプレートは全部src/main/resources/templates/に保存します。

contents.html

<!DOCTYPE html>
<!--/* layout:decorateで埋め込み先を定義 */-->
<html lang="ja" xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
	layout:decorate="layout">
<head>
<meta charset="UTF-8">
<title>コンテンツ</title>
</head>
<body>
	<!--/* layoutに埋め込む部分を定義 */-->
	<div layout:fragment="contents">
		<!--/* Modelに渡した値はどのhtmlでも参照可能 */-->
		<h1 th:text="${title}">ダミー</h1>
		<p>コンテンツ</p>
	</div>
</body>
</html>

Controllerから呼び出されるファイルです。5行目のlayout:decorateで呼び出す対象ファイルを指定し、12行目で呼び出し先のファイルに埋め込む部分を設定します。

other.html

<!DOCTYPE html>
<!--/* layout:decorateで埋め込み先を定義 */-->
<html lang="ja" xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
	layout:decorate="layout">
<head>
<meta charset="UTF-8">
<title>コンテンツ</title>
</head>
<body>
	<!--/* layoutに埋め込む部分を定義 */-->
	<div layout:fragment="contents">
		<!--/* Modelに渡した値はどのhtmlでも参照可能 */-->
		<h1 th:text="${title}">ダミー</h1>
		<p>別ページ</p>
	</div>
</body>
</html>

contents.htmlとほぼ同じ内容です。複数のファイルからlayout.htmlを呼び出すとどうなるかを示すためだけのファイルです。

layout.html

<!DOCTYPE html>
<!--/* layoutを使うための定義もしておく */-->
<html lang="ja" xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<!--/* Modelに渡した値はどのhtmlでも参照可能 */-->
<title th:text="${title}">タイトル</title>
<link
	href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css"
	rel="stylesheet">
</head>
<body>
	<div class="container">
		<!--/* ヘッダーを別ファイルから読み込み */-->
		<div id="header" th:include="header::header">ヘッダー</div>
		<main class="col-9">
			<!--/* 本文は別ファイルから埋め込む */-->
			<th:block layout:fragment="contents"></th:block>
		</main>
		<!--/* フッターを別ファイルから読み込み */-->
		<div id="footer" th:replace="footer::footer">フッター</div>
	</div>
</body>
</html>

短いけど色々内容が詰まったファイルです。layout:decolateを使って呼び出されると同時に、th:includeとth:replaceで他のファイルを呼び出しています。

header.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ヘッダー</title>
</head>
<body>
	<!--/* th:fragmentで別ファイルから読み込めるようにする */-->
	<header class="my-2" th:fragment="header">
		<nav class="navbar navbar-dark bg-dark">
			<!--/* Modelに渡した値はどのhtmlでも参照可能 */-->
			<p class="navbar-brand" th:text="${title}">ダミー</p>
		</nav>
	</header>

</body>
</html>

layout.htmlから呼び出されるファイルです。正確にはth:fragmentを使ったタグが呼び出されます。

footer.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!--/* th:fragmentで別ファイルから読み込めるようにする */-->
	<footer class="text-center my-2 bg-dark text-light"
		th:fragment="footer">
		Copyright 2021
		<!--/* Modelに渡した値はどのhtmlでも参照可能 */-->
		<span th:text="${title}">ダミー</span>
	</footer>

</body>
</html>

header.htmlと同様layout.htmlから呼び出されるファイルです。

共通パーツを使い回すlayout:decorateとlayout:fragment

layoutで始まる属性を使うためには、htmlタグに以下の属性を追加する必要があります。

xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"

layout:decorateとlayout:fragmentはセットで使うことになる属性です。呼び出し側はlayout:decorateで呼び出す先のファイルを指定し、layout:fragmentで呼び出す先の埋め込み先を設定します。

contents.htmlだとこの部分ですね。

<html lang="ja" xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
	layout:decorate="layout">
<!-- 中略 -->
	<div layout:fragment="contents">
<!-- 後略 -->

layout:decorateでlayoutファイルを呼び出す指定(拡張子は省略します)を行い、呼び出し先で埋め込むタグをlayout:fragmentで指定しています。

呼び出す先のlayout.htmlでは、どこにlayout:fragmentで指定したタグを埋め込むのかを指定します。呼び出し元でもlayout:fragment、呼び出し先でもlayout:fragmentで分かりにくいですが、仕様なので仕方ないです・・・。

layout.htmlで埋め込む先はこの部分になります。

			<th:block layout:fragment="contents"></th:block>

Spring Bootを実行してhttp://localhost:8080/にアクセスして見てください。[ページのソースを表示]すると、次のように展開されていることがわかります。

		<main>
			
			<div>
		
		<h1>コンテンツページ</h1>
		<p>コンテンツ</p>
	</div>
		</main>

contents.htmlからlayout.htmlを呼び出し、layout.htmlのlayout:fragment属性を指定したタグをcontents.htmlのlayout:fragment属性を指定したタグで置き換えていることがわかります。

layout.htmlは複数のファイルから呼び出すことが可能です。http://localhost:8080/otherを表示するとother.htmlの内容を埋め込んだ表示がされます。

外部ファイルからの呼び出しその1、th:include

layout.htmlではヘッダとフッタの内容は別ファイルに切り出しています。少しだけ呼び出し方が異なっているので、その差を説明しましょう。

layout.htmlでヘッダの内容を呼び出しているのはこの部分です。

		<div id="header" th:include="header::header">ヘッダー</div>

th:includeに{呼び出し先の拡張子なしファイル名}::{th:fragmentで指定した名前}という形で値を指定します。

呼び出し先のheader.htmlでは、どのタグを埋め込み対象にするのかth:fragment属性で指定します。

	<header class="my-2" th:fragment="header">

今回はこのheaderタグが埋め込み対象です。

th:includeの場合は、th:include属性があるタグの中に指定したタグを展開します。[ページのソースを表示]するとこうなっているはずです。

		<div id="header">
		<nav class="navbar navbar-dark bg-dark">
			
			<p class="navbar-brand">コンテンツページ</p>
		</nav>
	</div>

th:include属性があったid=”header”のついたタグが残っていて、その中にheader.htmlでth:fragment=”header”と指定したタグが展開されているのがわかるでしょう。

外部ファイルからの呼び出しその2、th:replace

layout.htmlでフッタの内容を呼び出しているのはこの部分です。

		<div id="footer" th:replace="footer::footer">フッター</div>

th:replaceもth:includeと同じように{呼び出し先の拡張子なしファイル名}::{th:fragmentで指定した名前}を指定します。

footer.htmlでの記述もheader.htmlと似たようなものです。

	<footer class="text-center my-2 bg-dark text-light"
		th:fragment="footer">

th:includeとth:replaceの違いは、th:replaceがあるタグがまるごと置き換わることです。

				</main>
		
		<footer class="text-center my-2 bg-dark text-light">
		Copyright 2021
		
		<span>コンテンツページ</span>
	</footer>
	</div>
</body>

th:includeでは残っていたid指定したdivタグが、th:replaceではなくなっています。

まとめ:ファイルの分割をうまく利用しよう

Thymeleafでファイルを分割する方法を説明しました。

layout:decolateとlayout:fragmentは共通の部品を使い回すのに有用です。ヘッダ、フッタ、サイドバーなど各画面共通な部分をあるファイルに切り出しておいて、変更があるコンテンツ部分だけを必要に応じで埋め込む使い方がメインになるかと思います。

th:includeとth:replaceは長くなってしまったソースを分割するなどに使います。もちろん、共通部分を抜き出して使い回すことも可能です。

どちらもうまく使うとソースコードの整理に役立つので、必要に応じて使ってみてください。

サンプルコードをGithubに公開してあります。

わかりにくところなどがありましたら、Twitterお問い合わせフォームで質問をお願いします。記事のブラッシュアップに役に立つので、遠慮は無用どころか大歓迎です。

Spring Boot入門:Formと入力チェック(バリデーション)に続きます。

おすすめ

せっかくJavaを勉強しているのなら、資格を取得してみませんか?OCJP(Oracle Certified Java Programmer) SilverはJavaの基本を学ぶのに適した資格です。初心者はもとより、中堅の人でも普段使わない機能を勉強するきっかけになると思います。

残念なことに試験は有料で、しかも結構高い(税抜26,600円)です。

しかし、お勤めの会社によっては費用を負担してくれるところもあるでしょうし、場合によっては報奨金まで出してくれるはずです。(私の会社は初回の試験料負担+報奨金を出してくれました)

私のおすすめな勉強方法は徹底攻略Java SE 11 Silver問題集を読むことです。問題集ですが、解説が充実しています。

徹底攻略Java SE 11 Silver問題集[1Z0-815]対応 徹底攻略シリーズ | 志賀 澄人 | 工学 | Kindleストア | Amazon
Amazonで志賀 澄人の徹底攻略Java SE 11 Silver問題集[1Z0-815]対応 徹底攻略シリーズ。アマゾンならポイント還元本が多数。一度購入いただいた電子書籍は、KindleおよびFire端末、スマートフォンやタブレットなど、様々な端末でもお楽しみいただけます。

大体30時間くらいの勉強で合格できました。

関連記事

この記事はSpring Boot入門の一部です。環境構築の方法から初めて基本的なWebアプリケーションの開発に必要なことを説明しています。

コメント

タイトルとURLをコピーしました