仕事で
「select + optionタグを使用し、ユーザ操作で選択はさせたくないけど、初期状態では選択して表示したい」
ということをしたい時があったので、その備忘録です。
言葉では判りづらいので実例で説明すると
販売するため「机、椅子、棚」のリスト(select + option)を表示します。
ある時点で「椅子」を販売しました。
その後「椅子」が販売中止になったので、選択できないように(disabled)、さらにformしてもエラーにしたい。
でも、販売情報を見る際は「椅子」を選択(selected)して初期表示したい。
といった感じです。(さらに判りづらいかも…)
簡潔に言うと「selectのoptionタグで、selectedとdisabledを同時に使用したい」ということです。
前提
環境構築、th:fieldの使い方などは知っているものとして記載しています。
単にソースコードを書いてもわかりづらいので、以下の目的に沿って書いてます。
- 家具リストを「机、椅子、棚」とし、「椅子」を初期選択して表示。
- それぞれの家具は有効無効状態を持っており、「机、棚」は有効、「椅子」は無効。
- 椅子が選択されたまま form post するとエラー表示。
尚、ソースコードには必要最低限しか書いてません。
Formクラスについて
Formクラスを以下とします。
KaguPageForm.java
public class KaguPageForm {
@NotNull(message="選択された家具は、販売中止です。")
private Integer selectedKaguId; // 選択された家具
private ArrayList<KaguInfo> kaguInfoList = new ArrayList<>(); // 家具情報リストを格納
// getter/setterは省略
// 必要に応じて、コンストラクタを追加
}
disabled状態で選択表示されている時にpostするとnullが送信されます。
そのため、@NotNullで検出することができます。
KaguInfo.java
public class KaguInfo {
private Integer id; // 家具ID
private String name; // 家具名
private Integer isValid; // 有効無効。 0:無効、1:有効
// getter/setterは省略
}
注意点としては、KaguInfoクラスをKaguPageFormクラス内で宣言しないようにしてください(インナークラスにしない)。
Thymeleafについて
次は今回メインのThymeleaf部分です。
<form th:action="@{/kagupage}" method="post" th:object="${kaguPageForm}">
<select id="selectedKaguId" name="selectedKaguId">
<option th:each="kagu : *{kaguInfoList}" th:value="${kagu.id}" th:text="${kagu.name}"
th:selected="${kagu.id} == *{selectedKaguId}" th:disabled="${kagu.isValid == 0}">
</option>
</select>
</form>
※th:errorclassなどエラー表示部については省略しています。
ここでのポイントは
- selectでは、th:fieldを使用せずに id / nameで記述する。
- option内のth:selectedでは、表示しようとしている家具IDが、選択された家具IDと同じなら selectedを表示させるようにする。
- option内のth:disabledでは、表示しようとしている家具が無効なら disabledを表示させるようにする。
です。
th:fieldを使用すると、disabledのみ効きselectedが効きません。
そのため id/nameで記述させ、且つ、th:selectedにて明示的にselectedを付加させています。
Controllerクラスについて
Controller側のコードです。
KaguController.java
// GET時(初期表示)
@GetMapping(value = "/kagupage")
public ModelAndView get(
KaguPageForm formData,
ModelAndView mav) {
// 初期表示用
KaguInfo info = new KaguInfo();
info.setId(1);
info.setName("机");
info.setIsValid(1); // 有効
formData.getKaguInfoList.add(info);
info = new KaguInfo();
info.setId(2);
info.setName("椅子");
info.setIsValid(0); // 無効
formData.getKaguInfoList.add(info);
info = new KaguInfo();
info.setId(3);
info.setName("棚");
info.setIsValid(1); // 有効
formData.getKaguInfoList.add(info);
formData.setSelectedKaguId(2); // 椅子を初期選択
mav.addObject("kaguPageForm", formData);
return mav;
}
// POST時
@PostMapping(value = "/kagupage")
public ModelAndView post(
@ModelAttribute("kaguPageForm") @Validated KaguPageForm formData,
ModelAndView mav) {
if (result.hasErrors()) {
// disabled状態のoptionを選択したままpostすると、@NotNullに引っかかり、このルートを通る。
}
.. 以下、省略 ..
}
KaguPageFormクラスのselectedKaguIdに@NotNullを設定しておくと、disabledで初期選択されている状態でform postが行われた時に、エラーを検出することができます。
最後に
いかがでしたでしょうか?
私は、最初素直にselectにth:fieldを、optionにdisabledを表示するように記述したのですが、
期待通りに表示されず、いろいろと調べてやっとできるようになりました。
プログラムを組んでるとたまに、理論的にはこうすれば良いはずだけど何故かうまくいかない、といった状況に陥ることがあり、とても悩みます。
同じように悩んでいる人が、ここを見て解決に一歩前進できたら嬉しく思います。
コメント