Thymeleafのth:objectの使い方を完全ガイド!初心者でもわかるフォームデータの連携
生徒
「Thymeleafでフォームとサーバー側のデータを連携させる方法ってありますか?」
先生
「はい、Thymeleafではth:objectという属性を使って、フォーム全体をサーバーサイドのデータオブジェクトと簡単に連携させることができます。」
生徒
「具体的にはどのように使うんですか?」
先生
「それでは、th:objectの使い方を順を追って見ていきましょう!」
1. th:objectとは?
th:objectは、Thymeleafでフォームとサーバー側のデータを結びつけるための“フォームの代表者”のような存在です。
フォームタグにこの属性を設定すると、その中にある複数の入力項目を一つのJavaオブジェクトへまとめて受け渡すことができ、
Spring MVCのコントローラーとスムーズに連携できます。
初心者の方だと「入力項目ごとに値を取得するの?」と思いがちですが、th:objectを使うと、
名前やメールアドレスなど複数の項目を、ひとまとまりのデータオブジェクトとして扱えるようになります。
つまり、フォーム送信時に自動でプロパティへバインド(紐づけ)してくれるのです。
例えば、以下のようにユーザー情報を入力するフォームがあったとします。
<form th:action="@{/submit}" th:object="${user}" method="post">
<input type="text" th:field="*{name}" placeholder="名前を入力" />
<input type="email" th:field="*{email}" placeholder="メールアドレスを入力" />
<button type="submit">送信</button>
</form>
th:object="${user}"を指定しているので、このフォーム内で入力された値は、
自動的にuserオブジェクト(Javaのクラス)へ割り当てられます。
特別な処理を書かなくても、nameやemailの値がサーバー側で使えるようになるため、
入力フォームを扱うプログラムがとてもシンプルになります。
2. th:objectの基本的な使い方
th:objectを使う場合は、まずフォームタグにth:object属性を設定します。
これを設定すると、フォーム全体で扱うデータの“受け皿”が決まり、入力された値が自動でそのオブジェクトへ入っていきます。
つまり「このフォームはこのデータを扱いますよ」と宣言するイメージです。
例えば、名前とメールアドレスを入力して送信するだけの、とてもシンプルなフォームを見てみましょう。 プログラミング初心者でも理解しやすい構成になっているので安心してください。
<form th:action="@{/submit}" th:object="${user}" method="post">
<label for="name">名前:</label>
<input type="text" id="name" th:field="*{name}" placeholder="山田 太郎" />
<label for="email">メールアドレス:</label>
<input type="email" id="email" th:field="*{email}" placeholder="taro@example.com" />
<button type="submit">送信</button>
</form>
th:object="${user}"と書くことで、フォームの入力内容はすべてuserというオブジェクトにまとめられます。
そして、th:field="*{name}"のように書くと、入力された値が自動でuser.nameにセットされる仕組みです。
特別なJavaコードを書かなくても値が関連付けられるため、フォーム画面の作成がぐっと楽になります。
また、フォームタグの中に入力項目が増えても、ひとつのth:objectでまとめて扱えるため、
データの受け渡しミスが起こりづらく、後から項目を増やすときも整理しやすくなります。
3. th:objectを使うメリット
th:objectを使う一番のメリットは、フォームとサーバー側のオブジェクトが“自動でつながる”点です。
テンプレートの中で入力した値が、そのままJavaのプロパティへ入り、送信後も同じ名前でアクセスできるため、
コードの見通しがよくなります。特に、複数の入力欄があるフォームでは、この仕組みがとても役立ちます。
- テンプレートの保守性が上がり、HTMLが読みやすくなる。
- Spring MVCのバリデーション機能と自然に連携できる。
- 既存データの表示や初期値の反映が簡単にできる。
例えば、「名前」と「メールアドレス」を初期表示したい場合、コントローラー側でオブジェクトを用意し、ビューに返すだけです。 フォーム側では特別な設定を書かなくても、自動で初期値が入力欄に反映されます。
// コントローラー側(初心者でも使いやすい例)
@GetMapping("/entry")
public String showForm(@ModelAttribute("user") UserForm form) {
form.setName("山田 太郎");
form.setEmail("taro@example.com");
return "user/entry";
}
これだけで、テンプレート側にあるth:field="*{name}"やth:field="*{email}"へ自動反映されます。
開発者が値の受け渡しを手作業で行う必要がなく、プログラムの書き忘れやミスを減らせるのが大きなポイントです。
4. th:objectを使ったフォームバリデーション
Spring MVCとThymeleafを組み合わせることで、フォームバリデーションを簡単に実現できます。 例えば、入力必須の項目にエラーメッセージを表示する例を以下に示します。
<form th:action="@{/submit}" th:object="${user}" method="post">
<label for="name">名前:</label>
<input type="text" id="name" th:field="*{name}" />
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
<label for="email">メールアドレス:</label>
<input type="email" id="email" th:field="*{email}" />
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>
<button type="submit">送信</button>
</form>
この例では、th:errorsを使って、エラーが発生したフィールドに対応するエラーメッセージを表示しています。
これにより、ユーザーがどの項目でエラーが発生したかを簡単に確認できます。
5. th:objectを使用する際の注意点
th:objectを使う際には、以下の点に注意してください。
- サーバーサイドで正しいオブジェクトがコントローラーから渡されていることを確認する。
- オブジェクトのプロパティ名と
th:fieldの指定が一致していること。 - フォーム送信時に適切なエラーハンドリングを実装すること。
これらを考慮することで、th:objectを使ったフォームの動作を正確に管理できます。
6. ネストしたオブジェクトとコレクションのバインド(住所・電話番号など)
th:objectは、ネストしたプロパティ(例:address.city)やリスト型(例:phones)にも対応できます。
住所や電話番号のように階層構造や複数行の入力を扱うときは、プロパティパスとインデックスを正しく指定します。
<form th:action="@{/users/save}" th:object="${user}" method="post">
<!-- ネストしたオブジェクト -->
<div class="mb-3">
<label>郵便番号</label>
<input type="text" th:field="*{address.postalCode}" />
</div>
<div class="mb-3">
<label>都道府県</label>
<input type="text" th:field="*{address.prefecture}" />
</div>
<div class="mb-3">
<label>市区町村</label>
<input type="text" th:field="*{address.city}" />
</div>
<!-- コレクション(phones) -->
<div th:each="ph, stat : *{phones}" class="mb-3">
<label>電話番号 [[${stat.index}+1]] 件目</label>
<!-- インデックスを使ってコレクション要素にバインド -->
<input type="text" th:field="*{phones[__${stat.index}__].number}" placeholder="090-xxxx-xxxx" />
<select th:field="*{phones[__${stat.index}__].type}">
<option value="HOME">自宅</option>
<option value="MOBILE">携帯</option>
<option value="WORK">勤務先</option>
</select>
</div>
<button type="submit" class="btn btn-primary">保存</button>
</form>
- ネストはドット記法(
*{address.city})で指定。 - リストはインデックス(
*{phones[__${stat.index}__].number})で要素を特定。 - 動的に行を追加する場合も、同じインデックス規則を守ると確実にバインドされます。
7. @ModelAttributeとth:objectの連携(コントローラー側の基本)
フォーム表示(GET)と送信(POST)で同じ属性名を使うのがポイントです。@ModelAttribute("user")の名前と、
テンプレート側のth:object="${user}"を揃えると、双方向のデータ連携が自然に機能します。
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("/new")
public String showForm(@ModelAttribute("user") UserForm form) {
// 初期値(例:電話番号1件だけ用意)
form.getPhones().add(new PhoneForm());
return "users/form"; // Thymeleafテンプレート
}
@PostMapping("/save")
public String save(
@Valid @ModelAttribute("user") UserForm form,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "users/form"; // エラー時は同じテンプレートへ戻す
}
// TODO: 保存処理
return "redirect:/users/complete";
}
}
@ModelAttribute("user")とth:object="${user}"の名称一致が重要。- バリデーションは
@ValidとBindingResultで受け取り、テンプレートの#fieldsと連携。 - 初期表示時にコレクション要素を用意しておくと、空のフォームが描画しやすくなります。
8. 既存データの編集フォーム(初期値表示・IDの保持)
編集フォームでは、th:fieldが自動で初期値を反映します。識別子(ID)はhiddenで保持し、
CSRF対策やメソッド、アクションはアプリの規約に合わせて設定します。
<form th:action="@{/users/update}" th:object="${user}" method="post">
<input type="hidden" th:field="*{id}" />
<div class="mb-3">
<label>氏名</label>
<input type="text" th:field="*{name}" />
</div>
<div class="mb-3">
<label>メール</label>
<input type="email" th:field="*{email}" />
</div>
<!-- 既存の住所・電話もそのまま表示される -->
<div class="mb-3">
<label>市区町村</label>
<input type="text" th:field="*{address.city}" />
</div>
<button type="submit" class="btn btn-success">更新</button>
</form>
th:fieldは既存値を自動反映(th:valueの併用は不要)。- IDは
<input type="hidden" th:field="*{id}">で送信時に失われないよう保持。 - 表示専用の値は
th:text、編集可能な値はth:fieldを使い分け。
9. セレクト・ラジオ・チェックボックス・ファイルとの連携
選択系やファイル入力もth:objectと相性抜群です。選択肢のリストをモデルに用意しておけば、値と表示名を分けて安全にバインドできます。
<form th:action="@{/profile/save}" th:object="${user}" method="post" enctype="multipart/form-data">
<!-- セレクト(単一) -->
<div class="mb-3">
<label>都道府県</label>
<select th:field="*{prefecture}">
<option value="" disabled>選択してください</option>
<option th:each="p : ${prefectureList}"
th:value="${p.code}"
th:text="${p.label}"></option>
</select>
</div>
<!-- ラジオ(性別など) -->
<div class="mb-3" th:each="g : ${genders}">
<label>
<input type="radio" th:field="*{gender}" th:value="${g.code}" />
<span th:text="${g.label}"></span>
</label>
</div>
<!-- チェックボックス(複数ロール) -->
<div class="mb-3" th:each="r : ${roleOptions}">
<label>
<input type="checkbox" th:field="*{roles}" th:value="${r}" />
<span th:text="${r}"></span>
</label>
</div>
<!-- 複数選択セレクト -->
<div class="mb-3">
<label>興味のある分野</label>
<select multiple th:field="*{interests}">
<option th:each="it : ${interestOptions}"
th:value="${it}"
th:text="${it}"></option>
</select>
</div>
<!-- ファイルアップロード(MultipartFile avatar) -->
<div class="mb-3">
<label>プロフィール画像</label>
<input type="file" th:field="*{avatar}" />
</div>
<button type="submit" class="btn btn-primary">登録</button>
</form>
- 選択肢は「値(
code)と表示名(label)」を分離してモデルに用意。 - 複数選択はコレクション型プロパティ(
List<String>など)にバインド。 - ファイルは
enctype="multipart/form-data"を忘れず、サーバー側でMultipartFileを受け取ります。
まとめ
本記事では、Thymeleafのth:object属性について詳しく解説しました。この属性は、フォーム全体をサーバーサイドのモデルオブジェクトと連携させるための重要な機能です。
th:objectを使うことで、フォーム入力データとバックエンドのプロパティを効率的に紐づけることができます。また、th:fieldと組み合わせることで、個々の入力要素のバインディングが簡単に実現します。
特に、フォームバリデーションの実装やエラーメッセージの表示がスムーズになるため、ユーザー体験を向上させるフォーム作成に役立ちます。 以下に、もう一度基本的なフォーム例を掲載しますので、ぜひ実際に試してみてください。
<form th:action="@{/submit}" th:object="${user}" method="post">
<label for="username">ユーザー名:</label>
<input type="text" id="username" th:field="*{username}" />
<label for="email">メールアドレス:</label>
<input type="email" id="email" th:field="*{email}" />
<button type="submit">登録</button>
</form>
このフォームでは、usernameとemailのデータがサーバーサイドのuserオブジェクトにバインドされます。適切にコントローラーを設定することで、入力値を受け取ることができます。
生徒
「th:objectを使うと、フォームとバックエンドの連携がすごく簡単になりますね!」
先生
「その通りです。th:objectを活用すれば、データバインディングが効率的になりますし、テンプレートの保守性も向上しますよ。」
生徒
「フォームバリデーションも簡単に実装できるのが良いですね。これからの開発でどんどん使ってみたいです!」
先生
「ぜひ取り入れてみてください。次は、さらに高度なフォーム機能も試してみるといいですね。」
この記事を読んだ人からの質問
プログラミング初心者からのよくある疑問/質問を解決します
Thymeleafのth:objectとは何ですか?
Thymeleafのth:objectは、フォーム全体をサーバーサイドのオブジェクトと連携させるための属性です。これにより、フォームの入力データを簡単にサーバーに渡すことができます。
フォームにth:objectを使う具体的な手順を教えてください。
フォームタグにth:objectを指定し、連携するモデルオブジェクトを指定します。そして、各入力要素にth:fieldを使ってプロパティをバインドします。
th:fieldとth:objectの関係は何ですか?
th:fieldはth:objectで指定されたオブジェクトのプロパティをフォーム要素と紐付ける役割を持っています。
Thymeleafでフォームの初期値を設定するにはどうすればいいですか?
フォームの初期値は、コントローラー側でオブジェクトにプロパティを設定し、そのオブジェクトをth:objectでバインドすることで実現できます。
フォームバリデーションのエラーメッセージを表示するにはどうすればいいですか?
th:errors属性を使うと、バリデーションでエラーが発生したフィールドに対応するエラーメッセージを表示できます。
Thymeleafのth:objectを使うメリットは何ですか?
th:objectを使うと、テンプレートとサーバーサイドの連携が簡単になり、データバインディングやフォームバリデーションが効率的に行えるようになります。
フォーム送信時にサーバー側でエラーが発生した場合、どう対応すればいいですか?
エラーが発生した場合は、BindingResultを使ってエラーメッセージをビューに渡し、th:ifやth:errorsを使って表示できます。
ThymeleafとSpring MVCを組み合わせると何が便利ですか?
ThymeleafとSpring MVCを組み合わせると、データの入力やバリデーション、エラーメッセージの表示を簡単に行えるため、開発効率が向上します。
テンプレートエンジンとしてThymeleafを選ぶ理由は何ですか?
ThymeleafはHTMLテンプレートをそのまま使用できるため、デザイナーと開発者の間で作業がスムーズに進みます。また、動的なデータバインディングが可能で、Springと相性が良いのも特徴です。
Thymeleafのフォームでサーバーサイドのオブジェクトをどのように受け取りますか?
フォーム送信後、サーバーサイドのコントローラーで@ModelAttributeを使用してオブジェクトを受け取ります。
ネストしたプロパティ(例:address.city)がnullのときにテンプレートでエラーを避ける方法は?
コントローラーで子オブジェクト(address)を必ず初期化してからモデルに渡します。描画側では th:if="*{address} != null" でガードするのも有効です。
リストの行を削除してインデックスが飛んだ場合でもバインドできますか?
Springのバインドは連番(0,1,2…)を前提とします。送信前にJavaScriptで行を詰め直して再採番するか、描画時に stat.index を用いて連続したインデックスでname属性が出力されるようにします。
Map型のプロパティをフォームにバインドするには?
*{attributes['color']} のようにキーを指定します。一覧表示は th:each="e : *{attributes}" として e.key / e.value を使います。
リスト要素ごとのバリデーションエラーはどう表示しますか?
フォームクラスで @Valid な要素型(例:List<@Valid PhoneForm>)にし、テンプレートでは th:errors="*{phones[__${stat.index}__].number}" のように各要素へ紐付けます。
ChatGPTなどの生成AIをこれから学びたい方に向けて、AIスキルアカデミーの口コミ・評判、料金、無料セミナーの内容を詳しくまとめています。
AIスキルアカデミーの口コミ・評判を詳しく見る