我有一个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