この章の内容は後で参照できれば大丈夫です。覚える必要はありません。

前回は「セッション管理」について学びました。セッションにより複数のHTMLページでデータを保持することができ、ログインや買い物かごの機能が実現できました。

今回はThymeleafを使いデータをHTMLテンプレートで動的に表示する方法を学びましょう。

今回の学習の重点をオレンジの枠線で図示します。

Thymeleafを学び、データをHTMLテンプレートで動的に表示する

1. なぜThymeleaf(テンプレートエンジン)が必要なのか?

Thymeleafを使うと、Javaのオブジェクトの値を動的に埋め込んだり、条件分岐や繰り返し表示ができます。

なぜThymeleafなのかその理由は以下の2点です。

  1. デザイナーとの協業がしやすい:HTMLファイルをそのままブラウザでプレビューしやすい
  2. Spring Bootとの相性が良い

「templatesフォルダに入っているのは動的なページのもと」であるということ

Spring BootでWebアプリを作るとき、「templates」フォルダには動的なWebページの“もと”となるファイルが入っています。ここでいう「動的」とは、「アクセスするたびに内容が変わるページ」という意味です。たとえば、ユーザーの名前を表示したり、ログイン中の人の情報を載せたりするページなどがそれに当たります。

このフォルダの中には、通常のHTMLに加えて「テンプレートエンジン」を使った特別なHTMLファイルを置きます。Spring Bootでは「Thymeleaf(タイムリーフ)」がよく使われており、HTMLの中に ${} や th:text のような記法で、Javaのコードから渡された値を埋め込むことができます。

「templates」フォルダには動的なWebページの“もと”となるファイルが入っていて、実はHTMLそのものではありません。「templates」はひな形ですからね。このことは、アクセスするURLの形式からも確認できます。

staticフォルダにはその名の通り静的なファイルを置きます。例えばHTMLの研修で作成したファイルです。staticフォルダにある画像やCSSファイルであれば、以下のようにファイルパスをそのまま指定すれば表示されます。

ところが、templatesフォルダにあるHTMLファイルは、次のようなURLでアクセスすることはできません。

http://localhost:8080/templates/page.html ← エラーになる

では、どうやってアクセスするのか?

それはコントローラー(Controller)を通して呼び出します。
Spring Bootのコントローラーで「return "page";」のように書くと、templates/page.html をもとにして、値を埋め込んだページを生成してから返してくれるのです。

このように、templates内のファイルはあくまで「台本」や「設計図」のようなもので、そのままでは表示されず、サーバー側で処理をして初めてWebページになるのです。

この違いを理解しておくことで、Spring Bootの仕組みがぐっと分かりやすくなります!

2. Thymeleafの基本

Thymeleafを使用するには、pom.xmlに以下の依存関係(Dependency)が必要です。

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

2.1. 変数展開の基本

ThymeleafではHTMLタグ属性に対してth:*を使います。

HTMLの中に以下のように記述します。

<p th:text="${msg}">ここにメッセージが入ります</p>

${msg} → コントローラーからmodel.addAttribute("msg", "Hello") された文字列などを埋め込みます。

Controller側

package com.example.demo.controller;

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

@Controller
public class MainController{
	@GetMapping("/")
	public String sample(Model model) {
		model.addAttribute("msg", "Hello");
	
		return "sample";
	}
}

sample.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"></head>
<body>
  <p th:text="${msg}">ここにメッセージ</p>
</body>
</html>

変数展開のイメージ

これで / にアクセスすると <p>Hello</p> が表示されます。

"ここにメッセージ" は、通常 表示されない 文字列です。なぜなら、Thymeleafの th:text 属性は、指定された変数 ${msg} の値でタグの内容を置き換えるからです。つまり、msg に値がある場合は、<p> の中身が msg の値に置き換えられ、元の "ここにメッセージ..." は消えてしまいます。

ただし、サーバーを通さない場合は、ブラウザが th:text という属性を無視するため、タグの中身である「ここにメッセージ」を表示します。デザイナーがJavaを起動しなくても、ブラウザでHTMLファイルを開くだけで画面レイアウトを確認できます。

Thymeleaf側のスペルミスを素早く見分ける

上記のThymeleafで以下のように間違えたとします。

<p th:text="${ms}">ここにメッセージ</p>

この場合は、msはnullとなり、何も表示されません。

しかし、以下のように間違えるとJavaのNullPointerException的なエラーが発生します。

<p th:text="${ms.toString()}">ここにメッセージ</p>

エラーログの読み解き方

