Vaadin 8 Converter 的行为与 Vaadin 7 Converter 不同(不更新 UI)?

Kev*_*sen 5 java converter vaadin vaadin7 vaadin8

TL;DR:Vaadin 8 中是否有类似于 Vaadin 7 的转换器来更新 UI 中输入字段的表示?即在输入字段失去焦点后立即从用户输入中删除所有非数字,或将小数转换为货币?

Vaadin 论坛链接:http : //vaadin.com/forum#!/ thread/ 15931682


我们目前正在将 Vaadin 7 项目迁移到 vaadin 8。

在我们的 Vaadin 7 应用程序中,我们有几个转换器。例如这个 CurrencyConverter:

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;
import org.apache.commons.lang3.StringUtils;
import com.vaadin.v7.data.util.converter.StringToBigDecimalConverter;
import com.vaadin.v7.data.util.converter.Converter.ConversionException;

/**
 * A converter that adds/removes the euro sign and
 * formats currencies with two decimal places.
 */
public class EuroConverter extends StringToBigDecimalConverter {

    @Override
    public BigDecimal convertToModel(String value,
                                     Class<? extends BigDecimal> targetType,
                                     Locale locale) throws ConversionException {
        if(StringUtils.isBlank(value)) {
            return null;
        }
        value = value.replaceAll("[€\\s]", "").trim();
        if(StringUtils.isBlank(value)) {
            value = "0";
        }
        return super.convertToModel(value, targetType, locale);
    }

    @Override
    public String convertToPresentation(BigDecimal value,
                                    Class<? extends String> targetType, 
                                    Locale locale) throws ConversionException {
        if(value == null) {
            return null;
        }
        return "€ " + super.convertToPresentation(value, targetType, locale);
    }

    @Override
    protected NumberFormat getFormat(Locale locale) {
        // Always display currency with two decimals
        NumberFormat format = super.getFormat(locale);
        if(format instanceof DecimalFormat) {
            ((DecimalFormat)format).setMaximumFractionDigits(2);
            ((DecimalFormat)format).setMinimumFractionDigits(2);
        }
        return format;
    }
}
Run Code Online (Sandbox Code Playgroud)

当您使用此转换器加载页面并且初始值为 时null,它不会在 TextField 中显示任何内容。如果之前保存的初始值是"1234",它将显示€ 1.234,00在 TextField 中。这一切都很好。
如果您将此初始值更改为"987",则与转换器的绑定将在对象中保存“987”并€ 987,00在您输入 TextField 后立即显示在 TextField 中。

在我们的 Vaadin 8 迁移中,我们已将其更改为:

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;
import org.apache.commons.lang3.StringUtils;
import com.vaadin.data.Result;
import com.vaadin.data.ValueContext;
import com.vaadin.data.converter.StringToBigDecimalConverter;

/**
 * A converter that adds/removes the euro sign and formats currencies with two
 * decimal places.
 */
public class EuroConverter extends StringToBigDecimalConverter {

    public EuroConverter() {
        super("defaultErrorMessage");
    }

    public EuroConverter(String errorMessage) {
        super(errorMessage);
    }

    @Override
    public Result<BigDecimal> convertToModel(String value, ValueContext context) {
        if(StringUtils.isBlank(value)){
            return Result.ok(null);
        }
        value = value.replaceAll("[€\\s]", "").trim();
        if(StringUtils.isBlank(value)){
            value = "0";
        }
        return super.convertToModel(value, context);
    }

    @Override
    public String convertToPresentation(BigDecimal value, ValueContext context) {
        if(value == null){
            return convertToPresentation(BigDecimal.ZERO, context);
        }
        return "€ " + super.convertToPresentation(value, context);
    }

