2

次の問題が発生しています。

ビューが復元された後、フィールドの検証により、JSF は Render response フェーズにスキップします (必須フィールドが空であるため)。ただし、現在の値 (空の文字列) がレンダリングされて、ユーザーが何も入力していないことをユーザーに示す場合でも、次のステートメントは実行されません。

 <c:if test="#{empty cc.attrs.fieldValue}">
     <f:attribute name="style" value="background-color: yellow;"/>
 </c:if>

それはバグですか、それとも機能ですか?助けてください。

完全なテスト例 (Netbeans 6.8 プロジェクト) はこちら: http://www.221b.cz/so/JSFTester.zip

チュートリアルから: 「リクエストがポストバックで、リクエスト値の適用フェーズ、プロセス検証フェーズ、またはモデル値の更新フェーズでエラーが発生した場合、元のページはレンダリング レスポンス フェーズでレンダリングされます」( http://java.sun .com/javaee/5/docs/tutorial/doc/bnaqq.html )

ビューが「ビューの復元」フェーズで復元され、その後、リクエスト/検証/更新モデルの適用フェーズが失敗し、「レスポンスのレンダリング」にスキップすると、レンダリング レスポンスはクライアントに変更を加えずに復元されたビューのみを渡すということですか?

マネージド Bean (TesterBean.java):

package cz.test;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class TesterBean {

// Simple DataStore (in real world EJB)
private static String storedSomeValue = null;

private String someValue;

public TesterBean() {
}

public String storeValue() {
    storedSomeValue = someValue;
    return "index";
}

public String eraseValue() {
    storedSomeValue = null;
    return "index";
}

public String getSomeValue() {
    someValue = storedSomeValue;
    return someValue;
}

public void setSomeValue(String someValue) {
    this.someValue = someValue;
}    

}

複合コンポーネント (field-component.xhtml):

<?xml version='1.0' encoding='ISO-8859-1' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:composite="http://java.sun.com/jsf/composite"
  xmlns:c="http://java.sun.com/jsp/jstl/core">

<!-- INTERFACE -->
<composite:interface>
    <composite:attribute name="currentBehaviour" type="java.lang.String" required="true"/>
    <composite:attribute name="fieldValue" required="true"/>
</composite:interface>

<!-- IMPLEMENTATION -->
<composite:implementation>
    <h:panelGrid columns="3">
        <c:choose>
            <c:when test="#{cc.attrs.currentBehaviour == 'READONLY'}" >
                <h:outputText id="fieldValue" value="#{cc.attrs.fieldValue}">
                </h:outputText>
            </c:when>
            <c:when test="#{cc.attrs.currentBehaviour == 'MANDATORY'}" >
                <h:inputText id="fieldValue" value="#{cc.attrs.fieldValue}" required="true">
                    <f:attribute name="requiredMessage" value="Field is mandatory"/>
                    <c:if test="#{empty cc.attrs.fieldValue}">
                        <f:attribute name="style" value="background-color: yellow;"/>
                    </c:if>
                </h:inputText>&nbsp;*
            </c:when>
            <c:when test="#{cc.attrs.currentBehaviour == 'OPTIONAL'}" >                    
                <h:inputText id="fieldValue" value="#{cc.attrs.fieldValue}">                        
                </h:inputText>                    
            </c:when>
        </c:choose>
        <h:message for="fieldValue" style="color:red;" />
    </h:panelGrid>
</composite:implementation>

ページ (index.xhtml):

<?xml version='1.0' encoding='UTF-8' ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:h="http://java.sun.com/jsf/html"      
   xmlns:ez="http://java.sun.com/jsf/composite/components">

<h:head>
    <title>Testing page</title>
</h:head>
<h:body>
    <h:form>                        
        <h:outputText value="Some value:"/>
        <ez:field-component currentBehaviour="MANDATORY" fieldValue="#{testerBean.someValue}"/>           
        <h:commandButton value="Store" action="#{testerBean.storeValue}"/>
        <h:commandButton value="Erase" action="#{testerBean.eraseValue}" immediate="true"/>
    </h:form>

    <br/><br/>
    <b>Why is field's background color not set to yellow?</b>
    <ol>
        <li>NOTICE: Field has yellow background color (mandatory field with no value)</li>
        <li>Fill in any value (eg. "Hello") and press Store</li>
        <li>NOTICE: Yellow background disappeared (as mandatory field has value)</li>
        <li>Clear text in the field and press Store</li>
        <li><b>QUESTION: Why is field's background color not set to yellow?</b></li>
        <li>Press Erase</li>
        <li>NOTICE: Field has yellow background color (mandatory field with no value)</li>
    </ol>
</h:body>

編集、ブライアンの提案 (field-component.xhtml) に従って

<?xml version='1.0' encoding='ISO-8859-1' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:composite="http://java.sun.com/jsf/composite">

<!-- INTERFACE -->
<composite:interface>
    <composite:attribute name="currentBehaviour" type="java.lang.String" required="true"/>
    <composite:attribute name="fieldValue" required="true"/>
</composite:interface>

<!-- IMPLEMENTATION -->
<composite:implementation>
    <h:panelGrid columns="3">
        <h:outputText rendered="#{cc.attrs.currentBehaviour == 'READONLY'}" id="fieldValue1" value="#{cc.attrs.fieldValue}" />

        <h:inputText rendered="#{cc.attrs.currentBehaviour == 'MANDATORY'}" id="fieldValue2" title="#{cc.attrs.fieldValue}" value="#{cc.attrs.fieldValue}" required="true" style="#{empty cc.attrs.fieldValue ? 'background-color: yellow;' : ''}">
            <f:attribute name="requiredMessage" value="Field is mandatory"/>
        </h:inputText>&nbsp;*

        <h:inputText rendered="#{cc.attrs.currentBehaviour == 'OPTIONAL'}" id="fieldValue3" value="#{cc.attrs.fieldValue}"/>

        <h:message for="fieldValue" style="color:red;" />
    </h:panelGrid>
</composite:implementation>

しかし、JSTL を削除してもまだ機能しません:-( h:inputText の http リクエストからの新しい値で value 属性のみが更新されているようですが、残りの属性はフェーズ Render Response で再評価されません。

4

3 に答える 3

3

使用している<c:choose>および<c:when>タグは、JSF タグではなく JSTL タグです。これは、レンダリング時ではなくビルド時に評価されることを意味します。ポストバックすると、コンポーネント ツリーは再構築されず、再レンダリングされ、<c:タグは再評価されません。

<h:panelGroup rendered="#{}">タグの代わりにタグを使用して、例をもう一度試してください<c:

詳細については、この記事を参照してください: http://drewdev.blogspot.com/2008/03/build-time-vs-render-time.html

JSF フォームのポストバックでコンポーネントを「再表示」することはできないことに注意してください。これは、状態が保存されてから状態が復元されるまでの間、JSF コンポーネント ツリーを変更してはならないためです。これは非常に重要なことです。もう一度言いますが、JSF コンポーネント ツリーは、状態が保存されてから復元されるまでの間、決して変更されるべきではありません。

于 2010-06-10T06:22:11.463 に答える
1

過去 2 日間でいくつかの調査とデバッグを行いましたが、これが私の結果です。

まず、複合コンポーネントを省略し、できるだけ単純にするために例を単純化しました。

マネージド Bean (TesterBean2.java)

package cz.test;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class TesterBean2 {

// Simple DataStore (in real world EJB)
private static String storedSomeValue = null;

private String someValue;

public TesterBean2() {
}

public String storeValue() {
    storedSomeValue = someValue;
    return "index";
}

public String eraseValue() {
    storedSomeValue = null;
    return "index";
}

public String getSomeValue() {
    someValue = storedSomeValue;
    return someValue;
}

public void setSomeValue(String someValue) {
    this.someValue = someValue;
}
}

テストページ (index.xhtml)

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html">

<h:head>
    <title>Testing page</title>
</h:head>
<h:body>
    <h:form>
        <h:inputText id="fieldValue" requiredMessage="Field is mandatory" title="#{testerBean2.someValue}" value="#{testerBean2.someValue}" required="true" style="#{empty testerBean2.someValue ? 'background-color: yellow;' : ''}"/>
        <h:commandButton value="Store" action="#{testerBean2.storeValue}"/>
        <h:commandButton value="Erase" action="#{testerBean2.eraseValue}"/>
    </h:form>
</h:body>

問題はどこだ?com.sun.faces.renderkit_html_basic.TextRenderer とそのメソッド getEndTextToRender の問題だと思います:

protected void getEndTextToRender(FacesContext context,
                                  UIComponent component,
                                  String currentValue)
      throws IOException {

    ResponseWriter writer = context.getResponseWriter();
    assert(writer != null);
    boolean shouldWriteIdAttribute = false;
    boolean isOutput = false;

    String style = (String) component.getAttributes().get("style");
    String styleClass = (String) component.getAttributes().get("styleClass");
    String dir = (String) component.getAttributes().get("dir");
    String lang = (String) component.getAttributes().get("lang");
    String title = (String) component.getAttributes().get("title");
    if (component instanceof UIInput) {
        writer.startElement("input", component);
        writeIdAttributeIfNecessary(context, writer, component);
        writer.writeAttribute("type", "text", null);
        writer.writeAttribute("name", (component.getClientId(context)),
                              "clientId");

        // only output the autocomplete attribute if the value
        // is 'off' since its lack of presence will be interpreted
        // as 'on' by the browser
        if ("off".equals(component.getAttributes().get("autocomplete"))) {
            writer.writeAttribute("autocomplete",
                                  "off",
                                  "autocomplete");
        }

        // render default text specified
        if (currentValue != null) {
            writer.writeAttribute("value", currentValue, "value");
        }

   // Rest of code omitted 
}

currentValueパラメータは、com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd から呼び出されるこのメソッドに明示的に渡されます。

@Override
public void encodeEnd(FacesContext context, UIComponent component)
      throws IOException {

   rendererParamsNotNull(context, component);

    if (!shouldEncode(component)) {
        return;
    }

    ResponseWriter writer = context.getResponseWriter();
    assert(writer != null);

    // NOTICE currentValue getter
    String currentValue = getCurrentValue(context, component);
    if (logger.isLoggable(Level.FINE)) {
        logger.log(Level.FINE,
                   "Value to be rendered {0}",
                   currentValue);
    }
    // NOTICE currentValue
    getEndTextToRender(context, component, currentValue);

}

getCurrentValue() メソッドを詳しく見てみると、

/**
 * @param context the FacesContext for the current request
 * @param component the UIComponent whose value we're interested in
 *
 * @return the value to be rendered and formats it if required. Sets to
 *  empty string if value is null.
 */
protected String getCurrentValue(FacesContext context,
                                 UIComponent component) {

    if (component instanceof UIInput) {
        Object submittedValue = ((UIInput) component).getSubmittedValue();
        if (submittedValue != null) {
            // value may not be a String...
            return submittedValue.toString();
        }
    }

    String currentValue = null;
    Object currentObj = getValue(component);
    if (currentObj != null) {
        currentValue = getFormattedValue(context, component, currentObj);
    }
    return currentValue;

}

getSubmittedValue()によって返されるプロパティは、ビューの復元フェーズで入力されます (プロセスの検証フェーズが応答のレンダリング フェーズにスキップする場合)。結果として、値属性についてのみユーザーから渡された「更新された」値を取得し、残りは変更されません。

プロセス検証フェーズが成功した場合、Render Response フェーズへの直接スキップは発生しません (ユーザーが null 以外の値を入力した場合)、HtmlInputText の新しいコンストラクターが呼び出され、スタイル、タイトルなどの属性が最初から入力されます。これらの属性は、モデル値の更新フェーズで適切なデータで更新されたマネージド Bean から入力されます。

OK、これはバグではなく機能です。「リクエストがポストバックで、リクエスト値の適用フェーズ、プロセスの検証フェーズ、またはモデル値の更新フェーズでエラーが発生した場合、元のページはレスポンスのレンダリングフェーズでレンダリングされます」 .

必須フィールドの黄色の背景が本当に欲しい場合、この状況を解決する方法の手がかりはありますか?

更新されたプロジェクトはこちら: http://www.221b.cz/so/JSFTester2.zip

于 2010-06-11T09:05:45.187 に答える
1

私はついに検証を機能させることができました。

UIComponentにアクセスできるバリデーターを使用しました。検証に失敗した場合、特別なスタイルがコンポーネントに適用されます。このスタイルは、Render レスポンス フェーズでも考慮されます。

それで、それはどのように振る舞いますか?

  1. style="#{empty testerBean2.someValue ? 'background-color: yellow;' を含むビューが復元されます。: ''}"
  2. 検証に合格しません。そのため、testerBean2.someValue は更新されません (モデル値の更新フェーズがスキップされるため) が、RequiredValidator を使用して定数スタイルが h:inputText に設定されます - component.setValueExpression("style", new ValueExpressionLiteral("background-color: yellow;", String.クラス));
  3. Render Response では、必要なバリデータが既に定数 new ValueExpressionLiteral("background-color: yellow;", String.class) を設定しているため、testerBean.someValue が更新されていない場合でも黄色の背景が適用されます。

必要な独自のバリデーターを実装しました ( http://www.codereye.com/2009/12/validating-empty-text-field-using-jsf.htmlの Bashan のバリデーターに触発されました)。

RequiredValidator.java

package cz.test;

import javax.faces.application.FacesMessage;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import org.apache.el.ValueExpressionLiteral;

public class RequiredValidator implements Validator {

public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
    if (value == null || "".equals(value.toString().trim())) {
        FacesMessage message = new FacesMessage();
        String messageStr = (String) component.getAttributes().get("message");
        if (messageStr == null) {
            messageStr = "Please enter data";
        }
        message.setDetail(messageStr);
        message.setSummary(messageStr);
        message.setSeverity(FacesMessage.SEVERITY_ERROR);
        component.setValueExpression("style", new ValueExpressionLiteral("background-color: yellow;", String.class));
        throw new ValidatorException(message);
    } else {
        component.setValueExpression("style", new ValueExpressionLiteral("", String.class));
    }
}
}

追加した行: component.setValueExpression("style", new ValueExpressionLiteral("background-color: yellow;", String.class));

空のフィールド (web.xml) に対して JSF トリガーの検証を強制するには:

....
<context-param>
    <param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
    <param-value>true</param-value>
</context-param>
....

バリデーターを JSF (faces-config.xml) に登録するには:

<validator>
    <validator-id>RequiredValidator</validator-id>
    <validator-class>cz.test.RequiredValidator</validator-class>
</validator>

必要なバリデーターと TesterBean2 (index.xhtml) を使用する Web ページ:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core">

<h:head>
    <title>Testing page</title>
</h:head>
<h:body>
    <h:form>
        <h:messages/>
        <h:inputText id="fieldValue"                          
                     title="#{testerBean2.someValue}"
                     value="#{testerBean2.someValue}"                         
                     style="#{empty testerBean2.someValue ? 'background-color: yellow;' : ''}">
            <f:validator validatorId="RequiredValidator"/>
            <f:attribute name="message" value="Field is mandatory"/>
        </h:inputText>

        <h:commandButton value="Store" action="#{testerBean2.storeValue}"/>
        <h:commandButton value="Erase" action="#{testerBean2.eraseValue}"/>
    </h:form>
</h:body>
</html>

注意: h:inputText で必須属性を使用することはできません。必要なバリデーターを追い越します。

于 2010-06-14T14:28:47.467 に答える