LoginSignup
16
9

More than 1 year has passed since last update.

【Themeleaf初心者向け】formにおけるth:fieldの挙動まとめ

Last updated at Posted at 2021-12-04

はじめに

Spring+Thymeleafで開発を行っていて、
formの作成の際、
<input type="radio" th:value="○" th:text="○" th:field="○">
のように、複数Thymeleafタグを使っています。
これって全部書く意味があるのか?必要ないものはないのか?
と思ったので、自分で検証してみた結果を以下にまとめます。

概要

inputのtypeによって、th:fieldの挙動が違うので、記載すべきものが違います。

  • text,number,range
    th:fieldは、「id」「name」「value」の役割を果たします。
    th:fieldとth:valueが重複している時、th:objectで指定したformの値がある場合は、th:fieldが優先されます。

  • textarea
    th:fieldは、「id」「name」「text」の役割を果たします。
    th:fieldとth:textが重複している時、th:objectで指定したformの値がある場合は、th:fieldが優先されます。

  • radio,checkbox,select
    th:fieldは「id」「name」の役割を果たします。
    つまり、他2パターンのように「value」や「text」を補うことはできません。
    th:valueとth:textは別途指定する必要があります。

前提

・コントローラークラス
コード説明は後述します。

ThFieldTestController.java
public class ThFieldTestController {
    // import文省略

    @ModelAttribute
    private ThFieldTestForm setUpForm() {
        ThFieldTestForm thFieldTestForm = new ThFieldTestForm();
        thFieldTestForm.setGender("2");
        return thFieldTestForm;
    }

    @RequestMapping("")
    public String index(Model model) {
        Map<Integer, String> genderMap = new LinkedHashMap<>();
        genderMap.put(1, "woman");
        genderMap.put(2, "man");

        model.addAttribute("genderMap", genderMap);
        model.addAttribute("gender", "1"); // ${"gender"}で1を呼べるように

        return "th-field-test";
    }

setUpForm()メソッドでは、
thFieldTestForm.setGender("2");
でフォームのgenderの初期値に2を渡しています。

index()メソッドでは、リクエストスコープにデータを格納し、
<○ th:each="gender:${genderMap}" th:○="${gender.key}">
<th:○="${gender}">
のどちらでも値を呼べるようにしています。
この時、マップでないgenderの値は1を渡しています。

・フォームクラス

ThFieldTestForm.java
public class ThFieldTestForm {
    private String gender;

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getGender() {
        return gender;
    }
}

詳細

textの場合

th-field-test.html
<div class="text">
    <h4>text</h4>
    リクエストスコープのgender:1<br>
    formの初期値:2<br>
    【text】th:field="*{gender}"<br>
    <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
        <input type="text" th:field="*{gender}"><br>
        <button>送信</button>
    </form>
    【text】th:field="*{gender}" th:value="${gender}"<br>
    <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
        <input type="text" th:field="*{gender}" th:value="${gender}"><br>
        <button>送信</button>
    </form>
  th:value="${gender}"が優先されるなら1<br>
    th:field="*{gender}"が優先されるなら2<br>
    →th:fieldの入力値保存のほうが強く働いている<br>

    【text】th:value="${gender}"
    <br>
    <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
        <input type="text" th:value="${gender}"><br>
        <button>送信</button>
    </form>
    【text】th:text="${gender}"
    <br>
    <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
        <input type="text" th:text="${gender}"><br>
        <button>送信</button>
    </form><br>
    </div>

ブラウザだと・・・
image.png
コンソールで確認すると・・・
image.png
th:fieldがあると、id,name,valueが入力されている。
(th:fieldとth:valueが重複している時、th:objectで指定したformの値がある場合は、th:fieldが優先される。)
(numberとrangeも同じ挙動のため割愛します。)

textareaの場合

th-field-test.html
  <div class="textarea">
    <h4>textarea</h4>
        リクエストスコープのgender:1<br>
        formの初期値:2<br>
        【textarea】th:field="*{gender}"<br>
        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
            <textarea th:field="*{gender}"></textarea><br>
            <button>送信</button>
        </form>
        【textarea】th:id="gender" th:name="gender" th:text="${gender}"<br>
        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
            <textarea th:id="gender" th:name="gender" th:text="${gender}"></textarea><br>
            <button>送信</button>
        </form>
        【textarea】th:field="*{gender}" th:value="${gender}"
        <br>
        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
            <textarea th:field="*{gender}" th:value="${gender}"></textarea>
            <button>送信</button>
        </form>
        【textarea】th:value="${gender}"
        <br>
        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
            <textarea th:value="${gender}"></textarea>
            <button>送信</button>
        </form>
        【textarea】th:text="${gender}"
        <br>
        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
            <textarea th:text="${gender}"></textarea>
            <button>送信</button>
        </form><br>
        【textarea】th:field="*{gender}" th:text="${gender}"
        <br>
        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
            <textarea th:field="*{gender}" th:text="${gender}"></textarea>
            <button>送信</button>
        </form>
        th:text="${gender}"が優先されるなら1<br>
        th:field="*{gender}"が優先されるなら2<br>

