p:selectOneMenu

时间:2016-02-05 10:36:08

标签: jsf primefaces converter bigdecimal

转换器:

@FacesConverter("bigDecimalConverter")
public class BigDecimalConverter implements Converter {

    private static final int SCALE = 2;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {

        if (value == null || value.isEmpty()) {
            return null;
        }

        try {
            return new BigDecimal(value);
        } catch (NumberFormatException e) {
            throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, null, "Message"), e);
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {

        if (value == null) {
            return "";
        }

        BigDecimal newValue;

        if (value instanceof Long) {
            newValue = BigDecimal.valueOf((Long) value);
        } else if (value instanceof Double) {
            newValue = BigDecimal.valueOf((Double) value);
        } else if (!(value instanceof BigDecimal)) {
            throw new ConverterException("Message");
        } else {
            newValue = (BigDecimal) value;
        }

        DecimalFormat formatter = (DecimalFormat) NumberFormat.getNumberInstance();
        formatter.setGroupingUsed(false);
        formatter.setMinimumFractionDigits(SCALE);
        formatter.setMaximumFractionDigits(SCALE);
        return formatter.format(newValue);
    }
}

列表:

<p:selectOneMenu id="list" value="#{bean.value}">
    <f:selectItems var="row" value="#{bean.list}" itemLabel="#{row}" itemValue="#{row}"/>
    <f:converter converterId="bigDecimalConverter"/>
</p:selectOneMenu>

<p:message id="msg" for="list"/>
<p:commandButton value="Submit" update="list msg" actionListener="#{bean.action}"/>

上述<p:selectOneMenu>支持的托管bean:

@ManagedBean
@ViewScoped
public class Bean implements Serializable {

    private List<BigDecimal> list; // Getter only.
    private BigDecimal value; // Getter & setter.
    private static final long serialVersionUID = 1L;

    public Bean() {}

    @PostConstruct
    private void init() {
        list = new ArrayList<BigDecimal>(){{
            add(BigDecimal.valueOf(10));
            add(BigDecimal.valueOf(20.11));
            add(BigDecimal.valueOf(30));
            add(BigDecimal.valueOf(40));
            add(BigDecimal.valueOf(50));
        }};
    }

    public void action() {
        System.out.println("action() called : " + value);
    }
}

验证消息,&#34;验证错误:值无效&#34;在提交表格时出现。表单提交时getAsObject()方法不会抛出任何异常。

如果在列表中选择了具有20.11等比例的值,则验证通过。似乎equals()类中的java.math.BigDecimal方法变得可疑,例如,认为两个BigDecimal个对象相等,只要它们的值和规模相等{{1}这需要10.0 != 10.00才能使它们相等。

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

转换为String时丢失信息。默认的JSF BigDecimalConverter执行此操作,它在BigDecimal#toString中使用getAsStringBigDecimal#toString的javadoc说:

  

可区分的BigDecimal值与此转换的结果之间存在一对一的映射关系。也就是说,每个可区分的BigDecimal值(未缩放的值和比例)都具有使用toString的结果的唯一字符串表示形式。如果使用BigDecimal(String)构造函数将该字符串表示形式转换回BigDecimal,则将恢复原始值。

这正是您所需要的。不要对转换器进行处理,因为它们在转换为String时必须产生用户可读写的结果。他们一定不能也常常不会。它们生成对象的String表示或对象引用。而已。在这种情况下,selectItems'itemLabel定义了用户可读的表示。我假设您不想在此处使用用户可写值,您确实有一个固定的值列表供用户选择。

如果你的意思是这个数据必须始终具有2的标度,并且你需要一个用户可写值,那么在验证器中可以更好地检查它,并且p:inputMask可以帮助输入用户输入。

最后,让我们撇开你的转换器不是最好的事实。它说“数据的比例必须为2”。然后,您应该在selectItems中提供符合要求的数据。更一般地,服务器定义的值必须符合相关的转换器和验证器。例如。当使用带有模式DateTimeConverter的{​​{1}}时,你可能会遇到同样的问题,但是在没有删除时间部分的情况下将默认值设置为"dd.MM.yyyy"

另请参阅(有关转换器的更一般概念):https://stackoverflow.com/a/30529976/1341535