2018年5月28日月曜日

Spring MVCとThymeleafの組み合わせでcheckboxのデフォルト値を設定する

Spring MVCとThymeleafの組み合わせでcheckboxを使った場合の動きを理解する。

checkboxの値をBooleanで受け取る場合

Form

public class Form {

    private Boolean check;

    // ほかは省略
}

Controller

checkboxの値を確認できるように標準出力に値を出力するだけに
@PostMapping("hello")
public String hello(Form form) {
    System.out.println("form.check = " + form.check);
    return "input";
}

View

<form th:action="@{/hello}" method="post" th:object="${form}">
  <input type="checkbox" th:field="*{check}" />
  <button type="submit">submit</button>
</form>

動かしてみた結果

出力されるhtmlには、checkboxとcheckboxに対応したhiddenが出力されます。このhiddenがあることでcheckboxがチェックされなかった場合でもFormに値が設定されます。(Booleanなのでfalseが設定される)
これは、thymeleafのth:fieldで自動的に出力されます。
<form action="/hello" method="post">
  <input type="checkbox" id="check1" name="check" value="true" /><input type="hidden" name="_check" value="on"/>
  <button type="submit">submit</button>
</form>

checkboxをチェックしないで送信した時は標準出力に↓が出力されます。
form.check = false

ちなみに、th:fieldを使わないと下のようにhiddenが出力されないので、checkboxがチェックされなかった場合はFormには値が設定されないのでnullのままとなります。
<form action="/hello" method="post">
  <input type="checkbox" name="checkbox" />
  <button type="submit">submit</button>
</form>

checkboxの値をBoolean以外で受け取る場合

単一のcheckboxの値をBooleanで受け取ることはないと思うけど、Stringで受け取った場合の動きを確認してみます。

Stringの場合も、Booleanと同じようにth:fieldを使うと、↓が出力されます。だけど、Stringの場合はth:fieldが出力するアンダースコア付きのhiddenに対応していないので、このままだと未チェック時はFormの値はnullのままになります。

<form action="/hello" method="post">
  <input type="checkbox" value="on" id="check1" name="check"/><input type="hidden" name="_check" value="on"/>
  <button type="submit">submit</button>
</form>

未送信時に送信する値は、別途↓のようにhiddenで定義します。デフォルト設定だと、対応するcheckboxのnameの先頭に!をつけた名前でhiddenを定義する必要があります。
プレフィックスの値はWebDataBinderのJavadocが参考になります。
<form th:action="@{/hello}" method="post" th:object="${form}">
  <input type="checkbox" th:field="*{check}" value="on" />
  <input type="hidden" name="!check" value="off" />
  <button type="submit">submit</button>
</form>

これで、未チェック時にはhiddenで指定した値をFormで受け取れるようになります。
form.check = off