はじめに
入力エラー時にテキストボックスの枠を赤くしようとしたのですが、実装に苦労したので投稿します。
環境
OS: macOS Catalina 10.15.6
JDK:14.0.1
Spring Boot 2.3.3
jquery 3.3.1-1
bootstrap 4.2.1
①入力チェックにあたって
入力チェック(バリデーション)を行うにあたって、作者は下記のようにクラスを作成しました。
■コントローラークラス
package com.example.demo.login.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import com.example.demo.login.domain.model.GroupOrder;
import com.example.demo.login.domain.model.SignupForm;
import com.example.demo.login.domain.model.User;
import com.example.demo.login.domain.service.UserService;
//ユーザー新規登録用コントローラー
@Controller
public class SignupController {
@Autowired
private UserService userService;
//ユーザー登録画面へ遷移
@PostMapping("/signup")
public String postSignUp(@ModelAttribute SignupForm form, Model model) {
return "signup/signup";
}
//ユーザー新規登録実行用メソッド
@PostMapping("/signupUser") //「GroupOrder」で設定した順序でバリデーションを実行
public String postSignUp(@ModelAttribute @Validated(GroupOrder.class) SignupForm form, BindingResult bindingResult, Model model) {
//バインディングでエラー発生(バリデーションエラー含む)した場合、ユーザー登録画面へ遷移
if (bindingResult.hasErrors()) {
return postSignUp(form, model);
}
//新規登録した内容をコンソールへ表示
System.out.println(form);
//Userインスタンスの作成
User user = new User();
//登録フォームに入力した内容をUserクラスにセット
user.setUserId(form.getUserId());
user.setMailAddress(form.getMailAddress());
user.setPassword(form.getPassword());
//フォーム入力内容をセットした「User」を引数にサービスクラスへ処理を投げる
boolean result = userService.insertOne(user);
//登録作業の実行結果をコンソールに表示
if (result == true) {
System.out.println("insert成功");
} else {
System.out.println("insert失敗");
}
//ログイン画面へ遷移
return "redirect:/login";
}
}
■バリデーショングループ
package com.example.demo.login.domain.model;
import javax.validation.GroupSequence;
//ユーザー新規登録とユーザー登録情報更新の際に「ValidGroup1」「ValidGroup2」の順番にバリデーションを実行
@GroupSequence({ValidGroup1.class, ValidGroup2.class})
public interface GroupOrder {
}
package com.example.demo.login.domain.model;
//ValidGroup1のインターフェース
public interface ValidGroup1 {
}
package com.example.demo.login.domain.model;
//ValidGroup2のインターフェース
public interface ValidGroup2 {
}
■入力フォームのクラス
package com.example.demo.login.domain.model;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.Length;
import lombok.Data;
//ユーザー新規登録フォームで使用するSignupForm
@Data
public class SignupForm {
/*
* null 半角スペース 空文字の場合エラー発生(ValidGroup1に分類)
* 3字以上20字以内(ValidGroup2に分類)
*/
@NotBlank(groups = ValidGroup1.class)
@Length(min = 3, max = 20, groups = ValidGroup2.class)
private String userId;
/*
* null 半角スペース 空文字の場合エラー発生(ValidGroup1に分類)
* メールアドレスの形式でないとエラー発生(ValidGroup2に分類)
*/
@NotBlank(groups = ValidGroup1.class)
@Email(groups = ValidGroup2.class)
private String mailAddress;
/*
* null 半角スペース 空文字の場合エラー発生(ValidGroup1に分類)
* 3字以上20字以内(ValidGroup2に分類)
* 英数字のみ(ValidGroup2に分類)
*/
@NotBlank(groups = ValidGroup1.class)
@Length(min = 3, max = 20, groups = ValidGroup2.class)
@Pattern(regexp = "^[a-zA-Z0-9]+$", groups = ValidGroup2.class)
private String password;
}
■エラーメッセージ
#バリデーションエラーメッセージ
signupForm.userId=ユーザーID
NotBlank.signupForm.userId={0}を入力してください
Length.signupForm.userId={0}は、{2}字以上{1}字以下で入力してください
signupForm.mailAddress=メールアドレス
NotBlank.signupForm.mailAddress={0}を入力してください
Email.signupForm.mailAddress=メールアドレス形式で入力してください
signupForm.password=パスワード
NotBlank.signupForm.password={0}を入力してください
Length.signupForm.password={0}は、{2}字以上{1}字以下で入力してください
Pattern.signupForm.password={0}は半角英数字で入力してください
searchForm.keyword=キーワード
NotBlank.searchForm.keyword={0}を入力してください
#ログインエラーメッセージをカスタマイズ
AbstractUserDetailsAuthenticationProvider.badCredentials=ログインIDまたはパスワードが間違っています。
②入力フォームの作成(修正前)
当初、下記のように入力フォームのhtmlを作成しました。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/template}">
<head>
<meta charset="UTF-8"></meta>
<title>SignUp</title>
</head>
<!-- ユーザー登録画面 -->
<body>
<div layout:fragment="content">
<div class="col-sm-4 offset-sm-4">
<div class="page-header">
<h1>ユーザー登録画面</h1>
</div>
<form method="post" th:action="@{/signupUser}" th:object="${signupForm}">
<table class="table table-bordered table-hover">
<tr>
<td> <!-- modelオブジェクト「signupForm」の「userId」フィールドでエラーが発生したら、class属性に「has-error」を追加し、テキストボックスの周りを赤くする
-->
<div class="form-group" th:classappend="${#fields.hasErrors('userId')}?'has-error'">
<input type="text" class="form-control" placeholder="ログインID" th:field="*{userId}"/>
<span class="text-danger"
th:if="${#fields.hasErrors('userId')}"
th:errors="*{userId}">
userId error
</span>
</div>
</td>
<tr>
<tr>
<td> <!-- modelオブジェクト「signupForm」の「mailAddress」フィールドでエラーが発生したら、class属性に「has-error」を追加し、テキストボックスの周りを赤くする
-->
<div class="form-group"
th:classappend="${#fields.hasErrors('mailAddress')}?'has-error'">
<input type="text" class="form-control" placeholder="メールアドレス" th:field="*{mailAddress}"/>
<span class="text-danger"
th:if="${#fields.hasErrors('mailAddress')}"
th:errors="*{mailAddress}">
mailAddress error
</span>
</div>
</td>
<tr>
<tr>
<td> <!-- modelオブジェクト「signupForm」の「password」フィールドでエラーが発生したら、class属性に「has-error」を追加し、テキストボックスの周りを赤くする
-->
<div class="form-group"
th:classappend="${#fields.hasErrors('password')}?'has-errors'">
<input type="text" class="form-control" placeholder="パスワード" th:field="*{password}"/>
<span class="text-danger"
th:if="${#fields.hasErrors('password')}"
th:errors="*{password}">
password error
</span>
</div>
</td>
<tr>
</table>
<button class="btn btn-primary col-sm-6" type="submit">新規登録</button>
</form>
</div>
</div>
</body>
</html>
html内のコメントでも記述しているように、各フィールドでエラーが発生したら、テキストボックスの枠が赤くなるよう記述したのですが、結果は下の通り枠は赤くはなりませんでした。
ちなみに「has-error」はBootstrapのclassでテキストボックスの枠を赤くしてくれるものです。
「Bootstrap4移行ガイド」を確認すると下記のような記述がありました。
【Bootstrap3.xとの変更箇所】
各検証状態の表示スタイルからHTML5フォーム検証機能を使用したスタイルに変更
.has-warning, .has-error, .has-success, .has-feedback, .form-control-feedback は廃止
フォールバックとして、.is-invalid クラスと .is-valid クラスをサーバー側の検証用の疑似クラスの代わりに使用可能。.was-validated 親クラスは必要ない。
URL:https://bootstrap-guide.com/components/forms?
Bootstrap4以降「has-error」は廃止、代わりに「is-invalid」が機能として提供されているようです。
③入力フォームの修正
divタグ内ではなく、inputタグの中に記述します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/template}">
<head>
<meta charset="UTF-8"></meta>
<title>SignUp</title>
</head>
<!-- ユーザー登録画面 -->
<body>
<div layout:fragment="content">
<div class="col-sm-4 offset-sm-4">
<div class="page-header">
<h1>ユーザー登録画面</h1>
</div>
<form method="post" th:action="@{/signupUser}" th:object="${signupForm}">
<table class="table table-bordered table-hover">
<tr>
<td>
<div class="form-group">
<!-- modelオブジェクト「signupForm」の「userId」フィールドでエラーが発生したら、class属性を追加し「is-invalid」でテキストボックスの周りを赤くする-->
<input type="text" class="form-control" placeholder="ログインID" th:field="*{userId}"
th:classappend="${#fields.hasErrors('userId')}?'is-invalid'"/>
<span class="text-danger"
th:if="${#fields.hasErrors('userId')}"
th:errors="*{userId}">
userId error
</span>
</div>
</td>
<tr>
<tr>
<td>
<div class="form-group">
<!-- modelオブジェクト「signupForm」の「mailAddress」フィールドでエラーが発生したら、class属性を追加し「is-invalid」でテキストボックスの周りを赤くする-->
<input type="text" class="form-control" placeholder="メールアドレス" th:field="*{mailAddress}"
th:classappend="${#fields.hasErrors('mailAddress')}?'is-invalid'"/>
<span class="text-danger"
th:if="${#fields.hasErrors('mailAddress')}"
th:errors="*{mailAddress}">
mailAddress error
</span>
</div>
</td>
<tr>
<tr>
<td>
<div class="form-group">
<!-- modelオブジェクト「signupForm」の「password」フィールドでエラーが発生したら、class属性を追加し「is-invalid」でテキストボックスの周りを赤くする-->
<input type="text" class="form-control" placeholder="パスワード" th:field="*{password}"
th:classappend="${#fields.hasErrors('password')}?'is-invalid'"/>
<span class="text-danger"
th:if="${#fields.hasErrors('password')}"
th:errors="*{password}">
password error
</span>
</div>
</td>
<tr>
</table>
<button class="btn btn-primary col-sm-6" type="submit">新規登録</button>
</form>
</div>
</div>
</body>
</html>
実行すると下のように、入力エラー時テキストボックスの枠を赤くすることができました。
参考:
https://www.e-pokke.com/blog/bootstrap4-invalid-feedback.html
https://learning-collection.com/thymeleaf%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9/