はじめに
やりたいこと
- ユーザがフォームに入力した値をもとに何かしたい。
- 入力画面⇒確認画面⇒完了画面、と遷移させたい。
この記事が参考になる人
- お問い合わせフォームを実装する人など。
- Java, Spring, Thymeleafの知識が最低限ある人。
使った技術要素
- Java
- Spring
- フレームワーク
- Thymeleaf
- テンプレートエンジン
- lombok
- getter, setterを自動生成してくれる
実装
好みもあるかと思いますが、以下の手順で実装していくといいと思います。
最低限のスニペットのみ掲載します。
入力画面
HTML
input.html
<blc:form th:action="@{/input}" method="POST" name="hogeForm" th:object="${hogeForm}">
<p>問い合わせカテゴリー</p>
<select name="category" th:field="*{category}">
<option value="">---</option>
<option th:each="item : *{getCategoryList()}" th:value="${item.key}" th:text="${item.value}"/>
</select>
<p th:if="${#fields.hasErrors('category')}" th:errors="*{category}">エラーメッセージ</p>
<p>問い合わせ内容</p>
<textarea name="content" placeholder="お問合せ内容を入力してください。" th:field="*{content}"></textarea>
<p th:if="${#fields.hasErrors('content')}" th:errors="*{content}">エラーメッセージ</p>
<input type="submit" value="確認"/>
</blc:form>
Formオブジェクト
HogeForm.java
@Getter
@Setter
public class HogeForm implements Serializable {
@NotBlank(message="選択してね")
private String category;
@NotBlank(message="何か入力してね")
@Length(max=1000)
private String content;
public Map<String, String> getCategoryList() {
Map<String, String> categoryMap = new LinkedHashMap<String, String>();
categoryMap.put("1", "カテゴリー1");
categoryMap.put("2", "カテゴリー2");
categoryMap.put("3", "カテゴリー3");
return categoryMap;
}
}
ポイント
- プロパティ名とformのname属性は同じ変数名を使う。
-
getCategoryList()
でセレクトボックスの内容はFormで保持しておく。順番に表示したいからLinkedHashMap
を使う。 HTML側での表示方法は以下の通り。
<option th:each="item : *{getCategoryList()}" th:value="${item.key}" th:text="${item.value}"/>
- Validationでエラーになった時、
- HTML側にて
#fields.hasErrors('(プロパティ名)')
がtrue
になる。 -
@(Validationタイプ)(message="~~~")
とプロパティにmessageを指定しておくと、HTML側でth:errors="*{(プロパティ名)}"
と記述すれば、指定したmessageを表示することができる。
- HTML側にて
Controller
HogeController.java
@RequestMapping("/input")
public ModelAndView input(
@ModelAttribute("hogeForm") HogeForm hogeContactForm,
Model model, HttpServletRequest request) {
return "index.html";
}
@RequestMapping(value = "/input", method = RequestMethod.POST)
public ModelAndView input(
@Valid @ModelAttribute("hogeForm") HogeForm hogeForm,
BindingResult bindingResult,
Model model, HttpServletRequest request) {
// エラーがある場合、自画面遷移する
if (bindingResult.hasErrors()) {
return "index.html";
}
HttpSession session = request.getSession();
session.setAttribute("hogeForm", hogeForm);
return "redirect:/confirm";
}
ポイント
- formのPOST先を確認画面(
/confirm
)にするのではなく、それ用に2つ目のinputメソッドを作成する。- メリット:
/confirm
のメソッドが確認画面の表示に専念できる。 - POST先を
/confirm
にしてしまうと、confirmメソッド内で入力画面用の処理と確認画面用の処理の2つを実装しなくてはならなくなってしまう。 - 上記はシンプル例だからそんなにメリットを感じられないが、実業務だとコードがすっきりしていい感じになる。
- メリット:
確認画面
HTML
confirm.html
<blc:form th:action="@{/complete}" method="POST" name="hogeForm" th:object="${hogeForm}">
<p>問い合わせカテゴリー</p>
<p th:text="*{getCategoryList().get('__*{category}__')}">カテゴリー1</p>
<p>問い合わせ内容</p>
<p th:text="*{content}">ここに問合せ内容を表示</p>
<div>
<input type="hidden" name="category" th:value="*{category}" />
<input type="hidden" name="content" th:value="*{content}" />
<input type="submit" value="送信" />
</div>
</blc:form>
ポイント
-
*{getCategoryList().get('__*{category}__')}
でMapのvalueを表示できる。普通に*{category}
だけだとkeyの方が表示される。
Controller
HogeController.java
@RequestMapping("/confirm")
public ModelAndView confirm(
Model model, HttpServletRequest request) {
HttpSession session = request.getSession();
HogeForm hogeForm = (HogeForm) session.getAttribute("hogeForm");
model.addAttribute("hogeForm", hogeForm);
return "confirm.html";
}
完了画面
HTML
complete.html
<p>以下の内容で受け付けました。</p>
<p>問い合わせカテゴリー</p>
<p th:text="*{getCategoryList().get('__*{category}__')}">カテゴリー1</p>
<p>問い合わせ内容</p>
<p th:text="*{content}">ここに問合せ内容を表示</p>
</blc:form>
Controller
HogeController.java
@RequestMapping("/complete")
public ModelAndView complete(
@ModelAttribute("hogeForm") HogeForm hogeContactForm,
Model model, HttpServletRequest request) {
model.addAttribute("hogeForm", hogeForm);
return "complete.html";
}
感想
2年ぶりくらいにformの実装をしたからすごいつまづきながらの実装だった。。