第11章 Seam での JSF フォーム検証


純粋な JSF では検証はビューで定義されます。
<h:form>
  <h:messages/>

  <div>
    Country:
    <h:inputText value="#{location.country}" required="true">
      <my:validateCountry/>
    </h:inputText>
  </div>
    
  <div>
    Zip code:
    <h:inputText value="#{location.zip}" required="true">
      <my:validateZip/>
    </h:inputText>
  </div>

  <h:commandButton/>
</h:form>
Copy to Clipboard Toggle word wrap
実際にはこの方法は通常 DRY に違反しています。データモデルの一部であり、データベーススキーマの定義全体にわたって存在する制約をほとんどの「検証」が実際は強制実行するためです。Seam は Hibernate Validator を使って定義されるモデルベースの制約に対するサポートを提供しています。
Location クラスで制約を定義するところから始めましょう。
public class Location {
    private String country;
    private String zip;
  
    @NotNull
    @Length(max=30)
    public String getCountry() { return country; }
    public void setCountry(String c) { country = c; }

    @NotNull
    @Length(max=6)
    @Pattern("^\d*$")
    public String getZip() { return zip; }
    public void setZip(String z) { zip = z; }
}
Copy to Clipboard Toggle word wrap
実際には Hibernate Validator に組み込みされたものではなく、カスタムな制約を使う方がスマートかもしれません。
public class Location {
    private String country;
    private String zip;
  
    @NotNull
    @Country
    public String getCountry() { return country; }
    public void setCountry(String c) { country = c; }

    @NotNull
    @ZipCode
    public String getZip() { return zip; }
    public void setZip(String z) { zip = z; }
}
Copy to Clipboard Toggle word wrap
いずれの方法を取るにしても、 JSF ページ内で使用される検証のタイプを指定する必要はありません。代わりに、<s:validate> を使ってモデルオブジェクトで定義される制約に対して検証を行います。
<h:form>
  <h:messages/>

  <div>
    Country:
    <h:inputText value="#{location.country}" required="true">
      <s:validate/>
    </h:inputText>
  </div>
  
  <div>
    Zip code:
    <h:inputText value="#{location.zip}" required="true">
      <s:validate/>
    </h:inputText>
  </div>
  
  <h:commandButton/>

</h:form>
Copy to Clipboard Toggle word wrap

注記

このモデルで @NotNull を指定してもコントロールに出現させるのに required="true" が必要なくなるというわけではありません。これは JSF 検証アーキテクチャの限界によるものです。
この方法によりモデルで制約を定義して、 ビューで制約違反を提示します。
デザインはよくなりましたが、 最初のデザインと比べてそれほど冗長性が軽減されているわけではありません。<s:validateAll> を使ってみます。
<h:form>
    
  <h:messages/>

  <s:validateAll>

  <div>
    Country:
    <h:inputText value="#{location.country}" required="true"/>
  </div>

  <div>
    Zip code:
    <h:inputText value="#{location.zip}" required="true"/>
  </div>

  <h:commandButton/>

  </s:validateAll>

</h:form>
Copy to Clipboard Toggle word wrap
このタグは <s:validate> をフォーム内のすべての入力に追加します。 フォームが大きくなる場合は、 入力の手間をかなり省くことができます。
次に、 検証が失敗した場合にユーザーにフィードバックを表示させる必要があります。 現在すべてのメッセージはフォームの冒頭に表示されます。 メッセージと入力を関連付けられるようにするためには、 入力コンポーネントで標準の label 属性を使いラベルを定義する必要があります。
<h:inputText value="#{location.zip}" required="true" label="Zip:"> 
  <s:validate/> 
</h:inputText>
Copy to Clipboard Toggle word wrap
プレースホルダーの {0} (Hiberate Validator の制約用に JSF メッセージに渡される最初で唯一のパラメータ) を使ってこの値をメッセージ文字列にインジェクトします。これらのメッセージを定義する場所の詳細は「国際化」の項をご覧ください。

注記

