カテゴリ: Thymeleaf 更新日: 2026/02/20

Thymeleaf th:fieldの基本と便利な使い方を初心者向けに解説!

Thymeleaf th:fieldの基本と便利な使い方
Thymeleaf th:fieldの基本と便利な使い方

新人と先輩の会話形式で理解しよう

新人

「SpringでHTMLフォームを作りたいんですが、th:fieldって何のために使うんですか?」

先輩

「それはThymeleafでフォーム入力値をJavaのオブジェクトと自動でバインドするための属性だよ。とても便利なんだ。」

新人

「Javaオブジェクトに自動で? どんな風に書けばいいんですか?」

先輩

「じゃあ、HTMLフォームとth:fieldの基本から順番に説明するよ。」

1. HTMLフォームの基本とinput要素

1. HTMLフォームの基本とinput要素
1. HTMLフォームの基本とinput要素

まずはSpringとは関係なく、通常のHTMLでフォームを作る場合の基本構造を見てみましょう。以下はユーザー名とパスワードを入力するフォームの例です。


<form action="/login" method="post">
    <input type="text" name="username">
    <input type="password" name="password">
    <button type="submit">ログイン</button>
</form>

このようにinputタグのname属性を使って、サーバー側にデータを送信します。ただし、Springでフォームバインドを使いたい場合は、これだけでは不十分です。コントローラ側で受け取るモデルオブジェクトと連携するためには、Thymeleafのth:objectth:fieldを使う必要があります。

2. Thymeleafのth:fieldとは?(フォームとの連携)

2. Thymeleafのth:fieldとは?(フォームとの連携)
2. Thymeleafのth:fieldとは?(フォームとの連携)

Thymeleafにおけるth:field属性は、Spring MVCのフォームバインド機能と連携して、HTMLフォームの各フィールドをJavaのオブジェクトと結びつけるために使います。

例えば、以下のようなUserFormクラスがあるとします。


public class UserForm {
    private String username;
    private String password;

    // getterとsetterは省略
}

このUserFormを使って、コントローラでは以下のようにモデルに登録します。


@Controller
public class LoginController {

    @GetMapping("/login")
    public String showLoginForm(Model model) {
        model.addAttribute("userForm", new UserForm());
        return "login";
    }

    @PostMapping("/login")
    public String submitLogin(@ModelAttribute UserForm userForm) {
        // ログイン処理
        return "result";
    }
}

この場合、Thymeleafのテンプレートでは次のようにth:objectth:fieldを使います。


<form th:action="@{/login}" th:object="${userForm}" method="post">
    <input type="text" th:field="*{username}" />
    <input type="password" th:field="*{password}" />
    <button type="submit">ログイン</button>
</form>

th:objectでバインド対象のオブジェクトを指定し、th:fieldでオブジェクトのフィールド名を指定します。*{username}と書くことで、userForm.getUsername()のように連携してくれるのです。

初心者が混乱しやすいポイントとして、th:fieldを使うと、自動的にname属性やid属性も自動で生成されます。そのため、手動でnameを指定する必要はありません。

補足:HTMLに出力される実際のコード

上記のth:fieldを使ったフォームは、ブラウザに表示されると以下のようなHTMLに変換されます。


<form action="/login" method="post">
    <input type="text" id="username" name="username" value="">
    <input type="password" id="password" name="password" value="">
    <button type="submit">ログイン</button>
</form>

このように、th:fieldを使うことで、idnamevalueなどの属性を自動でバインドしてくれるため、記述ミスを防げて非常に便利です。

ラジオボタンやチェックボックスの場合

th:fieldは、テキスト入力だけでなく、ラジオボタンやチェックボックスにも使えます。


<label><input type="radio" th:field="*{gender}" value="male"> 男性</label>
<label><input type="radio" th:field="*{gender}" value="female"> 女性</label>

<label><input type="checkbox" th:field="*{agree}"> 利用規約に同意する</label>

ラジオボタンでは同じフィールド名に対して異なるvalueを持たせることで、1つだけ選択されるようになります。チェックボックスではboolean型のフィールドと連携することで、オン・オフの状態を管理できます。

