如果仅更改了比例,则不会调用BigDecimal值的ValueChangeListener

时间:2017-04-12 08:28:21

标签: jsf

我有一个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的行为方式。

2 个答案:

答案 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?的出色答案中找到。