【Spring】入力フォームを実装する（入力画面⇒確認画面⇒完了画面）

# はじめに
## やりたいこと
- ユーザがフォームに入力した値をもとに何かしたい。
- 入力画面⇒確認画面⇒完了画面、と遷移させたい。

## この記事が参考になる人
- お問い合わせフォームを実装する人など。
- Java, Spring, Thymeleafの知識が最低限ある人。

## 使った技術要素
- Java
- Spring
    - フレームワーク
- Thymeleaf
    - テンプレートエンジン
- lombok
    - getter, setterを自動生成してくれる

# 実装
好みもあるかと思いますが、以下の手順で実装していくといいと思います。
最低限のスニペットのみ掲載します。

## 入力画面
### HTML
``` 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オブジェクト
```Java: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", "カテゴリー１");
        categoryMap.put("2", "カテゴリー２");
        categoryMap.put("3", "カテゴリー３");
        return categoryMap;
	}
}
```

ポイント

- プロパティ名とformのname属性は同じ変数名を使う。
- `getCategoryList()`でセレクトボックスの内容はFormで保持しておく。順番に表示したいから `LinkedHashMap`を使う。 HTML側での表示方法は以下の通り。

```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を表示することができる。

### Controller
```Java: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
``` HTML:confirm.html
<blc:form  th:action="@{/complete}" method="POST" name="hogeForm" th:object="${hogeForm}">
	<p>問い合わせカテゴリー</p>
	<p th:text="*{getCategoryList().get('__*{category}__')}">カテゴリー１</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
``` Java: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
``` HTML:complete.html
<p>以下の内容で受け付けました。</p>

<p>問い合わせカテゴリー</p>
<p th:text="*{getCategoryList().get('__*{category}__')}">カテゴリー１</p>

<p>問い合わせ内容</p>
<p th:text="*{content}">ここに問合せ内容を表示</p>
</blc:form>
```

### Controller
```Java:HogeController.java
@RequestMapping("/complete")
public ModelAndView complete(
		@ModelAttribute("hogeForm") HogeForm hogeContactForm,
		Model model, HttpServletRequest request) {

	model.addAttribute("hogeForm", hogeForm);
	return "complete.html";
}
```

# 感想
2年ぶりくらいにformの実装をしたからすごいつまづきながらの実装だった。。