ビーンバリデーション詳細②
都合の良いときだけ発火するバリデーション ①
例えば『下書』『保存して送信』のボタンがあるとします。
これ見るからに『下書』の方のバリデーションは多少ユルくても良いですよね?
『面倒だがカスタムバリデーションかぁ』と思うかもしれませんが、Jakartaにはちゃんと標準機能があります。 それがビーンバリデーションの『groups』属性です。 あれ、前の章でも出てきましたね。
実はこの属性順番にも使えるし、発火《させる|させない》にも使えるんです。 ではサンプルをご紹介します。
@Getter
@Setter
@NotBlank(message = "{error.required.name}", groups = Group1.class)
@Size(min = 3, max = 10, message = "{error.Size.name}", groups = Group2.class)
private String name;
@Getter
@Setter
@NotBlank(message = "{error.required.pref}", groups = Group1.class)
@Max(message = "{error.max.pref}", value = 10, groups = Group2.class)
private String pref;
error.required.name=名前は必須入力です(入力された値は「${validatedValue}」です)。
error.Size.name=この項目は{min}以上、{max}以下で記入してね。入力された文字は「${validatedValue}」でした。
error.required.pref=都道府県は必須入力です。
error.max.pref=都道府県は{value}以下だよ。
無事《Group1》のみのバリデーションが発火しましたね。
[form]タグ内を[f:validateBean]タグで囲う事で全てのUIにルールを適用できます。
ただひとつ問題があり、バッキングビーン側に「groups」属性を指定してないビーンバリデーションはどうなると思います?
答えは発火しないですw
ではどうする?全てのビーンバリデーションに「groups」属性を指定?
いえ、答えは簡単で、そのグループもルールに加えるだけです。 [xhtml]の[f:validateBean]タグを以下の様に修正すれば解決です!
<f:validateBean validationGroups="jakarta.validation.groups.Default, view.validator.group.Group1" >
都合の良いときだけ発火するバリデーション ②
<f:validateBean validationGroups="#{sampleBean.currentGroup}" > これでJAVA側の『submit()』メソッドを以下の様に修正する。
@Getter
private String currentGroup = "jakarta.validation.groups.Default, view.validator.group.Group1";
~ 省略 ~
public void submit() {
String actionType = FacesContext.getCurrentInstance()
.getExternalContext().getRequestParameterMap().get("actionType");
if (actionType.equals("DRAFT")) {
this.currentGroup = "jakarta.validation.groups.Default, view.validator.group.Group1";
} else {
this.currentGroup = "jakarta.validation.groups.Default, view.validator.group.GroupOrder";
}
// ~ バリデーションが通過した時の処理 ~
}
でもこれはもう遅いですよ。
② 各UIの内容とビーンバリデーションの内容を確認する
③ ②で1つでも問題があれば[xhtml]へ戻される
④ ②で問題がなければ、関数「submit」が処理される
そう!JAVAフレームワークと言ったら『AJAX』ですよね。 サンプルの前に仕組みをご説明します。
②「下書」「保存して送信」を押下する
②' ①'のボタンをAJAXで発火させる
③' ①'のAJAXは「execute="@this"」で送信するためバリデーションの影響を受けない
④' ①'のどのボタンが押下されたか、メソッドで振り分け「currentGroup」の内容を以下の様に変更する
・jakarta.validation.groups.Default, view.validator.group.GroupOrder
④ ②のAJAX通信が終了したら、JavaScriptを発火させ、隠しボタンを押下させる
⑤ ④で押下したボタンは同期通信なので、全てのUI情報がJAVA側に送信される
⑥ ③のルールでビーンバリデーションが発火します
⑦ ④のボタンの「action」属性を「submit()」に設定していればエラーが無い場合に限り発火します
それではサンプルです。
package view;
import java.io.IOException;
import java.io.Serializable;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.Flash;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import view.validator.group.Group1;
import view.validator.group.Group2;
@Named(value = "sampleBean")
@ViewScoped
public class SampleBean implements Serializable {
private static final long serialVersionUID = 1L;
@Getter
private String currentGroup = "jakarta.validation.groups.Default, view.validator.group.Group1";
@Getter
@Setter
@NotBlank(message = "{error.required.name}", groups = Group1.class)
@Size(min = 3, max = 10, message = "{error.Size.name}", groups = Group2.class)
private String name;
@Getter
@Setter
@NotBlank(message = "{error.required.pref}", groups = Group1.class)
@Max(message = "{error.max.pref}", value = 10, groups = Group2.class)
private String pref;
public void changeGroup1() {
this.currentGroup = "jakarta.validation.groups.Default, view.validator.group.Group1";
}
public void changeGroupOrder() {
this.currentGroup = "jakarta.validation.groups.Default, view.validator.group.GroupOrder";
}
public void submit() {
// ~ バリデーションが通過した時の処理 ~
System.out.println("処理実行!");
}
public String goPost() {
Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
flash.put("pram_name", "フラッシュスコープです");
return "next";
}
public String goGet() {
return String.format("next?faces-redirect=true&inpText=%s&munber=%d", this.name, 123);
}
public void goYahoo() {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
try {
String paramName = URLEncoder.encode("テスト", StandardCharsets.UTF_8.toString());
String targetUrl = "https://www.yahoo.co.jp/?q=" + paramName;
externalContext.redirect(targetUrl);
} catch (IOException e) {
e.printStackTrace();
}
}
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="jakarta.faces.core"
xmlns:h="jakarta.faces.html">
<h:head>
<title>Hello World</title>
<h:outputStylesheet library="css" name="style.css" />
<script>
function change_rule(data){
if (data.status === 'success') {
let btn = document.getElementById("kakushi");
btn.click();
}
}
</script>
</h:head>
<h:body>
<h1>おはよう世界!</h1>
<h:form id="sample_form" prependId="false">
<h:messages errorClass="alert-danger" warnClass="alert-warning" infoClass="alert-info" layout="list" />
<f:validateBean validationGroups="#{sampleBean.currentGroup}">
<h:inputText id="inpText" value="#{sampleBean.name}" />
<BR />
<h:inputText id="pref" value="#{sampleBean.pref}"/>
<BR />
<h:commandButton value="下書き" actionListener="#{sampleBean.changeGroup1()}">
<f:ajax execute="@this" onevent="change_rule" />
</h:commandButton>
<BR />
<h:commandButton value="送信して保存" actionListener="#{sampleBean.changeGroupOrder()}">
<f:ajax execute="@this" onevent="change_rule" />
</h:commandButton>
<BR />
<h:commandButton id="kakushi" value="隠し" action="#{sampleBean.submit()}" tabindex="-1" style="display: none;" />
<BR />
<h:commandButton value="POST繊維" action="#{sampleBean.goPost()}" />
<BR />
<h:commandButton value="GET繊維" action="#{sampleBean.goGet()}" />
<BR />
<h:commandButton value="外部繊維" action="#{sampleBean.goYahoo()}" />
</f:validateBean>
</h:form>
</h:body>
</html>
きっちりバリデーションルール通りですよね!
ルールが切り替わる仕組みは前述の通りです。
『AJAX』の部分に注目して下さい。『render』属性がありませんよね。
なので本当は『f:validateBean』タグに《id="valid"》とか付与して『render="valid"』とかしたかったんですが、問題ないみたいです。




<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="jakarta.faces.core"
xmlns:h="jakarta.faces.html">
<h:head>
<title>Hello World</title>
<h:outputStylesheet library="css" name="style.css" />
</h:head>
<h:body>
<h1>おはよう世界!</h1>
<h:form id="sample_form" prependId="false">
<h:messages errorClass="alert-danger" warnClass="alert-warning" infoClass="alert-info" layout="list" />
<f:validateBean validationGroups="view.validator.group.Group1">
<h:inputText id="inpText" value="#{sampleBean.name}" />
<BR />
<h:inputText id="pref" value="#{sampleBean.pref}"/>
<BR />
<h:commandButton value="下書き" action="#{sampleBean.submit()}">
<f:param name="actionType" value="DRAFT" />
</h:commandButton>
<BR />
<h:commandButton value="送信して保存" action="#{sampleBean.submit()}">
<f:param name="actionType" value="SAVE" />
</h:commandButton>
<BR />
<h:commandButton value="POST繊維" action="#{sampleBean.goPost()}" />
<BR />
<h:commandButton value="GET繊維" action="#{sampleBean.goGet()}" />
<BR />
<h:commandButton value="外部繊維" action="#{sampleBean.goYahoo()}" />
</f:validateBean>
</h:form>
</h:body>
</html>