JSF2のシステムイベントで入力値の相関チェック

前回はFaceletsのテンプレートとシステムイベントを用いたサンプルでしたが今回はシステムイベントで入力値の相関チェックを行うサンプルを紹介します。

JSFにはフェーズという概念が存在し、モデルの更新や、バリデーションチェック、アクションの実行、Viewのレンダリングなどがそれぞれ独立したフェーズで実行されます。

バリデーションチェックを行うフェーズでは、入力値個別のチェックを行うことは可能ですが、複数の入力値の相関チェックを行うことは、基本的にはできません。

ですが、システムイベントのPostValidateをうまく利用すると、相関チェックが可能となります。

例えば入力値1と2の合計が上限値を超えていた場合の確認等。

まずViewを以下のように定義します。

...
<h:form>
    ...
    <h:panelGrid id="inputValues" columns="2">
        <f:event listener="#{addBean.validate}" type="postValidate" />
        <h:outputLabel value="値1" />
        <h:inputText id="value1" value="#{addBean.value1}" required="true" />
        <h:outputLabel value="値2" />
        <h:inputText id="value2" value="#{addBean.value2}" required="true"/>
    </h:panelGrid>
    <h:message for="inputValues"/>
    <h:commandButton action="add" value="合計" />
</h:form>
...

panelGridの定義直下にと定義していますが、これがpostValidateイベントのリスナ定義です。

このpostValidateイベントはその名の通りバリデーションが実行された後に、登録されているリスナの実行を行うイベントです。

リスナのメソッドは次の通り。

@Named
public class AddBean {
    ...

    public void validate(ComponentSystemEvent e) {
        UIComponent component = e.getComponent();
        UIInput value1 = (UIInput)component.findComponent("value1");
        UIInput value2 = (UIInput)component.findComponent("value2");
        int ret = add(((Short)value1.getLocalValue()).shortValue(),
                      ((Short)value2.getLocalValue()).shortValue());
        if (ret > 10) {
            // 10より大きい場合はチェックエラー
            FacesContext context = FacesContext.getCurrentInstance();
            FacesMessage message = new FacesMessage(
                    Integer.toString(ret) + " is greater than 10.", 
                    Integer.toString(ret) + " is greater than 10.");
            message.setSeverity(FacesMessage.SEVERITY_ERROR);
            context.addMessage(component.getClientId(), message);
            context.renderResponse();
        }
    }

    public static int add(short v1, short v2) {
        return v1 + v2;
    }
    ...
}

バリデーション実行後にvalidateメソッドがシステムイベントによって実行されます。

validateメソッド内ではコンポーネントツリーよりidがvalue1とvalue2の値を取得して合計値を計算し、値が10より大きければチェックエラーとして処理しています。

PostValidateイベントを利用している理由は、まず通常のバリデーションで各項目のチェック(NotNullなど)を行った後でないと相関チェックを行う意味がないからです。

ポイントは最後のcontext.renderResponse();です。この1文が重要で、これは、エラーとなったら即座に最後のRenderResponseフェーズまで飛んで、Viewのレンダリングを行うということを意味しています。

サンプルは非実用的ですが、このシステムイベントはうまく利用すれば、実開発においてある種、統一的な仕組みを提供することができるのではないでしょうか。