Spring Boot解説第16回(開発環境編：Thymeleafその2 ~Thymeleaf Layout Dialect~)

みなさん、こんにちはこんばんは！
株式会社情創 技術開発局
贅肉を気にし始めた @TEBASAKI です。

今回は Thymeleaf Layout Dialect について解説していきます。

# Thymeleaf Layout Dialect とは
これまでは html のヘッダーやナビゲーション、フッターなどの各ページの共通部分は、それぞれの html ファイルに記述しており、管理するのが面倒でした。
テンプレートエンジン[Thymeleaf](http://www.thymeleaf.org/) の **[Thymeleaf Layout Dialect](https://github.com/ultraq/thymeleaf-layout-dialect)** は、テンプレートの再利用を可能にします。この機能を使用することで、共通部分はベースとするテンプレートページとして管理でき、そこに各ページのコンテンツデータを埋め込んで各コンテンツのページを生成できます。

# base の概念
共通部分の変更の際にすべての html ファイルを変更するのは非常に手間がかかります。
そこで共通部分を base として管理し、コンテンツ部分については別に作成します。
そして base にコンテンツ部分を埋め込むことで、実際に表示するページを作成します。
これにより共通部分は base を変更するだけで済みます。

## Decorator と Fragment
Thymeleaf Layout Dialect では base の部分を **Decorator** 、コンテンツ部分を **Fragment** と表現します。
Decorator と Fragment についてのイメージは以下の通りです。

![image](https://qiita-image-store.s3.amazonaws.com/0/116031/21661f11-a419-4187-d269-b926248890ec.png)


Decorator のコンテンツ表示部分に Fragment を埋め込み、組み合わせて一つのページにします。
Decorator である base.html と、Fragment である contents.html の詳細については後述の実装部分で解説します。

# 実装
実装の流れをデモを使って説明していきます。

## Thymeleaf Layout Dialect を使用するために
Thymeleaf Layout Dialect は Thymeleaf の機能の一つなので、build.gradle は[第15回](http://qiita.com/YAKINIKU/items/44cdbebbe9788992386b#buildgradle "第15回")のものを使えば利用できます。
しかし Decorator と Fragment には、それぞれ役割に応じた記述が必要です。

## base.html
先述した通り base にはヘッダーやナビゲーション、フッターなど、各コンテンツページの共通部分を記述します。
今回使用する base は下記の通りです。

```html:base.html
<!DOCTYPE html>
<html xmlns           ="http://www.w3.org/1999/xhtml"
      xmlns:th        ="http://www.thymeleaf.org"
      xmlns:layout    ="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
	<meta http-equiv="Content-Type"    content="text/html; charset=UTF-8">
	<title>thymeleaf base</title>
</head>
<body>
	<!-- header -->
	<header>
		<div align="center" >header</div>
		<hr />
	</header>

	<!-- contents -->
	<div layout:fragment="content">Contents is here!</div>

	<!-- footer -->
	<footer>
	 	<hr />
		<div align="center">footer</div>
	</footer>
</body>
</html>
```

### \<html\>
Thymeleaf Layout Dialect では **layout** 属性を使用するので、html タグで名前空間を指定します。

### \<head\>
head タグ内に記載した内容は title タグを除きコンテンツを埋め込む際に出力されるので、contents.html で再度記述する必要はありません。

### \<body\>
内容はシンプルにするため、ヘッダー、コンテンツ表示部分、フッターのみとしました。
コンテンツの表示部分は **layout:fragment** 属性を指定します。
このタグで囲まれた"Contents is here!"の部分が、 後述する Fragment において layout:fragment="content" の属性を持つ部分に置き換わります。
なお、Fragment で 同じ属性値をもつタグが存在しなかった場合、デフォルトで"Contents is here!"が表示されます。

## コンテンツの html
Decorator に埋め込むコンテンツを記述します。

```html:contents.html
<!DOCTYPE html>
<html xmlns           ="http://www.w3.org/1999/xhtml"
      xmlns:th        ="http://www.thymeleaf.org"
      xmlns:layout    ="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorator="base">
<head>
	<title>Contents</title>
</head>
<body>
	<div layout:fragment="content">
		<!-- コンテンツ内容 ここから -->
		<table class="table">
            <thead class="sunflower">
                <tr>
                    <td>ID</td>
                    <td>NAME</td>
                </tr>
            </thead>
            <tbody th:each="list : ${beans}">
                <tr>
                    <td class="text-str" th:text="${list.id}"></td>
                    <td class="text-str" th:text="${list.name}"></td>
                </tr>
            </tbody>
        </table>
		<!-- コンテンツ内容 ここまで -->
	</div>
</body>
</html>
```

### \<html\>
Fragment では埋め込み先の Decorator を指定する必要があります。
html タグの **layout:decorator** 属性に Decorator のファイル名を設定します。
拡張子は必要ありません。
今回は先述した base.html を指定しています。

### \<head\>
タイトルは基本的には Fragment で指定したものが優先されます。

### \<body\>
`<div layout:fragment="content">`で囲まれた部分が実際に埋め込まれる部分となります。
今回は第15回と同様のテーブルデータを表示しています。

## 動作確認
実行して動作を確認してみます。

### コントローラの記述
TestController.java を以下のように変更します。
変更箇所は、[第15回](http://qiita.com/YAKINIKU/items/44cdbebbe9788992386b#controller%E3%81%AE%E4%BD%9C%E6%88%90 "第15回")の home メソッドの返り値だけです。

```java:TestController.java
package com.qiita.demo.web.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.qiita.demo.domain.tbl.TestBean;
import com.qiita.demo.service.test.TestService;


@Controller
public class TestController {

	@Autowired
	private TestService testService;

	@RequestMapping("/test")
	public String home(Model model) {
		List<TestBean> list = testService.selectAll();
		model.addAttribute("beans", list);
		return "contents";
	}
}

```

呼び出す html ファイルは Fragment を指定します。
Fragment 内で Decorator を指定しているので、Fragment を呼べば自動的に埋め込みが行われます。

### ビルド&実行
前回までと同様にプロジェクトを右クリック→Gradleから、すべてリフレッシュを行い、bootRun で実行します。

http://localhost:8080/test にアクセスすると以下の画面が表示されます。
![image](https://qiita-image-store.s3.amazonaws.com/0/116031/545136c6-4837-b7b8-6d87-b6c704b697d9.png)


画面のコードを表示してみると、Decorator に Fragment が埋め込まれていることが確認できます。
![image](https://qiita-image-store.s3.amazonaws.com/0/116031/8288e13f-5849-213d-4c98-2309c0f8b1b7.png)




以上のように、Thymeleaf Layout Dialect を使えば大量のhtmlファイルを修正する手間が省け、作業が捗ります。

***

今回はここまでです。
最後まで読んでいただき、ありがとうございました。

それではみなさん、またお会いしましょう！

# 参考URL
- [thymeleafのlayoutについて - Qiita](http://qiita.com/rubytomato@github/items/cbfe7540da0e2e35318a "thymeleafのlayoutについて - Qiita")

- [Thymeleaf Layout Dialectのご紹介 ｜ Developers.IO](http://dev.classmethod.jp/client-side/intro-to-thymeleaf-layout-dialect/ "Thymeleaf Layout Dialectのご紹介 ｜ Developers.IO")

- [spring boot その4 -  Thymeleaf  の layout を利用して、 ページ固有の title、meta、 style、script タグを出力する - huruyosi’s blog](http://huruyosi.hatenablog.com/entry/2015/08/02/012646 "spring boot その4 -  Thymeleaf  の layout を利用して、 ページ固有の title、meta、 style、script タグを出力する - huruyosi’s blog" )