Thymeleafのth:objectとth:fieldを使ったフォーム送信サンプル|新人エンジニア向けに解説
こんにちは。ゆうせいです。
今回は、次のThymeleafのフォームコードに、簡単なSpring Bootのサンプルコードと解説を追加します。
<form th:action="@{/update}" th:object="${user}" method="post">
<input type="text" th:field="*{name}" />
<input type="email" th:field="*{email}" />
<button type="submit">送信</button>
</form>このコードは、ユーザーの名前とメールアドレスを入力し、Spring BootのControllerへPOST送信するフォームです。
新人エンジニア向けに一言で言うと、th:objectは「このフォームで使うJavaオブジェクトを指定するもの」、th:fieldは「そのオブジェクトのどのプロパティと入力欄を結びつけるかを指定するもの」です。
たとえるなら、th:objectは記入する申込用紙そのもの、th:fieldは申込用紙の「名前欄」「メールアドレス欄」です。
全体の完成イメージ
今回作る流れは、次のとおりです。
| 順番 | 処理 | 役割 |
|---|---|---|
| 1 | ControllerでUserオブジェクトを作る | 画面に渡す入力用データを用意する |
| 2 | Modelにuserという名前で入れる | Thymeleafから${user}で使えるようにする |
| 3 | Thymeleafで入力フォームを表示する | nameとemailを入力できるようにする |
| 4 | 送信ボタンを押す | /updateへPOST送信する |
| 5 | ControllerでUserとして受け取る | 入力された値をJavaオブジェクトとして扱う |
Userクラス
まず、フォームの入力値を受け取るUserクラスを作ります。
今回は学習用なので、シンプルにnameとemailだけを持つクラスにします。
package com.example.demo.form;
public class User {
private String name;
private String email;
public User() {
}
public User(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}ここで重要なのは、getterとsetterです。
Thymeleafのth:field="*{name}"は、Java側のgetName()やsetName()と対応します。
同じように、th:field="*{email}"は、getEmail()やsetEmail()と対応します。
| Thymeleaf | 対応するJavaのメソッド |
|---|---|
| *{name} | getName()、setName() |
| *{email} | getEmail()、setEmail() |
もしgetName()やsetName()がないと、ThymeleafやSpring Bootがnameプロパティをうまく扱えないことがあります。
プロパティは、フィールドだけでなく、getterとsetterまでセットで考えましょう。
Controllerクラス
次に、画面を表示する処理と、フォーム送信後に値を受け取る処理を作ります。
package com.example.demo.controller;
import com.example.demo.form.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class UserController {
@GetMapping("/edit")
public String showEditForm(Model model) {
User user = new User();
user.setName("山崎太郎");
user.setEmail("yamazaki@example.com");
model.addAttribute("user", user);
return "user/edit";
}
@PostMapping("/update")
public String update(User user, Model model) {
System.out.println("送信された名前: " + user.getName());
System.out.println("送信されたメールアドレス: " + user.getEmail());
model.addAttribute("user", user);
model.addAttribute("message", "更新内容を受け取りました。");
return "user/result";
}
}このControllerには、2つのメソッドがあります。
| メソッド | URL | 役割 |
|---|---|---|
| showEditForm | /edit | 入力フォーム画面を表示する |
| update | /update | フォームから送信された値を受け取る |
showEditFormでは、Userオブジェクトを作って、model.addAttribute("user", user)で画面に渡しています。
ここで指定した"user"という名前が、HTML側のth:object="${user}"と対応します。
model.addAttribute("user", user);<form th:object="${user}">この名前がずれると、Thymeleafでエラーになります。
たとえば、Controllerでmodel.addAttribute("userForm", user)としているのに、HTMLでth:object="${user}"と書くと、userが見つかりません。
入力フォーム画面
次に、入力フォーム画面を作ります。
ファイルの場所は、Spring Bootの一般的な構成なら次のようになります。
src/main/resources/templates/user/edit.html
edit.htmlのサンプルです。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ユーザー編集</title>
</head>
<body>
<h1>ユーザー編集</h1>
<form th:action="@{/update}" th:object="${user}" method="post">
<div>
<label for="name">名前</label>
<input type="text" th:field="*{name}" id="name" />
</div>
<div>
<label for="email">メールアドレス</label>
<input type="email" th:field="*{email}" id="email" />
</div>
<button type="submit">送信</button>
</form>
</body>
</html>最初に示されたコードに、labelやHTML全体を追加した形です。
th:action="@{/update}"は、フォームの送信先URLを指定しています。
method="post"は、POST送信するという意味です。
th:object="${user}"は、このフォーム全体がuserオブジェクトと結びつくという意味です。
th:field="*{name}"は、userオブジェクトのnameプロパティと入力欄を結びつけます。
th:field="*{email}"は、userオブジェクトのemailプロパティと入力欄を結びつけます。
| コード | 意味 |
|---|---|
| th:action="@{/update}" | 送信先を/updateにする |
| th:object="${user}" | フォームで使うオブジェクトをuserにする |
| th:field="*{name}" | user.nameと入力欄を結びつける |
| th:field="*{email}" | user.emailと入力欄を結びつける |
| method="post" | POST送信する |
th:fieldが実際に作るHTML
th:fieldは、単にvalueを入れるだけではありません。
id、name、valueを自動的に整えてくれます。
たとえば、次のThymeleafコードがあるとします。
<input type="text" th:field="*{name}" id="name" />user.nameに「山崎太郎」が入っている場合、ブラウザに届くHTMLは次のようなイメージになります。
<input type="text" id="name" name="name" value="山崎太郎">つまり、th:field="*{name}"によって、name="name"とvalue="山崎太郎"が作られます。
このname="name"が、フォーム送信時のパラメータ名になります。
emailも同じです。
<input type="email" th:field="*{email}" id="email" />ブラウザに届くHTMLのイメージです。
<input type="email" id="email" name="email" value="yamazaki@example.com">フォーム送信時には、次のような値が送られます。
name=山崎太郎
email=yamazaki@example.com
Spring Bootは、このnameとemailを見て、UserオブジェクトのsetName()とsetEmail()に値を入れます。
送信後の結果画面
送信後に確認画面を表示するため、result.htmlを作ります。
ファイルの場所です。
src/main/resources/templates/user/result.html
result.htmlのサンプルです。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>更新結果</title>
</head>
<body>
<h1>更新結果</h1>
<p th:text="${message}"></p>
<table border="1">
<tr>
<th>名前</th>
<td th:text="${user.name}"></td>
</tr>
<tr>
<th>メールアドレス</th>
<td th:text="${user.email}"></td>
</tr>
</table>
<p>
<a th:href="@{/edit}">編集画面に戻る</a>
</p>
</body>
</html>この画面では、POST送信されたUserオブジェクトを再びModelに入れ、Thymeleafで表示しています。
model.addAttribute("user", user);
そのため、result.htmlでは次のように参照できます。
<td th:text="${user.name}"></td>
<td th:text="${user.email}"></td>
th:textは、画面に文字を表示するための属性です。
th:fieldは入力欄とJavaオブジェクトを結びつけるための属性です。
| 属性 | 役割 | よく使う場所 |
|---|---|---|
| th:field | 入力フォームとプロパティを結びつける | input、select、textarea |
| th:text | 値を画面に表示する | p、td、span、div |
動作の流れを確認しよう
今回のサンプルは、次の流れで動きます。
| 操作 | URL | 動き |
|---|---|---|
| 編集画面を開く | /edit | ControllerがUserを作って画面へ渡す |
| フォームを表示する | user/edit.html | th:fieldでnameとemailを入力欄へ表示する |
| 送信ボタンを押す | /update | POSTでnameとemailを送る |
| Controllerで受け取る | update(User user) | Spring BootがUserに値を詰める |
| 結果画面を表示する | user/result.html | 送信された値を表示する |
特に重要なのは、updateメソッドの引数です。
@PostMapping("/update")
public String update(User user, Model model) {
ここでは、@RequestParamを1つずつ書いていません。
Spring Bootが、リクエストパラメータのnameとemailを見て、Userオブジェクトに自動で詰めてくれます。
この仕組みを、フォームバインディングと呼びます。
バインディングとは、「結びつける」という意味です。
画面の入力欄とJavaオブジェクトのプロパティを結びつけるイメージです。
@ModelAttributeを明示して書く場合
updateメソッドは、次のように@ModelAttributeを付けて書くこともできます。
@PostMapping("/update")
public String update(@ModelAttribute User user, Model model) {
System.out.println("送信された名前: " + user.getName());
System.out.println("送信されたメールアドレス: " + user.getEmail());
model.addAttribute("user", user);
model.addAttribute("message", "更新内容を受け取りました。");
return "user/result";
}
@ModelAttributeは、リクエストパラメータをJavaオブジェクトに詰めるときによく使います。
新人エンジニアのうちは、フォームクラスやUserクラスで値を受け取る場合、@ModelAttributeを付けておくと意図がわかりやすくなります。
| 書き方 | 意味 |
|---|---|
| User user | Spring BootがUserに値を入れる |
| @ModelAttribute User user | リクエスト値をUserに入れることを明示する |
よくあるミス1:model.addAttributeの名前が違う
次のようなミスはとても多いです。
Controller側です。
model.addAttribute("userForm", user);
HTML側です。
<form th:object="${user}">
ControllerではuserFormという名前で渡しているのに、HTMLではuserという名前で受け取ろうとしています。
この場合、Thymeleafはuserを見つけられません。
正しくは、名前を合わせます。
model.addAttribute("user", user);
<form th:object="${user}">
または、次のようにします。
model.addAttribute("userForm", user);
<form th:object="${userForm}">
名前合わせは、Thymeleafフォームの基本です。
宅配便の宛名と受け取り人名が違うと届かないのと同じです。
よくあるミス2:Userクラスにgetterやsetterがない
次のようなUserクラスでは、うまく動かないことがあります。
public class User {
private String name;
private String email;
}
フィールドだけありますが、getterとsetterがありません。
Spring Bootがフォーム値をUserに詰めるには、setName()やsetEmail()が必要です。
Thymeleafが値を表示するには、getName()やgetEmail()が必要です。
正しくは、次のように書きます。
public class User {
private String name;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
フォームで使うクラスには、基本的にgetterとsetterを用意しましょう。
よくあるミス3:th:objectなしで*{}を使う
次のようなHTMLはエラーになりやすいです。
<form th:action="@{/update}" method="post">
<input type="text" th:field="*{name}" />
</form>
th:field="*{name}"の*{}は、th:objectで指定されたオブジェクトを前提にしています。
そのため、th:object="${user}"がないと、「どのオブジェクトのnameなのか」がわかりません。
正しくは次のようにします。
<form th:action="@{/update}" th:object="${user}" method="post">
<input type="text" th:field="*{name}" />
</form>
th:objectとth:fieldはセットで考えましょう。
よくあるミス4:プロパティ名が違う
UserクラスのプロパティがuserNameなのに、HTMLでnameと書いているケースです。
public class User {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
この場合、HTMLでは次のように書く必要があります。
<input type="text" th:field="*{userName}" />
次の書き方では、nameプロパティが存在しません。
<input type="text" th:field="*{name}" />
Thymeleafのプロパティ名は、Javaのgetterと対応します。
| Javaのgetter | Thymeleafの書き方 |
|---|---|
| getName() | *{name} |
| getEmail() | *{email} |
| getUserName() | *{userName} |
このフォームを一言で説明すると
最初のコードを、もう一度見てみましょう。
<form th:action="@{/update}" th:object="${user}" method="post">
<input type="text" th:field="*{name}" />
<input type="email" th:field="*{email}" />
<button type="submit">送信</button>
</form>
このフォームは、次の意味になります。
| コード | 新人向けの説明 |
|---|---|
| th:action="@{/update}" | 送信ボタンを押したら/updateへ送る |
| th:object="${user}" | このフォームはuserオブジェクトを使う |
| method="post" | POST方式で送信する |
| th:field="*{name}" | userのnameと入力欄を結びつける |
| th:field="*{email}" | userのemailと入力欄を結びつける |
| button type="submit" | フォームを送信するボタン |
つまり、このコードは「userというJavaオブジェクトのnameとemailを画面で編集し、/updateへ送信するフォーム」です。
まとめ
Thymeleafのth:objectとth:fieldを使うと、HTMLフォームとJavaオブジェクトを簡単に結びつけられます。
今回のサンプルでは、Userクラスのnameとemailを入力欄に表示し、送信後にControllerでUserとして受け取りました。
| 重要ポイント | 内容 |
|---|---|
| th:object | フォーム全体で使うオブジェクトを指定する |
| th:field | オブジェクトのプロパティと入力欄を結びつける |
| model.addAttribute | ControllerからThymeleafへデータを渡す |
| @PostMapping | フォーム送信後の処理を受け取る |
| @ModelAttribute | フォーム値をJavaオブジェクトに詰めることを明示する |
新人エンジニアは、まず次の対応関係を覚えてください。
model.addAttribute("user", user)
↓
th:object="${user}"
↓
th:field="*{name}"
↓
UserクラスのgetName()、setName()
Thymeleafフォームでエラーが出たときは、「Model名」「th:objectの名前」「th:fieldのプロパティ名」「Javaクラスのgetterとsetter」を順番に確認しましょう。
今後の学習では、@ModelAttribute、バリデーション、BindingResult、入力エラー時の再表示、DB更新処理の流れを順番に学ぶとよいです。まずは、画面の入力欄とJavaオブジェクトがどう結びついているのかを、手を動かして確認してください!
セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。
投稿者プロフィール

- 代表取締役
-
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。
学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。