このとき、Eclipseのコンソールでログの最上部を確認すると、org.thymeleaf.exceptions.TemplateProcessingException という例外の名前が記載されています。この記述は、Thymeleafが画面を組み立てる処理の途中で問題が発生したことを示しています。

さらに、原因の根本を示す Caused by の行を確認すると、org.springframework.expression.spel.SpelEvaluationException という例外とともに、Attempted to call method toString() on null context object というメッセージが記録されています。このメッセージは、中身が空のオブジェクトに対して toString メソッドを呼び出そうとしたという意味になります。

Javaのプログラムの内部処理で計算を間違えたのではなく、画面を組み立てる段階で存在しないデータ(ms)を扱おうとしたために起因するエラーであるため、修正すべき対象はHTMLファイルの側であると判断できます。

文字列はシングルクォートで囲む

変数と文字列を組み合わせる場合

変数と文字列を組み合わせて表示したい場合、文字列部分は シングルクォートで囲む 必要があります。

例:変数と文字列の結合

<p th:text="${name} + 'さん、こんにちは!'"></p>

この場合、name の値が "田中" だったとすると、以下のHTMLに変換されます:

<p>田中さん、こんにちは!</p>

ダブルクォートを使うとエラーになる

th:text はHTMLの属性なので、通常は ダブルクォート(")で囲まれた属性値 の中に記述します。そのため、文字列の開始・終了にダブルクォートを使うと、HTMLの属性と衝突する ため、エラーが発生します。

間違った例(エラー)

<p th:text="${name} + "さん、こんにちは!""></p>

このようにダブルクォートを使うと、th:text の属性値が "Hello, で終了したと解釈され、正しく動作しません。

なぜ、xmlnsを書く必要があるのか?

XMLはHTMLと異なり、開発者が独自のタグや属性を自由に定義して機能を拡張できる性質を持っています。

<html xmlns:th="www.thymeleaf.org">という記述が必要な理由は、thから始まる属性がThymeleafの規格に属することを明示するためです。同じ名前のタグを重なった時に重複しないために付けています。

ただし、この研修では同じ名前のタグが重なることはないため付けなくても結構です。

調べてみましょう

Thymeleafでは[[${msg}]]記法を使うこともできます。

<p>[[${msg}]]</p>

この書き方では、[[${msg}]]の位置に${msg}の値が挿入されます。

HTMLエスケープ処理が行われ、安全に変数を表示できます。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
</head>
<body>
  <p>[[${msg}]]</p>
</body>
</html>

この場合も / にアクセスすると、以下のように表示されます。

<p>Hello</p>

このように、th:text属性と[[${msg}]]は同じ結果になりますが、[[${msg}]]記法の場合は、HTMLタグの中のプレースホルダーの位置に変数の値を展開する形になります。

th:text → タグ内の内容を丸ごと置換する

[[${msg}]] → タグ内の指定位置に挿入する

本研修では、th:text を優先して使っていきます。

実験

以下は、Thymeleafテンプレートで悪意あるJavaScriptが挿入される例(XSS攻撃の例)です。

th:utext は、「unescaped text」(エスケープされていないテキスト)を意味するものです。HTMLをそのまま出力したいときに使います。以下を実験してみましょう。

Controller側の例(安全ではない文字列を渡す場合)
@GetMapping("/")
public String sample(Model model) {
    model.addAttribute("msg", 
      "<script>alert('攻撃!');</script>");
    return "sample";
}
安全でない書き方(HTMLタグをそのまま出力する場合)
th:utext属性を使用する場合(危険)
<p th:utext="${msg}">ここにメッセージ</p>

結果(ブラウザのレンダリング)

<p><script>alert('攻撃!');</script></p>

このコードがブラウザで実行されると、JavaScriptのダイアログが表示され、攻撃が成立してしまいます。

安全な書き方(自動エスケープで安全に表示される)
th:text属性を使用した場合(安全)
<p th:text="${msg}">ここにメッセージ</p>

結果(エスケープされて安全)

<p>&lt;script&gt;alert(&#39;攻撃!&#39;);&lt;/script&gt;</p>


HTMLタグとして認識されず、ブラウザでそのままテキストとして表示されます。

2.4 ==で文字列の比較ができる

Thymeleafの式内で、文字列を比較する場合は、JavaSEとは異なり、==で比較可能です。なお文字列リテラルは '(シングルクォーテーション)で囲む必要があります。

※ここから先はThymeleafと対応するHTMLを上下に並べて配置しますので見比べてください。

 <p th:text="${msg == 'Hello World' }">
メッセージがHello Worldかどうか?</p>
<p>true</p> <!--msgがHello Worldの場合 -->

ポイント:

  1. Thymeleafでは内部で equals() による比較を自動で行うため、Javaのように equals()を呼ぶ必要はありません。
  2. ただし、equals()を明示的に呼ぶことも可能です。
<p th:text="${msg.equals('Hello World') }">
メッセージがHello Worldかどうか?</p>
<p>true</p> <!--msgがHello Worldの場合 -->

2.5 数値の比較 (==)

数値についても同様に比較可能です。

<p th:text="${age == 20 }">ageが20かどうか?</p>
<p>false</p> <!--ageが19の場合 -->

数値の場合、以下のような大小比較も簡単に行えます。

<p th:text="${age <= 20 }">ageが20以下かどうか?</p>
<p>true</p> <!--ageが19の場合 -->

3. Thymeleafでの条件分岐・繰り返し

3.1 条件分岐

th:if, th:unless

例えばVIPユーザーと一般ユーザーで表示画面を変えたいような場合には、th:ifth:unless を使うのが最も簡単です。

th:if は条件が「真(true)」のときに要素を表示し、th:unless は条件が「偽(false)」のときに要素を表示します。どちらもテンプレートの中で条件分岐を簡潔に記述できるため、ユーザーの権限や状態によって表示を切り替えたい場面で非常に便利です。

また、th:if と th:unless を併用することで、HTMLの構造を分かりやすく保ちながら柔軟な表示制御が可能となります。たとえば、ログインしているかどうかを判定して、ログイン中のメッセージとログイン前の案内を切り替えるといったケースにも応用できます。

ちなみに英語の【unless 】には~しない限りという意味があります。例えば、「unless it rains」で雨が降らない限りという意味です。

<p th:if="${age >= 20}">お酒が飲めます</p>
<p th:unless="${age >= 20}">まだ飲めません</p>
<p>まだ飲めません</p> <!--ageが19の場合 -->

  1. th:if="${条件式}" → 条件式がtrueならタグを表示、falseなら非表示
  2. th:unless="${条件式}" → th:ifの逆で、条件式がfalseなら表示、trueなら非表示

参考(JavaSE)

int age = 19; // 任意の年齢を設定

if (age >= 20) {
    System.out.println("お酒が飲めます");
} else {
    System.out.println("まだ飲めません");
}

3.2 null の扱いについて

変数がnullかどうかは、Thymeleafでは以下のように記述します。

nullの場合を判定

<p th:if="${user == null}">ユーザーが存在しません。</p>
<p>ユーザーが存在しません。</p> <!--userがnullの場合 -->

空文字の扱いについて

文字列が空文字かどうかの判定もよく行います。

<p th:if="${name == ''}">名前が入力されていません</p>
<p>名前が入力されていません</p> <!--nameが空文字の場合 -->

また、空文字やnullをまとめて判定するには以下の書き方が便利です。

<!-- 文字列がnullまたは空文字の場合 -->
<p th:if="${#strings.isEmpty(name)}">名前が未入力です</p>

#stringsはThymeleafが提供する便利なユーティリティオブジェクトです。

3.3 プロパティアクセスのNullPointerException対策

Thymeleafテンプレートでオブジェクトのプロパティにアクセスする際、対象のオブジェクトがnullの場合にはNullPointerExceptionに似たエラーが発生することがあります。たとえば、user.nameにアクセスしようとして、user自体がnullの場合、テンプレート処理中に例外が発生し、画面の描画に失敗してしまう可能性があります。これを防ぐためには、プロパティにアクセスする前に、そのオブジェクトがnullでないことを確認する対策が必要です。

th:ifやth:unlessを使って、条件付きで要素の表示を制御できます。たとえば、次のように記述することで、安全にuser.nameを扱うことができます。

<p th:if="${user != null}" th:text="${user.name}">名前</p>

このようにすることで、userがnullでない場合にのみuser.nameが評価されるため、テンプレートエンジンによる評価エラーを回避できます。

また、テンプレート内で複数のプロパティアクセスが必要な場合、三項演算子を使って簡潔に条件分岐を書くこともできます。

<p th:text="${user != null ? user.name : '未設定'}"></p>

このようにすることで、userがnullなら「未設定」と表示し、安全かつ柔軟なテンプレート記述が可能になります。

3.4 繰り返し

データベースで取得した商品情報や顧客情報をWebページに一覧表示したいときに便利なのが th:each 属性です。これは JavaSEの拡張for文(for-each文) に相当し、リストや配列などのコレクションを1件ずつ順に取り出して表示する際に使われます。

<ul>
  <li th:each="name : ${names}" th:text="${name}">名前</li>
</ul>
<ul>
	<li>imai</li>
	<li>matsuda</li>
	<li>tabuchi</li>
	<li>hashi</li>
</ul>
<!-- namesが4人の名前の場合 -->

  1. ${names} は、Controller側で List<String> などをmodel.addAttribute("names", namesList) しておく。
  2. name : ${names} → 変数nameにリストの要素が順番に入る
  3. th:text="${name}" → 文字列を表示

例)オブジェクトのリストをテーブル表示

<table>
  <thead>
    <tr><th>ID</th><th>名前</th><th>価格</th></tr>
  </thead>
  <tbody>
    <tr th:each="car : ${cars}">
      <td th:text="${car.id}"></td>
      <td th:text="${car.name}"></td>
      <td th:text="${car.price}"></td>
    </tr>
  </tbody>
</table>

このように、th:each はThymeleafでリストデータを表形式やカード形式などで表示したいときに非常に便利な属性です。リスト表示の基本として、フォーム連携やテーブル更新処理とも相性が良く、Webアプリケーション開発における重要な機能の1つです。

参考(JavaSE)

for (Car car : cars) {
        System.out.println(car.getId(), 
           car.getName(), car.getPrice());
}

3.5 th:eachとth:ifの組み合わせ

例えば、名前が「tabuchi」の場合のみ表示する、というケースを考えてみます。

<ul>
    <li th:each="name : ${names}" th:if="${name == 'tabuchi'}" th:text="${name}">
        ここは表示されません(プレースホルダ)
    </li>
</ul>
<ul>
    <li>tabuchi</li>
</ul>

参考(JavaSE)

for (String name : names) {
     if ("tabuchi".equals(name)) {
           System.out.println(name);
     }
}

4. CSS・JavaScriptに変数の値を渡す

4.1 CSSに変数を渡す

CSSに変数を渡す最もシンプルな方法はThymeleafで以下のようにHTMLの属性として「th:style=」を設定する方法です。

<body th:style="'background-color: ' + ${bgColor}">

以前フォームのところで学んだ天気予報プログラムのプロトタイプを例にCSSに変数の値を渡す例を説明します。天気によって背景色を変えたいとします。晴れなら"yellow"、雨なら"blue"、曇りなら"gray"とします。

背景色が変わる天気予報プログラム

入力フォーム

    <a th:href="@{/weather(weather='晴れ')}">晴れ</a>
    <a th:href="@{/weather(weather='曇り')}">曇り</a>
    <a th:href="@{/weather(weather='雨')}">雨</a>

Controller

package com.example.demo.controller;

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

@Controller
public class WeatherController {

	@GetMapping("/weather")
	public String weather(String weather, Model model) {
		// 天気に応じて背景色を設定
		String backgroundColor;
		switch (weather) {
		case "曇り":
			backgroundColor = "gray";
			break;
		case "雨":
			backgroundColor = "blue";
			break;
		default: // 晴れ
			backgroundColor = "yellow";
			break;
		}

		// Modelに値をセット
		model.addAttribute("weather", weather);
		model.addAttribute("bgColor", backgroundColor);

		return "weather";
	}

	@GetMapping("/link")
	public String showLink() {
		return "link";
	}
}

HTML内で変数を埋め込む

<body th:style="'background-color: ' + ${bgColor}">
  今は <span th:text="${weather}"></span> なんですね。
</body>

4.2 JavaScriptに変数を渡す

JavaScriptのconfirm()メソッドを使ってログアウトの確認をする例で説明します。

confirm()メソッドを使ってログアウトの確認をする

Controller

    @GetMapping("/home")
    public String home(Model model) {
        String username = "yusei"; // ここでは仮のユーザー名
        model.addAttribute("username", username);
        return "home"; // home.html を表示
    }

    @GetMapping("/logout")
    public String logout() {
    	// ログアウト後、ホームへリダイレクト
        return "redirect:/home";
    }

サーバーから渡された値をJavaScriptで利用する(JavaScriptが分かる人向け)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<title>Home</title>
</head>
<body>
	<h1>ようこそ、<span th:text="${#strings.isEmpty(username)
	 ? 'ゲスト' : username}"></span> さん!</h1>

	<a href="#" onclick="confirmLogout()">ログアウト</a>

	<script th:inline="javascript">

		// Thymeleafから受け取ったusernameをJavaScriptの変数に代入。
		// 空またはnullの場合は 'ゲスト' を代入しておく。
		let username = /*[[${username} ?: 'ゲスト']]*/ 'ゲスト';

		// ログアウト確認ダイアログを表示する関数
		function confirmLogout() {
			let isConfirmed = confirm(username + 
			" さん、本当にログアウトしますか?");

			if (isConfirmed) {
				window.location.href = "/logout";
			}
		}
	</script>