    @Override
    protected NumberFormat getFormat(Locale locale) {
        // Always display currency with two decimals
        NumberFormat format = super.getFormat(locale);
        if(format instanceof DecimalFormat){
            ((DecimalFormat)format).setMaximumFractionDigits(2);
            ((DecimalFormat)format).setMinimumFractionDigits(2);
        }
        return format;
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望与 Vaadin 7 中的行为相同,但它的行为如下:

当您使用此转换器加载页面并且初始值为 时null,它不会在 TextField 中显示任何内容。如果之前保存的初始值是"1234",它将显示€ 1.234,00在 TextField 中。这一切都很好。
如果您将此初始值更改为"987",则与转换器的绑定将在对象中保存“987”€ 987,00您输入TextField 后立即显示TextField 中。

它不会更新 TextField 中的值。它仍然会显示987。似乎 Vaadin 8 转换器仅用于通过绑定从用户输入到域模型,而不是更改已放入的内容。

另一个变化是转换器现在放在 Vaadin 8 中的 Binding 本身,而不是 Vaadin 7 中的 TextField。

这是一个带有 Vaadin 7 行为的 gif/我在顶部输入字段中寻找的内容,以及底部输入字段中当前 Vaadin 8 的行为:

在此处输入图片说明


编辑:我们尝试过的一些事情:

我创建了一个只有一个 TextField、一个绑定和一个 Converter 的测试页面:

TextField inputField = new TextField("Test");
Binder<PiggyBank> binder = new Binder<PiggyBank>();

// PiggyBank only has a property `amount` with getter & setter (see below)
PiggyBank piggyBank = new PiggyBank();
piggyBank.setAmount(BigDecimal.valueOf(1234))

binder.forField(inputField)
      .withConverter(new EuroConverter())
      .bind(PiggyBank::getAmount, PiggyBank::setAmount);
//1
binder.setBean(piggyBank);
//2

addComponent(inputField);
Run Code Online (Sandbox Code Playgroud)

这将€ 1.234,00最初显示,当您键入时"987",它将写入987对象(但仍会显示987而不是€ 987,00)。

一些注意事项:

  1. 如果我们piggyBank.setAmount(456);在第 1 行添加,它将€ 456,00最初显示在 UI 的 TextField 中。
  2. 如果我们piggyBank.setAmount(456);在第 2 行而不是第 1 行添加,它仍然会显示€ 1.234,00在 UI 的 TextField 中!所以转换器的convertToPresentation方法在binder.setBean(piggyBank).
  3. 如果我们piggyBank.setAmount(456);在第 1 行添加并删除binder.setBean(piggyBank);它仍然显示€ 1.234,00而不是€ 456,00!

我们已经找到了一个丑陋的解决方法,如下所示:

inputField.setValueChangeMode(ValueChangeMode.BLUR);
inputField.addBlurListener(new BlurListener() {

    @Override
    public void blur(BlurEvent event) {
        if(binder.isValid()) {
            binder.readBean(binder.getBean());
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

如您所见,我们只是在每次输入字段值更改时读取 bean。通过此更改,该值仍然正确保存到模型中,并且现在也在此之后触发convertToPresentation转换器的方法,正确显示€ 987,00(尽管它仍然不正确地显示,€ 1.234,00而不是€ 456,00在上面编号列表的情况 2 和 3 中)。

我们还尝试了内联 lambda 转换器而不是对象;尝试一些事情binder.forMemberField;尝试一些事情binder.bindInstanceFields;试图以某种方式检索设置的活页夹,这是不可能的;等等等等。我们已经尝试了各种方法,但是 Vaadin 8 转换器无法像在 Vaadin 7 中那样工作,我们无法为此找到替代方案,甚至找不到合适的解决方法。


PiggyBank 代码应要求在评论中提供:

public class PiggyBank {

    private BigDecimal amount;

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount){
        this.amount = amount;
    }
}
Run Code Online (Sandbox Code Playgroud)

Tat*_*und 1

此问题已在 Vaadin 8.12.1 中修复。参考号 https://github.com/vaadin/framework/pull/12132