select要素(プルダウン)との連携

selectタグにもth:fieldを使うことができます。以下は都道府県の一覧を選択肢として表示する例です。


<select th:field="*{prefecture}">
    <option value="tokyo">東京都</option>
    <option value="osaka">大阪府</option>
    <option value="hokkaido">北海道</option>
</select>

コントローラ側でuserForm.setPrefecture("osaka")のようにしておけば、「大阪府」が初期選択されます。

3. th:fieldを使ったバインドの具体例(オブジェクトとの連携)

3. th:fieldを使ったバインドの具体例(オブジェクトとの連携)
3. th:fieldを使ったバインドの具体例(オブジェクトとの連携)

ここでは、Thymeleafのth:fieldを使って、複数のフィールドを含むフォーム入力をオブジェクトにバインドする具体的な例を紹介します。初心者向けに、エンティティクラスとテンプレート、そしてコントローラの流れを丁寧に見ていきましょう。

まずはユーザー登録用のフォームオブジェクトを定義します。


public class RegisterForm {
    private String name;
    private String email;
    private int age;
    private String password;

    // getterとsetterは省略
}

次にコントローラ側でこのフォームをモデルに追加します。


@Controller
public class RegisterController {

    @GetMapping("/register")
    public String showForm(Model model) {
        model.addAttribute("registerForm", new RegisterForm());
        return "register";
    }

    @PostMapping("/register")
    public String submitForm(@ModelAttribute RegisterForm registerForm) {
        // 登録処理を行う
        return "result";
    }
}

そして、Thymeleafテンプレートでは以下のようにth:objectth:fieldを活用して、オブジェクトとフォーム入力を連携させます。


<form th:action="@{/register}" th:object="${registerForm}" method="post">
    <label>名前: <input type="text" th:field="*{name}"></label><br>
    <label>メール: <input type="email" th:field="*{email}"></label><br>
    <label>年齢: <input type="number" th:field="*{age}"></label><br>
    <label>パスワード: <input type="password" th:field="*{password}"></label><br>
    <button type="submit">登録</button>
</form>

このようにth:fieldを使用することで、オブジェクトのフィールドとフォームの値が自動的に結びつきます。初心者が混乱しやすい点としては、th:objectとセットで使わないとth:fieldが正しく動作しないことがあります。この点に注意してください。

4. 入力値の保持とエラーメッセージの表示

4. 入力値の保持とエラーメッセージの表示
4. 入力値の保持とエラーメッセージの表示

フォームで入力された値にエラーがあった場合、そのまま再表示して入力値を保持したり、エラーメッセージを表示するのもThymeleafでは簡単に行えます。ここでは@ValidBindingResultを使った例を紹介します。

まず、フォームクラスにバリデーションアノテーションを追加します。


public class RegisterForm {

    @NotBlank(message = "名前を入力してください")
    private String name;

    @Email(message = "正しいメールアドレスを入力してください")
    private String email;

    @Min(value = 18, message = "18歳以上で登録できます")
    private int age;

    @Size(min = 6, message = "パスワードは6文字以上で入力してください")
    private String password;

    // getterとsetterは省略
}

コントローラでは次のように@ValidBindingResultをセットで使います。


@PostMapping("/register")
public String submitForm(@Valid @ModelAttribute RegisterForm registerForm, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return "register";
    }
    // 成功時の処理
    return "result";
}

Thymeleafテンプレートでエラーを表示するには、th:errorsを使います。以下のようにフィールドごとにエラーを表示できます。


<form th:action="@{/register}" th:object="${registerForm}" method="post">
    <label>名前: <input type="text" th:field="*{name}"></label>
    <div th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>

    <label>メール: <input type="email" th:field="*{email}"></label>
    <div th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>

    <label>年齢: <input type="number" th:field="*{age}"></label>
    <div th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></div>

    <label>パスワード: <input type="password" th:field="*{password}"></label>
    <div th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></div>

    <button type="submit">登録</button>