</body>
</html>

?: は三項演算子に似ていますが、ThymeleafのElvis演算子です。(?:がエルビス・プレスリーの顔に見えるところからその名がある。)
${username} が存在すればその値を使い、存在しなければ 'ゲスト' を使います。

後ろの 'ゲスト' は、Thymeleafを通さずにHTMLを直接開いた場合でもJavaScriptとして動くようにするための仮の値です。

5. Thymeleaf を使った共通レイアウトの作成

Webアプリケーションを開発する際、各ページで同じヘッダーやフッターなどの共通パーツを毎回記述するのは非効率です。複数のWebページを統一したレイアウトにしたい場合、th:fragment を使うことで、ヘッダーやフッターなどの共通部分を別のテンプレートとして定義し、複数のページで再利用できます。

以下に、main.html(メインレイアウト)、header.html(ヘッダー)、footer.html(フッター)の3つのファイルを用いた実装方法を説明します。

※サンプルは「http://localhost:8080/replace」で実行できます。
※メニューの「会社概要」や「お問い合わせ」のリンク先は未作成です。

コントローラー

package com.example.demo.controller;

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

@Controller
public class ReplaceController {
	@GetMapping("/replace")
	public String sample() {
		// 今回はtemplatesフォルダに階層構造を持たせた例です。
		// Webフォルダを作成し、その中にmain.htmlを作りました。
		return "web/main";
	}
}

