Java JSF2-为什么呈现响应不重新呈现组件设置?

Java JSF2-为什么呈现响应不重新呈现组件设置?,java,jsf,jsf-2,Java,Jsf,Jsf 2,我遇到以下问题: 恢复视图后,字段的验证会导致JSF跳到呈现响应阶段(因为必填字段为空)。但即使呈现当前值(空字符串)以向用户显示他/她没有填充任何内容,也不会执行以下语句: <c:if test="#{empty cc.attrs.fieldValue}"> <f:attribute name="style" value="background-color: yellow;"/> </c:if> 复合组件(fieldcomponent.xht

我遇到以下问题:

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

 <c:if test="#{empty cc.attrs.fieldValue}">
     <f:attribute name="style" value="background-color: yellow;"/>
 </c:if>
复合组件(fieldcomponent.xhtml):


*

页面(index.xhtml):


测试页


为什么字段的背景色没有设置为黄色?
  • 注意:字段背景颜色为黄色(必填字段,无值)
  • 填写任何值(如“Hello”)并按Store
  • 注意:黄色背景消失(因为必填字段有值)
  • 清除字段中的文本,然后按Store
  • 问题:为什么字段的背景色没有设置为黄色
  • 按擦除
  • 注意:字段背景颜色为黄色(必填字段,无值)
  • 根据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,仍然无法工作:-(在h:inputText中,似乎只有value属性用http请求中的新值更新,但其余属性不会在phase Render Response中重新求值。

    您使用的
    标记是JSTL标记,而不是JSF标记。这意味着它们在构建时求值,而不是在渲染时求值。当您发回时,组件树没有得到重建,而是得到了重新呈现,
    我在过去两天做了一些调查和调试,这是我的结果

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

    托管bean(TesterBean2.java)

    测试页面(index.xhtml)

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

    getSubmittedValue()返回的属性在还原视图阶段填充(如果流程验证阶段跳过呈现响应阶段)。因此,我们得到“更新”值,该值仅从用户传递给value属性,其余值保持不变

    在流程验证阶段成功的情况下,这不会导致直接跳过呈现响应阶段(如果用户填写任何非空值),调用HtmlInputText的新构造函数,并从头开始填充样式、标题等属性。这些属性由托管bean填充,托管bean在阶段更新模型值时使用适当的数据进行更新

    好的,这不是一个bug,而是一个特性。它只是证实了我的论点,即句子中有某种味道:“如果请求是回发,并且在应用请求值阶段、流程验证阶段或更新模型值阶段遇到错误,则在呈现响应阶段呈现原始页面”

    如果我真的希望必填字段有黄色背景,有什么线索可以解决这个问题吗


    更新的项目在这里:

    我终于设法让验证工作开始了

    我使用了可以访问UIComponent的validator。如果验证失败,将对组件应用特殊样式。在呈现响应阶段也会考虑这种样式

    那么它是如何表现的呢

  • 视图已还原,包括样式样式=“#{empty testerBean2.someValue?”背景色:黄色;':''}”
  • 验证未通过。因此testerBean2.someValue未更新(因为更新模型值阶段被跳过),但使用RequiredValidator-component.setValueExpression(“样式”,新的ValueExpressionLiteral(“背景色:黄色;”,String.class))将常量样式设置为h:inputText;
  • 在呈现响应中,即使testerBean.someValue尚未更新,也会应用黄色背景,因为验证器已经设置了常量new ValueExpressionLiteral(“背景颜色:黄色;”,String.class) 我已经实现了自己所需的验证器(受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(“样式”,新的ValueExpressionLiteral(“背景色:黄色;”,String.class));

    要强制对空字段(web.xml)进行JSF触发器验证,请执行以下操作:

    。。。。
    javax.faces.VALIDATE_空_字段
    真的
    ....
    
    要向JSF(faces config.xml)注册验证器,请执行以下操作:

    
    必需验证器
    cz.test.RequiredValidator
    
    以及使用所需验证器和TesterBean2(index.xhtml)的网页:

    
    测试页
    

    注意:h:inputText中不能使用required属性。它将超过required validator。

    谢谢你,Brian。我按照你的建议尝试实现field-component.xhtml。我也阅读了你的博客文章(很好,但对我来说很难)。我根据你的建议编辑了这个问题,但仍然不起作用:-(我在h:inputText中添加了标题属性,以表明在呈现响应阶段,新值(cc.attrs.fieldValue)未应用于h:inputText属性。这是我第一次填写“Hello”->Stor
    <?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>
    
    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;
    }
    }
    
    <?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>
    
    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 
    }
    
    @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);
    
    }
    
    /**
     * @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;
    
    }
    
    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));
        }
    }
    }
    
    ....
    <context-param>
        <param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
        <param-value>true</param-value>
    </context-param>
    ....
    
    <validator>
        <validator-id>RequiredValidator</validator-id>
        <validator-class>cz.test.RequiredValidator</validator-class>
    </validator>
    
    <?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>