validator.length={0} length must be between {min} and {max}
エラーがあるフィールドの隣にメッセージを表示させ、 フィールドとラベルをハイライトさせて、フィールドの隣にイメージを表示させたいとします。純粋な JSF で可能なのは最初のメッセージの表示のみです。 また、 必須フォームの各フィールドにはラベルの隣に色の付いたアスタリスクを表示させたいとします。
これは各フィールドにとって多くの機能となります。 フォームにあるすべてのフィールドそれぞれに対してイメージ、 メッセージ、 入力フィールドのレイアウトやハイライトを指定したいとは思わないでしょうから、Facelets テンプレートにそのレイアウトを指定します。
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:s="http://jboss.com/products/seam/taglib">           
  <div>
  
    <s:label styleClass="#{invalid?'error':''}">
      <ui:insert name="label"/>
      <s:span styleClass="required" rendered="#{required}">*</s:span>
    </s:label>
        
    <span class="#{invalid?'error':''}">
      <h:graphicImage value="/img/error.gif" rendered="#{invalid}"/>
      <s:validateAll>
        <ui:insert/>
      </s:validateAll>
    </span>
        
    <s:message styleClass="error"/>
        
  </div>
    
</ui:composition>
Copy to Clipboard Toggle word wrap
<s:decorate> を使って各フォームフィールドにこのテンプレートを含ませることができます。
<h:form>
  <h:messages globalOnly="true"/>

    <s:decorate template="edit.xhtml">
        <ui:define name="label">Country:</ui:define>
        <h:inputText value="#{location.country}" required="true"/>
    </s:decorate>
    
    <s:decorate template="edit.xhtml">
        <ui:define name="label">Zip code:</ui:define>
        <h:inputText value="#{location.zip}" required="true"/>
    </s:decorate>

    <h:commandButton/>

</h:form>
Copy to Clipboard Toggle word wrap
最後に、ユーザーがフォーム内を移動しながら RichFaces Ajax を使って検証メッセージを表示させることができます。
<h:form>
  <h:messages globalOnly="true"/>

    <s:decorate id="countryDecoration" template="edit.xhtml">
        <ui:define name="label">Country:</ui:define>
        <h:inputText value="#{location.country}" required="true">
            <a:support event="onblur" reRender="countryDecoration" 
                       bypassUpdates="true"/>
        </h:inputText>
    </s:decorate>
    
    <s:decorate id="zipDecoration" template="edit.xhtml">
        <ui:define name="label">Zip code:</ui:define>
        <h:inputText value="#{location.zip}" required="true">
            <a:support event="onblur" reRender="zipDecoration" 
                       bypassUpdates="true"/>
        </h:inputText>
    </s:decorate>

    <h:commandButton/>

</h:form>
Copy to Clipboard Toggle word wrap
重要なページのコントロールには明示的な ID を定義すると便利なスタイルになります。 特に UI 用の自動テストを行いたい場合などに適しています。 明示的な ID を与えないと、 JSF はそれらを生成しますがページ上で変更があると静的なままにはなりません。
<h:form id="form">
  <h:messages globalOnly="true"/>

    <s:decorate id="countryDecoration" template="edit.xhtml">
    <ui:define name="label">Country:</ui:define>
      <h:inputText id="country" value="#{location.country}" 
                   required="true">
        <a:support event="onblur" reRender="countryDecoration" 
                   bypassUpdates="true"/>
      </h:inputText>
    </s:decorate>
    
    <s:decorate id="zipDecoration" template="edit.xhtml">
      <ui:define name="label">Zip code:</ui:define>
      <h:inputText id="zip" value="#{location.zip}" required="true">
        <a:support event="onblur" reRender="zipDecoration" 
                   bypassUpdates="true"/>
      </h:inputText>
    </s:decorate>

    <h:commandButton/>

</h:form>
Copy to Clipboard Toggle word wrap
検証が失敗したときに表示させるメッセージを変えたい場合、 Seam メッセージバンドルを Hibernate Validator で使用することができます。
public class Location {
    private String name;
    private String zip;
  
    // Getters and setters for name

    @NotNull
    @Length(max=6)
    @ZipCode(message="#{messages['location.zipCode.invalid']}")
    public String getZip() { return zip; }
    public void setZip(String z) { zip = z; }
}
Copy to Clipboard Toggle word wrap
 location.zipCode.invalid = The zip code is not valid for #{location.name}
Copy to Clipboard Toggle word wrap
トップに戻る
Red Hat logoGithubredditYoutubeTwitter

詳細情報

試用、購入および販売

コミュニティー

Red Hat ドキュメントについて

Red Hat をお使いのお客様が、信頼できるコンテンツが含まれている製品やサービスを活用することで、イノベーションを行い、目標を達成できるようにします。 最新の更新を見る.

多様性を受け入れるオープンソースの強化

Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。このような変更は、段階的に実施される予定です。詳細情報: Red Hat ブログ.

会社概要

Red Hat は、企業がコアとなるデータセンターからネットワークエッジに至るまで、各種プラットフォームや環境全体で作業を簡素化できるように、強化されたソリューションを提供しています。

Theme

© 2025 Red Hat