templatesフォルダがルートなので"web/main"でmain.htmlを指定しています。

ヘッダーファイル

<!-- src/main/resources/templates/web/header.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
    <header th:fragment="header">
        <h1>サイトのタイトル</h1>
        <nav>
            <ul>
                <li><a th:href="@{/}">ホーム</a></li>
                <li><a th:href="@{/about}">会社概要</a></li>
                <li><a th:href="@{/contact}">お問い合わせ</a></li>
            </ul>
        </nav>
    </header>
</body>
</html>

th:fragment="header" で、この header.htmlを再利用可能な部品(フラグメント)として定義しています。名前は"header"です。この名前は再利用する際に必要になります。英語の【fragment】には「断片」といった意味があります。

フッターファイル

<!-- src/main/resources/templates/web/footer.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
    <footer th:fragment="footer">
        <p>Copyright © Say consulting group, Inc 
         All Rights Reserved.</p>
    </footer>
</body>
</html>

ヘッダーと同様にth:fragment="footer" で、フッターを再利用可能な部品として定義しています。名前は”footer”です。

メインレイアウト

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>メインページ</title>
</head>
<body>
    <header th:replace="~{web/header :: header}"></header>
    
    <main>
        <p>ここにメインコンテンツが入ります。</p>
    </main>
    
    <footer th:replace="~{web/footer :: footer}"></footer>
