我有一个JSF 2.2应用程序,用户必须在BigDecimal
中输入<h:inputText>
值。对于这样的输入字段,valueChangeListener
被配置为在输入变化时被调用。这是XHTML代码:
<h:form id="theForm">
<h:inputText id="bd" value="#{bean.bd}"
valueChangeListener="#{bean.bdChangedListener()}"/>
<h:commandButton id="submit" value="Submit" />
</h:form>
在大多数情况下这很好用。当值更改并按下提交按钮时,将调用bdChangedListener()
方法。该值已正确提交给模型。
但是,如果我输入1.1
并将其更改为1.10
,新值将提交给模型,但valueChangeListener
从未调用! Debuging表明,原因在于javax.faces.component.UIInput#compareValues()
。来自的JavaDoc
this method说:
如果新值与先前值不同,则返回true。 首先通过将值传递给equals方法来比较这两个值 以前的论点。如果该方法返回true,则返回true。如果说 方法返回false,并且两个参数都实现 java.lang.Comparable,通过将值传递给比较两个值 compareTo方法在参数之前。如果这个方法返回true 返回0,否则返回false。
所以在我看来,这是故意的。但为什么呢?
用户输入已更改,并且存在应用程序,其中BigDecimal
的比例是相关的。所以JSF不应该忽略改变的输入,而是通知我!它使用新值更新模型,为什么它会跳过valueChangeListener-method?
我怎样才能避免这种行为并以干净的方式得到通知? (我知道我可以加入制定者,但那不是我所说的干净的方式!)
有什么想法吗?
除了上述内容之外,我想提一下,我已经阅读了BigDecimal equals() versus compareTo()之类的问题。我理解为什么BigDecimal的equals()
和compareTo()
表现得像他们一样,我说它是正确的。 BigDecimal的行为不是问题,UIInput.compareValues()
就是问题。
此外,转换器(在评论或已删除的答案中建议)也不会节省我的一天。用户输入已正确转换,我需要BidDecimal,包括我的应用程序中的确切比例。任何返回BigDecimal的转换器都不会改变观察到的行为。
围绕BigDecimal的包装类可能会解决我的问题,但不是我认为的清洁解决方案。我真的想知道为什么UIInput的行为方式。
答案 0 :(得分:4)
确实表现得如规定。就是这样。您已经非常正确地将根本原因确定为javax.faces.component.UIInput#compareValues()
。解决方案是让它跳过额外的Comparable#compareTo()
检查。
您最好的选择是延长HtmlInputText
并相应地覆盖compareValues()
。
@FacesComponent(createTag=true)
public class InputBigDecimal extends HtmlInputText {
@Override
protected boolean compareValues(Object previous, Object value) {
return !Objects.equals(previous, value);
}
}
然后只需将<h:inputText>
替换为<x:inputBigDecimal>
,如下所示。
<... xmlns:x="http://xmlns.jcp.org/jsf/component">
...
<h:form id="theForm">
<x:inputBigDecimal id="bd" value="#{bean.bd}"
valueChangeListener="#{bean.bdChangedListener()}" />
<h:commandButton id="submit" value="Submit" />
</h:form>
注意:代码按原样完成。由于JSF 2.2的createTag=true
,不需要额外的XML配置文件。你只会错过IDE的XML intellisense,但这是一个不同的问题。
答案 1 :(得分:1)
BalusC的答案描述了一种通用的方法,可以在不改变应用程序行为的情况下以干净的方式解决问题。可行的Antoher解决方法是使用AJAX - listener
而不是valueChangeListener
:
<h:form id="theForm">
<h:inputText id="bd" value="#{bean.bd}">
<f:ajax listener="#{bean.ajaxListener}"/>
</h:inputText>
<h:commandButton id="submit" value="Submit" />
</h:form>
listener
方法必须具有不同的签名:
public void ajaxListener(AjaxBehaviorEvent event)
{
// Do whatever you like here.
// The new value is already in the model.
}
请注意,一旦DOM更改事件触发,就会调用AJAX侦听器(而不是按下sumbit按钮时),并且不会检查实际值是否发生变化,如valueChangeListener
所示。
有关这两种方法差异的详细信息可以在BalusC对When to use valueChangeListener or f:ajax listener?的出色答案中找到。