繁体   English   中英

JSF2-为什么渲染响应不重新渲染组件设置?

[英]JSF2 - Why does render response not rerender component setting?

我遇到以下问题:

恢复我的视图后,对字段的验证会导致JSF跳至“渲染”响应阶段(因为必填字段为空)。 但是,即使呈现了当前值(空字符串)以向用户显示他/她没有填写任何内容,也不会执行以下语句:

 <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>

按照Brian的建议进行编辑(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,仍然无法正常工作:-(似乎只有value属性用h:inputText中的http请求中的新值更新了,但是其余属性在阶段渲染响应中没有重新评估。

您使用的<c:choose><c:when>标签是JSTL标签,而不是JSF标签。 这意味着它们在构建时而不是在渲染时进行评估。 回发时,不会重建组件树,而是会重新渲染它,并且不会重新评估<c:标记。

使用<h:panelGroup rendered="#{}">标记而不是<c:标记再次尝试您的示例。

有关更多详细信息,请参见此文章http : //drewdev.blogspot.com/2008/03/build-time-vs-render-time.html

重要的是要记住,您不能在JSF表单的回发中“重新出现”组件。 这是因为在保存状态和恢复状态之间绝不能更改JSF组件树。 这非常重要,因此让我再说一遍,在保存状态和恢复状态之间,绝不能更改JSF组件树。

最近两天我做了一些调查和调试,这是我的结果。

首先,我简化了示例,以省略复合组件并使它尽可能简单。

托管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 
}

从com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd调用的此方法中显式传递了currentValue参数。

@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()返回的属性将填充到“还原视图”阶段(如果“流程验证”阶段跳至“渲染”响应阶段)。 结果,我们得到“更新的”值,该值仅从用户传递给value属性,其余保持不变。

如果成功的过程验证阶段不会导致直接跳到“渲染响应”阶段(如果用户填写了任何非null值),则将调用HtmlInputText的新构造函数,并从头开始填充样式,标题等 这些属性是从托管bean填充的,该bean在阶段Update Model Values中已使用适当的数据更新。

好的,这不是错误,而是功能。 它仅肯定了我的论点,即句子中有些气味:“如果请求是回发,并且在应用请求值阶段,过程验证阶段或更新模型值阶段遇到错误,则原始页面将在“渲染”响应阶段呈现。” 。

如果我真的想要必填字段的黄色背景,如何解决这种情况的任何线索?

更新的项目在这里: http : //www.221b.cz/so/JSFTester2.zip

我终于设法使验证工作。

我使用了可以访问UIComponent的验证器。 如果验证失败,则将特殊样式应用于组件。 在渲染响应阶段也考虑了此样式。

那么它的表现如何呢?

  1. 恢复的视图包括样式style =“#{empty testerBean2.someValue?'background-color:yellow;' :''}“
  2. 验证未通过。 因此,不会更新testerBean2.someValue(因为跳过了更新模型值阶段),但是使用RequiredValidator将常量样式设置为h:inputText-component.setValueExpression(“ style”,new ValueExpressionLiteral(“ background-color:yellow;”,String)。类));
  3. 在“渲染响应”中,即使尚未更新testerBean.someValue,也应用了黄色背景,因为“必填验证器”已设置了常量new ValueExpressionLiteral(“ background-color:yellow;”,String.class)

我已经实现了自己所需的验证器(灵感来自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):

<?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中不能使用必需的属性。 它将超出必需的验证器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM