ログインをして、ユーザー一覧を表示するアプリケーションを作成し、
Springでの開発について勉強していきます🌟
前回の記事で作った画面に引き続き、データバインドを実装します
前回の記事🌟
[【Java・SpringBoot・Thymeleaf】ログイン・新規登録画面作成(SpringBootアプリケーション実践編1)]
(https://qiita.com/suema0331/items/5e69a90a2f18ea3b5b67)
#データバインドとは?
- 画面の入力項目とオブジェクトのフィールドの**マッピング(割り当て)**を行うこと
- 画面から渡された値を、フィールドのデータ型に合わせて変換してくれる
##Springでのデータバインド
- Springでは、データバインドをある程度自動で行ってくれるが
- ex: 画面からテキストで入力した数値を、オブジェクトのint型に変換するなど
- データの型変換が難しい場合は、アノテーションを使うことでバインドできます
#データバインド実践!
構成は以下のようになっています
Project Root
└─src
└─ main
└─ java
└─ com.example.demo
└─ login
└─ controller ...コントローラクラス用パッケージ
└─ LoginController.java
└─ SignupController.java
└─ domain ...ビジネスロジック用パッケージ
└─ model ...Modelクラス用パッケージ
└─ SignupForm.java
└─ resouces
└─ static ...css,js用フォルダ
└─ templates
└─ login
└─ login.html
└─ signup.html
##ユーザー登録処理の内容
- 1.ログイン画面からコントローラー(SignupController)にGETリクエストを送信
- 2.ユーザー登録画面に遷移、ユーザー登録用フォームクラスのインスタンス(SignupForm)をユーザー登録画面に渡す
- 3.ユーザー登録ボタンをクリックすると、フォームクラスのインスタンスをコントローラークラスに渡す
- 4.フォームクラスのインスタンスを受け取ったら
/login
にリダイレクト(ユーザーの登録はしない)
=ユーザー登録画面から、ユーザー登録用フォームクラスへデータバインドしているということです
##ユーザー登録画面用のフォームクラスを作成
-
データバインド用のアノテーション
-
@DateTimeFormat:指定されたフォーマットの文字列を日付型に変換
- 以下の例では画面から渡されてきた文字列を日付型に変換
- pattern属性にどのようなフォーマットでデータが渡されてくるかを指定している
@DateTimeFormat(pattern = "yyyy/MM/dd")
- @NumberFormat:指定されたフォーマットの文字列を数値型に変換
-
@DateTimeFormat:指定されたフォーマットの文字列を日付型に変換
SignupForm.java
package com.example.demo.login.domain.model;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;
@Data
public class SignupForm {
private String userId;
private String userName;
@DateTimeFormat(pattern = "yyyy/MM/dd")
private Date birthday;
private int age;
private boolean marriage;
}
##コントローラークラスを編集し、フォームクラスを受け取る
###@ModelAttribute
- 引数のフォームクラスに@ModelAttributeアノテーションを付けると、自動でModelクラスに登録(addAttribute)してくれる!
public String getSignUp(@ModelAttribute SignupForm form, Model model) {...}
- つまり、以下のコードイメージ
//イメージはこう
@GetMapping("/signup")
public String getSignUp(SignupForm form, Model model){
//フォームクラスをModelに登録
model.addAttribute("SignupForm",form);
//login.htmlに画面遷移
return"login/signup";
}
###データバインド結果を受け取る
- メソッドの引数にBindingResultクラスを追加
-
hasErros()
メソッドで、データバインドに失敗しているかどうかが分かる - バリデーションエラーが発生した場合も、失敗しているかどうかが分かる
-
// 入力チェックに引っかかった場合、ユーザー登録画面に戻る
if (bindingResult.hasErrors()) {
// GETリクエスト用のメソッドを呼び出して、ユーザー登録画面に戻る
return getSignUp(form, model);
}
- データバインドに失敗した場合、hasErrors()メソッドでfalseが返る
- 上のコードではデータバインドに失敗した場合、ユーザー登録画面に戻り、getSignUpメソッドを呼び出す
- →ラジオボタン用の変数を初期化してくれる
SignupController.java
package com.example.demo.login.controller;
import java.util.LinkedHashMap;
import java.util.Map;
import com.example.demo.login.domain.model.SignupForm;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class SignupController {
//ラジオボタン用変数
private Map<String, String> radioMarriage;
//ラジオボタンの初期化メソッド
private Map<String, String> initRadioMarrige() {
Map<String, String> radio = new LinkedHashMap<>();
// 既婚、未婚をMapに格納
radio.put("既婚", "true");
radio.put("未婚", "false");
return radio;
}
//GETメソッド
@GetMapping("/signup")
public String getSignUp(@ModelAttribute SignupForm form, Model model) {
// ラジオボタンの初期化メソッド呼び出し
radioMarriage = initRadioMarrige();
// ラジオボタン用のMapをModelに登録
model.addAttribute("radioMarriage", radioMarriage);
// signup.htmlに画面遷移
return "login/signup";
}
//POSTメソッド
//データバインド結果の受けとり
@PostMapping("/signup")
public String postSignUp(@ModelAttribute SignupForm form,
BindingResult bindingResult,
Model model) {
// 入力チェックに引っかかった場合、ユーザー登録画面に戻る
if (bindingResult.hasErrors()) {
// GETリクエスト用のメソッドを呼び出して、ユーザー登録画面に戻る
return getSignUp(form, model);
}
// formの中身をコンソールに出して確認
System.out.println(form);
// login.htmlにリダイレクト
return "redirct:/login";
}
}
##ユーザ登録用の画面
###th:object属性
- Modelに登録されているオブジェクトを受け取る
th:object="${<ModelAttributeのキー名>}"
- 以下の例ではSignupFormクラスを受け取っている
<form method="post" action="@{/signup}" th:object="${signupForm}">
- th:objectを付けたタグの中では、th:fieldでそのオブジェクト名を省略可能
###th:fieldの使い方
- th:fieldを使用すると、オブジェクトの中のフィールドを取得し、コントローラークラスに値を渡せる
- ①th:object属性を書かなかった場合は
th:field="${<ModelAttributeのキー名.フィールド名>}"
- フィールドを1つしか使わない場合など、th:objectを書かなくても値の取得・送信ができる
<!-- コード修正例 -->
<inputtype="text"th:field="${signupForm.userId}"/>
- ②th:objectが付いたタグ内であれば、オブジェクト名を省略可能
th:field="∗{<フィールド名>}"
- 画面から送るフィールドが多いときに有効
###エラーメッセージをまとめて一覧表示
-
th:each属性:拡張for文のようにModelに登録されている値が繰り返し呼ばれる
th:each="<変数名>:${<ModelAttributeのキー名>}"
<li th:each="error : ${#fields.detailedErrors()}">
<span th:text="${error.message}">Error message</span>
</li>
- これでデータバインド実装完成!
signup.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"></meta>
<!-- Bootstrapの設定 -->
<link th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css}" rel="stylesheet"></link>
<script th:src="@{/webjars/jquery/1.11.1/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js}"></script>
<title>SignUp</title>
</head>
<body>
<div class="col-sm-5">
<div class="page-header">
<h1>ユーザー登録画面</h1>
</div>
<form method="post" th:action="@{/signup}" th:object="${signupForm}">
<table class="table table-bordered table-hover">
<!-- ユーザーID -->
<tr>
<th class="active col-sm-3">ユーザID</th>
<td>
<div class="form-group">
<input type="text" class="form-control" th:field="*{userId}" />
</div>
</td>
</tr>
<!-- パスワード -->
<tr>
<th class="active">パスワード</th>
<td>
<div class="form-group">
<input type="text" class="form-control" th:field="*{password}" />
</div>
</td>
</tr>
<!-- ユーザー名 -->
<tr>
<th class="active">ユーザー名</th>
<td>
<div class="form-group">
<input type="text" class="form-control" th:field="*{userName}" />
</div>
</td>
</tr>
<!-- 誕生日 -->
<tr>
<th class="active">誕生日</th>
<td>
<div class="form-group">
<input type="text" class="form-control" placeholder="yyyy/MM/dd" th:field="*{birthday}"/>
</div>
</td>
</tr>
<!-- 年齢 -->
<tr>
<th class="active">年齢</th>
<td>
<div class="form-group">
<input type="text" class="form-control" th:field="*{age}" />
</div>
</td>
</tr>
<!-- 結婚 -->
<tr>
<th class="active">結婚</th>
<td>
<div class="form-group">
<div th:each="item : ${radioMarriage}">
<input type="radio" name="radioMarrige"
th:text="${item.key}"
th:value="${item.value}"
th:field="*{marriage}">
</input>
</div>
</div>
</td>
</tr>
</table>
<!-- エラーメッセージの一覧表示 -->
<ul>
<li th:each="error : ${#fields.detailedErrors()}">
<span th:text="${error.message}">Error message</span>
</li>
</ul>
<button class="btn btn-primary" type="submit">ユーザー登録</button>
</form>
</div>
</body>
</html>
#SpringBootを起動して、ログイン画面を確認!
- http://localhost:8080/login
- ユーザー登録ボタンをクリックするとログイン画面に遷移し、コンソールにSignupFormクラスの中身が表示される!
- 画面からフォームクラスに値を渡し、フォームクラスをコントローラークラスに渡すことができました😊
//コンソール
SignupForm(userId=NEKO, password=password, userName=neko, birthday=Tue Jan 23 00:00:00 JST 2018, age=2, marriage=false)
##バインド失敗時
- 2つのフィールドでバインドに失敗しているため、エラーメッセージが2つ表示されます
- が分かりにくい。。。
- 次はこのエラーメッセージを編集します^^