</body>
</html>
  1. コロンの左側(web/header) 読み込みたい対象のファイルの場所を示しています。通常、Spring Bootでは、templatesというフォルダを基準として、その中にあるwebフォルダ内のheader.htmlというファイルを指します。拡張子の.htmlは省略するルールとなっています。
  2. コロンの記号(::) ファイル名と、そのファイルの中に定義されている特定の部品を区切るための記号です。
  3. コロンの右側(header) 呼び出す対象の部品の名前を示しています。対象のファイル(header.html)の中で、th:fragment="header"と名付けられた範囲だけを特定して呼び出します。

結合後のHTML


<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>メインページ</title>
</head>
<body>
    <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>
    
    <main>
        <p>ここにメインコンテンツが入ります。</p>
    </main>
    
    <footer>
        <p>Copyright © Say consulting group, Inc 
         All Rights Reserved.</p>
    </footer>
</body>
</html>

共通化を行うメリットとデメリット

この記述方法を採用することには、メリットとデメリットが存在します。

メリット

  • 修正作業を一元化でき: ウェブサイト全体のヘッダーのデザインやリンクを変更する際、共通ファイルであるheader.htmlを1つ修正するだけで、すべてのページに自動的に変更が反映されます。
  • 記述の重複を削減できる:同じHTMLコードを複数のファイルに何度も記述する必要がなくなるため、全体のコード量が減少し、ソースコードの見通しが良くなります。