</form>

このようにすると、フォーム入力時にバリデーションエラーがあった場合でも、入力値は保持され、該当フィールドの直下にエラーメッセージが表示されます。初心者が間違いやすい点としては、BindingResultの位置が@ModelAttributeの直後でないと無効になることです。注意しましょう。

5. th:fieldで扱えるフォーム部品(input、textarea、selectなど)

5. th:fieldで扱えるフォーム部品(input、textarea、selectなど)
5. th:fieldで扱えるフォーム部品(input、textarea、selectなど)

Thymeleafのth:field属性は、さまざまな種類のフォーム部品で使うことができます。ここでは初心者向けに、よく使われる代表的なフォーム要素との組み合わせを紹介します。

テキストエリア(textarea

複数行のテキスト入力にはtextareaを使いますが、Thymeleafでもth:fieldを使ってバインドできます。


<label>自己紹介:</label>
<textarea th:field="*{introduction}"></textarea>

これにより、入力された内容がJavaのオブジェクトと連携され、再表示時にも内容が保持されます。

セレクトボックス(select

select要素とth:fieldを組み合わせることで、リストから選択された値を自動でバインドできます。


<select th:field="*{job}">
    <option value="engineer">エンジニア</option>
    <option value="designer">デザイナー</option>
    <option value="manager">マネージャー</option>
</select>

選択肢の値は、Javaオブジェクトのフィールドに対応する値として渡されます。

チェックボックスとラジオボタン

チェックボックスとラジオボタンも、th:fieldで簡単に使えます。


<label><input type="checkbox" th:field="*{subscribe}"> メール配信を希望する</label>

<label><input type="radio" th:field="*{gender}" value="male"> 男性</label>
<label><input type="radio" th:field="*{gender}" value="female"> 女性</label>

チェックボックスの場合、バインド先はboolean型のフィールドとなり、チェックの有無で値が変わります。ラジオボタンは同じth:fieldで複数の選択肢を表示する形式になります。

このように、th:fieldはフォーム入力に必要な主要な部品すべてに対応しており、初心者でも簡単にオブジェクトとのデータ連携を実現できます。

6. よくあるバインドエラーとth:fieldの注意点(nullや型不一致など)

6. よくあるバインドエラーとth:fieldの注意点(nullや型不一致など)
6. よくあるバインドエラーとth:fieldの注意点(nullや型不一致など)

初心者がThymeleafのth:fieldを使ってフォームバインドを行うとき、最もつまずきやすいのが「バインド エラー」です。特に、nullの取り扱いや型の不一致によるエラーは頻出で、エラー画面に赤いスタックトレースが表示されて驚いてしまうこともあります。

たとえば、フォームクラスで次のような定義をしているとします。


public class UserForm {
    private int age;
    // getter/setter省略
}

このとき、ageが数値ではなく空欄のまま送信された場合、Springはint型にnullを入れることができず、バインド エラーになります。これを避けるには、ラッパークラスであるInteger型を使うようにしましょう。


private Integer age;

また、フォームの値を受け取るときに、Thymeleafテンプレート上で指定しているフィールド名が実際のJavaクラスに存在しない場合も、th:fieldは正しく機能せず、空のフォームや例外の原因になります。

このようなケースに備えて、th:fieldを記述する際は、バインド対象のフィールド名とJava側のプロパティ名が一致しているかラッパークラスを使ってnull対応しているかを常に確認する習慣をつけておきましょう。

7. 実務で役立つth:fieldの応用例(リストやネスト構造への対応)

7. 実務で役立つth:fieldの応用例(リストやネスト構造への対応)
7. 実務で役立つth:fieldの応用例(リストやネスト構造への対応)

実際の業務では、単純な文字列や数値の入力だけでなく、「リスト形式の入力」や「ネストしたオブジェクト」へのフォームバインドが求められるケースがよくあります。Thymeleafのth:fieldは、こうした複雑な構造にも対応できます。

リスト構造とのバインド

たとえば、複数の電話番号を入力するような場面を考えてみましょう。


public class ContactForm {
    private List<String> phones;
    // getter/setter省略
}

このとき、Thymeleafテンプレートではインデックス番号を使ってth:fieldを指定します。


<form th:action="@{/contact}" th:object="${contactForm}" method="post">
    <input type="text" th:field="*{phones[0]}" placeholder="電話番号1"><br>
    <input type="text" th:field="*{phones[1]}" placeholder="電話番号2"><br>
    <button type="submit">送信</button>
</form>

このように書くことで、phonesリストの0番目、1番目の要素にそれぞれバインドされます。フォーム側で空のリストが送られてくることもあるので、コントローラでは初期化しておくと安全です。


model.addAttribute("contactForm", new ContactForm(List.of("", "")));

ネストしたオブジェクトへのバインド

次に、親オブジェクトの中に別のオブジェクトを持つ「ネスト構造」の例を見てみましょう。


public class EmployeeForm {
    private String name;
    private Department department;
    // getter/setter省略
}

public class Department {
    private String code;
    private String name;
    // getter/setter省略
}

このような場合、Thymeleafではドット記法でth:fieldを指定します。


<form th:action="@{/employee}" th:object="${employeeForm}" method="post">
    <label>社員名: <input type="text" th:field="*{name}"></label><br>
    <label>部署コード: <input type="text" th:field="*{department.code}"></label><br>
    <label>部署名: <input type="text" th:field="*{department.name}"></label><br>
    <button type="submit">登録</button>
</form>

このように、th:fieldはリストやネスト構造にも柔軟に対応できるため、実務でも非常に役立ちます。初心者が注意すべき点は、ネストしたオブジェクトのインスタンスがnullのままだとバインド時に例外が発生するため、コントローラで必ず初期化しておく必要があることです。

8. フォーム送信とコントローラの連携(POSTの受け取り、ModelAttributeなど)

8. フォーム送信とコントローラの連携(POSTの受け取り、ModelAttributeなど)
8. フォーム送信とコントローラの連携(POSTの受け取り、ModelAttributeなど)

最後に、フォーム送信後にth:fieldでバインドされた値を、Springの@Controllerクラスでどのように受け取るかについて説明します。フォームとコントローラの連携は、初心者がつまずきやすいポイントなので、順を追って丁寧に解説します。

まず、Thymeleaf側のフォームにはth:actionmethod="post"を設定し、th:objectでバインド対象のオブジェクトを指定します。


<form th:action="@{/submit}" th:object="${userForm}" method="post">
    <input type="text" th:field="*{username}">
    <input type="password" th:field="*{password}">
    <button type="submit">送信</button>
</form>

次に、コントローラ側では@PostMappingで送信先を受け取り、@ModelAttributeを使ってフォームオブジェクトをバインドします。


@Controller
public class UserController {

    @PostMapping("/submit")
    public String handleForm(@ModelAttribute UserForm userForm) {
        // userForm.getUsername()やgetPassword()で値を取得できる
        return "result";
    }
}

ここで重要なのが、Thymeleafのテンプレートで使っているモデル名(例:userForm)と、コントローラで受け取る@ModelAttributeの型が一致していることです。

さらに、バリデーションを使う場合は@ValidBindingResultを組み合わせることで、エラーメッセージの表示や再描画にも対応できます。


@PostMapping("/submit")
public String handleForm(@Valid @ModelAttribute UserForm userForm, BindingResult result) {
    if (result.hasErrors()) {
        return "form"; // エラーがある場合は元のフォームに戻る
    }
    return "result"; // 正常なら次の画面へ
}

このように、Thymeleafのth:fieldでフォーム入力とJavaオブジェクトをバインドし、コントローラの@ModelAttributeで値を受け取る流れは、Spring MVCの基本的な連携パターンです。

初心者が間違えやすいのは、th:objectを使わずにth:fieldだけを使ってしまい、意図したバインドがされないというケースです。正しくオブジェクトと連携させるためにも、テンプレート・モデル・コントローラの対応を意識しておきましょう。

まとめ

まとめ
まとめ

ここまで、Spring Bootの開発において欠かせないテンプレートエンジン「Thymeleaf」の核心機能であるth:fieldについて、その基本構造から実務的な応用テクニックまで詳しく解説してきました。 Webアプリケーションの構築において、フロントエンドのフォーム画面とバックエンドのJavaオブジェクトをいかにスムーズに連携させるかは、開発効率や保守性に直結する非常に重要なポイントです。

th:fieldを使う最大のメリットは、HTMLのidnamevalueといった属性を自動的に生成・制御してくれる点にあります。 これにより、開発者が手動で属性値を記述する手間が省けるだけでなく、タイポによるバグを未然に防ぎ、フォームの値をJava側のモデル(Formオブジェクト)へ確実にマッピングすることが可能になります。

th:fieldの役割とメリットの再確認

改めてth:fieldの主な役割を整理すると、以下の3点に集約されます。

  • 双方向のデータ連携: 画面からの入力値をJavaオブジェクトへ渡すだけでなく、Java側で保持している初期値を自動的にフォームへ反映します。
  • 属性の自動生成: idnameをJavaのフィールド名に基づいて自動生成するため、Spring MVCの@ModelAttributeとの親和性が極めて高いです。
  • バリデーションとの連携: 入力エラーが発生した際に、直前の入力値を保持しつつエラーメッセージを表示する仕組みを容易に構築できます。

実践的なサンプルコード:ユーザー情報の統合編集フォーム

最後に、今回学んだ内容を盛り込んだ実践的なコードを振り返ってみましょう。 ここでは、テキスト入力、セレクトボックス、チェックボックスを組み合わせたユーザー情報の登録フォームを例に挙げます。


// ユーザー登録用のFormオブジェクト
public class UserRegistrationForm {
    
    @NotBlank(message = "ユーザー名を入力してください")
    private String username;
    
    @Email(message = "有効なメールアドレス形式で入力してください")
    private String email;
    
    private String role; // select用
    
    private boolean newsletter; // checkbox用
    
    // getter, setterはLombokなどを使用するか、手動で定義してください
}

<form th:action="@{/user/register}" th:object="${userRegistrationForm}" method="post" class="p-4 border rounded">
    
    <div class="mb-3">
        <label class="form-label">ユーザー名</label>
        <input type="text" th:field="*{username}" class="form-control" th:errorclass="is-invalid">
        <div class="invalid-feedback" th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></div>
    </div>

    <div class="mb-3">
        <label class="form-label">メールアドレス</label>
        <input type="email" th:field="*{email}" class="form-control" th:errorclass="is-invalid">
        <div class="invalid-feedback" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>
    </div>

    <div class="mb-3">
        <label class="form-label">権限</label>
        <select th:field="*{role}" class="form-select">
            <option value="USER">一般ユーザー</option>
            <option value="ADMIN">管理者</option>
        </select>
    </div>

    <div class="form-check mb-3">
        <input type="checkbox" th:field="*{newsletter}" class="form-check-input" id="newsletterCheck">
        <label class="form-check-label" for="newsletterCheck">ニュースレターを受け取る</label>
    </div>

    <button type="submit" class="btn btn-primary">登録する</button>
</form>

このように、th:fieldを適切に使い分けることで、複雑なフォームもシンプルかつ堅牢に実装できることがわかります。 最初は属性の自動生成に戸惑うかもしれませんが、「*{}(選択変数式)」を使って親オブジェクトのフィールドを指定するというルールさえマスターしてしまえば、開発の強力な味方になってくれます。

先生と生徒の振り返り会話

生徒

先生、ありがとうございました!th:fieldを使うと、HTMLのname属性やid属性を自分で書かなくていいのがすごく楽ですね。今まで一つずつ手打ちしていたのが嘘みたいです。

先生

そうだね。手書きだとどうしても「Java側はuserNameなのにHTML側でusernameと書いてしまった」といった些細なミスで動かなくなることが多いからね。自動生成に任せるのが一番安全なんだよ。

生徒

確かに!さっきエラーメッセージの表示も試してみたんですが、入力した内容が消えずに残っているのを見て感動しました。これ、th:fieldが裏側でvalue属性に値をセットし直してくれているからなんですね。

先生

その通り。バリデーションエラーで画面が戻ったときに、入力した内容が全部消えていたらユーザーはがっかりしちゃうからね。Webアプリとしての使い勝手を高めるためにも、th:fieldBindingResultの組み合わせは必須の知識だよ。

生徒

あと、セレクトボックスやチェックボックスでも同じ書き方で動くのが驚きでした。型に合わせて自動で処理を切り替えてくれるのは賢いですね。

先生

そうだね。ただし、講義でも触れたけどint型などの基本型を使うときは注意が必要だよ。未入力で送信されるとnullを扱えなくてエラーになるから、実務ではIntegerのようなラッパークラスを使うのが鉄則だ。

生徒

あ、そのエラー、さっそく練習中にやらかしました(笑)。これからはちゃんと型にも気をつけて、安全なコードを書けるように練習します!

先生

その意気だね。リスト形式やネストしたオブジェクトへのバインドができるようになると、もっと複雑な業務システムも作れるようになるよ。まずは基本をしっかりマスターして、次のステップに進んでいこう!

この記事を読んだ人からの質問

この記事を読んだ人からの質問
この記事を読んだ人からの質問

プログラミング初心者からのよくある疑問/質問を解決します

Thymeleafのth:fieldとは一体何のために使う属性なのですか?

Thymeleaf(タイムリーフ)におけるth:field属性は、Spring MVCの強力な機能であるフォームバインドと連携し、HTMLフォームの各入力項目(input要素など)をJavaのオブジェクト(モデルオブジェクト)のフィールドと自動的に結びつけるために使用します。これにより、画面から入力された値を簡単にJavaプログラム側で受け取ることができ、逆にJava側のデータを画面に初期値として表示させることも容易になります。
コメント
コメント投稿は、ログインしてください

まだ口コミはありません。

カテゴリの一覧へ
新着記事
New1
Springの基本
Spring Bootの@ConfigurationPropertiesScanとは?設定クラス自動検出の仕組みを解説
New2
SpringのAPI開発(REST & GraphQL)
Spring Boot GraphQLでResolverを理解しよう!初心者でもわかるデータ取得の基本
New3
SpringのAPI開発(REST & GraphQL)
Spring Boot GraphQL入門!Query・Mutation・Subscriptionの基本を初心者向けに解説
New4
SpringのDB操作
JPQLのパラメータバインド(:name / ?1)の使い方を完全解説!初心者でも迷わない基本の考え方
人気記事
No.1
Java&Spring記事人気No1
Thymeleaf
Thymeleaf とは?初心者向けにThymeleafの基本を徹底解説
No.2
Java&Spring記事人気No2
SpringのWeb開発(Spring MVC)
DispatcherServletの仕組みを理解する!初心者向け完全ガイド
No.3
Java&Spring記事人気No3
Springの基本
Spring Bootのデフォルトログ設定を徹底解説(Logback / SLF4J)
No.4
Java&Spring記事人気No4
SpringのDB操作
JPAの標準クエリメソッド(findById, findAll)を完全解説!初心者でもわかるデータ取得の基本
No.5
Java&Spring記事人気No5
SpringのWeb開発(Spring MVC)
Spring Bootでの@GetMappingと@PostMappingの基本を完全解説!初心者でも理解できる使い方
No.6
Java&Spring記事人気No6
Spring認証(Spring Security)
セッション管理の基本(@SessionAttributes)を完全解説!初心者でもわかるセッションの仕組み
No.7
Java&Spring記事人気No7
SpringのWeb開発(Spring MVC)
@Controller と @RestController の違いを完全解説!初心者向けSpring MVC入門
No.8
Java&Spring記事人気No8
SpringのWeb開発(Spring MVC)
ループ処理(th:each)の基本を完全ガイド!Thymeafの繰り返し処理の使い方