5

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;
    }
}

当您使用此转换器加载页面时,初始值为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;
    }
}

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

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

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

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

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

在此处输入图像描述


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

我创建了一个只有一个 TextField、一个绑定和一个转换器的测试页:

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);

这将€ 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());
        }
    }
});

如您所见,我们只是在每次输入字段值更改时读取 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;
    }
}
4

1 回答 1

1

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

于 2020-12-30T14:26:01.353 回答