    </div>

ブラウザだと・・・
image.png

コンソールだと・・・
image.png

th:fieldがあると、id,nameが入力されており、textを指定したときと同じ挙動をしている。
(th:fieldとth:textが重複している時、th:objectで指定したformの値がある場合は、th:fieldが優先される。)

radioの場合

th-field-test.html
    <div class="radio">
    <h4>radio</h4>
        リクエストスコープのgender:1<br>
        formの初期値:2<br>
        【radio】th:field="*{gender}" <br>
        → エラー(org.attoparser.ParseException: Attribute "value" is required in "input(radio)" tags)<br><br>
<!--        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}"> -->
<!--            <span th:each="gender:${genderMap}"> -->
<!--            <input type="radio" th:field="*{gender}"><br> -->
<!--            </span> -->
<!--            <button>送信</button> -->
<!--        </form><br> -->
        【radio】th:field="*{gender}" th:value="${gender.key}<br>
        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
            <span th:each="gender:${genderMap}">
            <input type="radio" th:field="*{gender}" th:value="${gender.key}"><br>
            </span>
            <button>送信</button>
        </form><br>
        【radio】th:value="${gender.key}<br>
        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
            <span th:each="gender:${genderMap}">
            <input type="radio" th:value="${gender.key}"><br>
            </span>
            <button>送信</button>
        </form><br>
        【radio】th:value="${gender.key} th:text="${gender.value}"<br>
        → 送れるけどデータとしてgenderのデータとして受け取られない<br>
        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
            <span th:each="gender:${genderMap}">
            <input type="radio" th:value="${gender.key}" th:text="${gender.value}"><br>
            </span>
            <button>送信</button>
        </form><br>
        【radio】th:field="*{gender}" th:text="${gender.value}"<br>
        → エラー(org.attoparser.ParseException: Attribute "value" is required in "input(radio)" tags)<br><br>
<!--        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}"> -->
<!--            <span th:each="gender:${genderMap}"> -->
<!--            <input type="radio" th:field="*{gender}" th:text="${gender.value}"><br> -->
<!--            </span> -->
<!--            <button>送信</button> -->
<!--        </form><br> -->
        【radio】th:field="*{gender}" th:value="${gender.key} th:text="${gender.value}"<br>
        <form th:action="@{/th-field-test/form-sent}" method="post" th:object="${thFieldTestForm}">
            <span th:each="gender:${genderMap}">
            <input type="radio" th:field="*{gender}" th:value="${gender.key}" th:text="${gender.value}"><br>
            </span>
            <button>送信</button>
        </form><br>
    </div>

ブラウザだと・・・
image.png

コンソールだと・・・
image.png

→th:fieldがあると、id,nameが入力されていますが、
別途valueを指定しないとエラーになる。また、textを指定しないとボタンの横に文字が現れません。
th:valueとth:textは別途指定する必要があります。
(checkboxとselectも同じ挙動のため割愛します。)

まとめ

th:fieldは以下図のように挙動していました。
image.png

・検証に使用したコードは以下に格納しておりますので、よろしければこちらからご確認ください。
https://github.com/MasayukiFurumoto-rks/th-field-test

参考

以下記事を参考にさせていただきました。
ありがとうございました。

Thymeleafのth:fieldの意味とSpring Frameworkの値の受け渡しの仕組み (Qiita)
th:field と th:object によるフォームバインディング機能(inputタグ・基本入力系編) (Hatena Blog)

所感

最後までご覧下さりありがとうございます。

今回、初めてQiita記事を投稿させていただきました。
マークダウン記法もままならないところからのスタートでしたが、アウトプットしていく中で理解が深まることもあり、ぜひまた投稿しようと思います。

もし訂正・加筆すべき箇所がございましたら、コメントにてご教示いただけますと幸いです。
何卒よろしくお願いいたします。

16
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
9