Java JSF2-为什么呈现响应不重新呈现组件设置?
我遇到以下问题: 恢复视图后,字段的验证会导致JSF跳到呈现响应阶段(因为必填字段为空)。但即使呈现当前值(空字符串)以向用户显示他/她没有填充任何内容,也不会执行以下语句: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
<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> *
<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> *
<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>