我有一个JSF 2.2应用程序,用户必须输入BigDecimal值<h:inputText>.对于这样的输入字段,a valueChangeListener被配置为在输入变化时被调用.这是XHTML代码:
<h:form id="theForm">
<h:inputText id="bd" value="#{bean.bd}"
valueChangeListener="#{bean.bdChangedListener()}"/>
<h:commandButton id="submit" value="Submit" />
</h:form>
Run Code Online (Sandbox Code Playgroud)
在大多数情况下,这样做很好.bdChangedListener()当值改变并按下提交按钮时,将调用该方法.该值已正确提交给模型.
但是,如果我进入1.1并改变它1.10,新的值被提交到模型,但valueChangeListener在从来没有所谓!Debuging表明,其原因在于javax.faces.component.UIInput#compareValues().这种方法的JavaDoc
说:
如果新值与先前值不同,则返回true.首先通过将值传递给参数previous之前的equals方法来比较这两个值.如果该方法返回true,则返回true.如果该方法返回false,并且两个参数都实现java.lang.Comparable,则通过将值传递给参数previous之前的compareTo方法来比较这两个值.如果此方法返回0,则返回true,否则返回false.
所以在我看来,这是故意的.但为什么?
用户输入已更改,并且存在应用程序,其中a的比例BigDecimal是相关的.所以JSF不应该忽略改变的输入,而是通知我!它使用新值更新模型,为什么它会跳过valueChangeListener-method?
我怎样才能避免这种行为并以干净的方式得到通知?(我知道我可以挂进制定者,但这不是我所谓的干净方式!)
有任何想法吗?
除了上面我想提到的,我已经读过像BigDecimal equals()和compareTo()这样的问题.我确实理解为什么BigDecimal equals()和compareTo()他们的行为一样,我说它是正确的.BigDecimal的行为不是问题,UIInput.compareValues()而是问题.
此外,转换器(如评论或已删除的答案中所示)将无法保存我的一天.用户输入已正确转换,我需要BidDecimal,包括我的应用程序中的确切比例.任何返回BigDecimal的转换器都不会改变观察到的行为.
围绕BigDecimal的包装类可能会解决我的问题,但不是我认为的干净解决方案.我真的想知道为什么UIInput的行为方式.
我使用OmniFaces的<o:validateAll>验证器来验证许多输入组件.只要我没有将它放入RichFaces <rich:tabPanel>,这就可以正常工作.当我这样做并将字段留空时,验证失败(如预期的那样),但无论验证失败,活动选项卡都会更改.我试过的其他验证器会tabPanel在验证失败时阻止切换到另一个选项卡.
这可能是什么原因?
我目前在Wildfly 9.0.2上使用OmniFaces 2.1和RichFaces 4.5.17.Final与Mojarra 2.2.12.
以下是重现问题的XHTML代码:
<ui:composition xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:o="http://omnifaces.org/ui"
xmlns:rich="http://richfaces.org/rich">
<h:form id="form">
<rich:messages />
<rich:tabPanel id="tabPanel">
<rich:tab id="tab1" header="Tab 1">
<h:inputText id="myDouble" value="#{someDoubleVal}">
<f:validateDoubleRange minimum="1.0" maximum="2.0"/>
</h:inputText>
<o:validateAll id="allValid" components="myDouble" message="Missing value!" />
</rich:tab>
<rich:tab id="tab2" header="Tab 2">
Just another tab to switch.
</rich:tab>
</rich:tabPanel>
</h:form>
</ui:composition>
Run Code Online (Sandbox Code Playgroud)
输入1.0和2.0之外的值并尝试切换到选项卡2以查看预期行为,由<f:validateDoubleRange>以下各项触发:显示faces-message并且第一个选项卡仍处于活动状态.
将输入留空并尝试切换到选项卡2以查看以下行为<o:validateAll>:验证似乎失败(显示面部消息),但激活了选项卡2.
更新:所描述的行为适用于switchType="ajax"(默认)以及switchType="server".在这两种情况下,选项卡面板都会执行所包含输入的提交,因此从用户的角度来看,选项卡切换似乎与<h:commandButton>提交相同(技术上可能存在差异,我不知道实现选项卡面板的详细信息).
如果我通过定期执行制表开关<h:commandButton>用<f:setPropertyActionListener>,所述<o:validateAll>的行为相同的方式,其他的验证,即,在制表开关不会由于验证错误执行.
<rich:tabPanel …Run Code Online (Sandbox Code Playgroud) 我真正想要的是两个PrimeFaces<p:pickList>,它们通过OmniFaces 的<o:validateAll>组件进行验证。请注意,验证pickList<o:validateAll>有一个问题,可以按照OmniFaces 问题跟踪器中的问题488中的描述解决该问题。
因此,我的需求的一个非常简单的示例如下所示:
<h:form id="form1">
<p:messages id="messages">
<p:autoUpdate/>
</p:messages>
<p:pickList id="pick1" value="#{dummy.dualListModel}"
var="item" itemLabel="#{item}" itemValue="#{item}">
<p:ajax event="transfer"/>
</p:pickList>
<p:pickList id="pick2" value="#{dummy.dualListModel2}"
var="item" itemLabel="#{item}" itemValue="#{item}">
<p:ajax event="transfer"/>
</p:pickList>
<o:validateAll id="validPicks" components="pick1 pick2"
message="all values required!" />
<h:commandButton id="done" value="Done" action="#{dummy.action1}"/>
</h:form>
<h:form id="theOtherForm">
<h:commandButton id="otherFormAction" value="Action in other form"
action="#{dummy.action2}"/>
</h:form>
Run Code Online (Sandbox Code Playgroud)
后面的虚拟backing-bean 只为这两个dualListModel属性和什么都不做的动作方法提供 getter/setter 。
当我运行此代码并至少保留一个 pickList 为空时,提交Done-button 会导致验证失败作为例外。但是,单击按钮,在其他形式的后验证失败,导致一NullPointerException中PickListRenderer …
在我的应用程序中,我有一个键(字符串)列表,用户可以在其中选择其中一个.在用户界面中,将根据当前区域设置输出密钥:
<h:selectOneMenu value="#{bean.selectedKey}">
<f:selectItems value="#{bean.allKeys}" var="_key" itemLabel="#{msgs[_key]}" />
</h:selectOneMenu>
Run Code Online (Sandbox Code Playgroud)
我的设置使用配置的标准资源包,faces-config.xml如BalusC 在本回答中所述.msgs在上面的示例中是资源包变量的名称.
我现在selectOneMenu想要的是,要按字母顺序排序的项目.当然,顺序取决于使用的区域设置.问题是,我不能/不会在backing-bean中进行排序,因为我不知道JSF页面将如何输出密钥.
这个问题对我来说似乎很通用,所以我想知道解决这类问题的最佳做法是什么.
(当然问题不仅适用于selectOneMenu.任何将在用户界面中输出的列表/集合都会遇到同样的问题.)
在将大型 JEE8 应用程序移植到 Java 17 时,我IllegalAccessException在渲染一个简单的 EL 表达式时偶然发现了一个:#{myWarBean.defaultTZ.rawOffset}。我设法在github 上的 SSCCE中重现了该问题。当您在 Wildfly 应用程序服务器(我使用的是 26.1.1.Final)上运行应用程序时,您会得到以下堆栈跟踪:
SEVERE [javax.enterprise.resource.webcontainer.jsf.application] (default task-1) Error Rendering View[/index.xhtml]: javax.el.ELException: /index.xhtml @23,74 value="raw offset=#{myWarBean.defaultTZ.rawOffset}": java.lang.IllegalAccessException: class javax.el.BeanELResolver cannot access class sun.util.calendar.ZoneInfo (in module java.base) because module java.base does not export sun.util.calendar to unnamed module @6a1cb0de
at com.sun.jsf-impl@2.3.17.SP01//com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:77)
at javax.faces.api@3.1.0.SP01//javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
at javax.faces.api@3.1.0.SP01//javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:181)
at javax.faces.api@3.1.0.SP01//javax.faces.component.UIOutput.getValue(UIOutput.java:140)
at com.sun.jsf-impl@2.3.17.SP01//com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:198)
at com.sun.jsf-impl@2.3.17.SP01//com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:328)
at com.sun.jsf-impl@2.3.17.SP01//com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:143)
at javax.faces.api@3.1.0.SP01//javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:600)
at com.sun.jsf-impl@2.3.17.SP01//com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:286)
at com.sun.jsf-impl@2.3.17.SP01//com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:90)
at javax.faces.api@3.1.0.SP01//javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:571)
at javax.faces.api@3.1.0.SP01//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1648)
at javax.faces.api@3.1.0.SP01//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1651)
at javax.faces.api@3.1.0.SP01//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1651) …Run Code Online (Sandbox Code Playgroud) jsf ×5
omnifaces ×2
el ×1
java-17 ×1
java-module ×1
localization ×1
primefaces ×1
richfaces ×1
sorting ×1
validation ×1