例題

上記header.htmlに対応した各ページ(「会社概要」や「お問い合わせ」など)を作成し、header.htmlとfooter.htmlを組み込んで相互に遷移できるようにしなさい。

調べてみましょう

ThymeleafでCSSとJavaScriptをどこに書くべきか

CSSはmain.htmlのhead内で読み込みましょう。header.htmlはあくまでナビゲーションなどの見た目の断片であり、headタグを持つべきではありません。ページ全体のスタイルはレイアウトの起点であるmain.htmlで一元管理するのが自然です。th:hrefを使って記述することで、コンテキストパスを意識せず安全に参照できます。

JavaScriptはfooter.htmlの末尾、もしくはmain.htmlのbody閉じタグ直前が適切です。JSをHTMLより先に読み込むと、DOMの生成をブロックしてページ表示が遅くなる場合があります。footer.htmlにフッターのHTMLとあわせてscriptタグを置く構成は、役割がやや混在しますが、コンパクトな構成では現実的な選択肢です。迷う場合はmain.htmlのbody閉じタグ直前にまとめる方がシンプルで明快です。

共通ルールは「CSSは上、JavaScriptは下」。この原則を押さえておけば、どんな構成でも迷わず判断できます。


最後にThymeleafでよく使われるth:属性と、追加可能なHTML属性との対応表を作成しました。

当社新人研修でよく使用するth:*属性ベスト7

1位:th:text(テキストの出力)

th:textは、指定した要素の間に文字列を出力するための属性です。Thymeleafの中で最も基本的な属性であり、Javaから渡された変数の値を画面に表示する際に使用します。

比喩として、プリントの空欄に指定された文字を鉛筆で清書する作業が該当します。

コード例:

<p th:text="${userName}">ゲスト</p>

2位:th:each(繰り返しの処理)

th:eachは、リストなどの複数データがある場合に、その要素の数だけHTML要素を繰り返し生成する属性です。動的なページを作成する上で非常に頻度高く使用されます。

比喩として、クラスの名簿をもとにして、全員分の席札を同じ形式で自動的に印刷する作業に該当します。

コード例:

<ul>
  <li th:each="item : ${itemList}" th:text="${item.name}">商品名</li>
</ul>

3位:th:if(条件による表示)

th:ifは、指定した条件が正しい場合にのみ、そのHTML要素を表示する属性です。

ただし、条件式が複雑になると、HTML全体の視認性が低下し、構造が把握しにくくなります。その場合はJava側であらかじめ処理した結果をThymeleafに渡すほうが良いでしょう。

コード例:

<div th:if="${user.age >= 20}">お酒の販売ページはこちらです</div>

4位:th:href(リンクURLの設定)

th:hrefは、a要素などのリンク先URLを指定するための属性です。

アプリケーションの配置場所が変わっても、自動的に正しいURLへと補正してくれるというメリットがあります。

コード例:

<a th:href="@{/top}">トップページへ戻る</a>

5位:th:action(フォーム送信先の設定)

th:actionは、フォームのデータを送信する先のURLを指定する属性です。

th:hrefと同様に、Webアプリケーションのルートパスを自動で補完するため、サーバーの環境が変わっても送信先が壊れません。

コード例:

<form th:action="@{/register}" method="post">
</form>

6位:th:value(入力値の設定)

th:valueは、ボタンや入力フォームの初期値を設定するための属性です。

Java側で計算した結果やデータベースから取得した初期値を、フォームの部品に直接埋め込むことができます。

コード例:

<input type="submit" th:value="${buttonLabel}" />

7位:th:src(画像の参照設定)

th:srcは、img要素などの画像ファイルの読み込み先URLを指定するための属性です。以下のメリットがあります。

  • 商品データの識別番号などに応じて、表示する画像を動的に切り替えることができます。
  • アプリケーションの配置場所が変更されても、画像の保存先パスを自動的に補正してくれます。

コード例:

<img th:src="@{/images/{carId}.png(carId=${c.carId})}" />
補足情報

th:で始まる属性を複数同時に使うことも可能です。

<a th:href="@{/home}" th:text="'ようこそ ' + ${name} + ' さん'">
</a>


第6章の今回はHTMLに動きを与える方法について学びました。

第7章は「DTOとは?データの受け渡しを整理し、コードをシンプルにする」です。