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

标签 java converter vaadin vaadin7 vaadin8

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

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" ,它将在 TextField 中显示 € 1.234,00。这一切都很好。
如果您将此初始值更改为 "987" ,与转换器的绑定(bind)将在对象中保存“987”,并在您键入 TextField 后立即在 TextField 中显示 € 987,00

在我们的 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 中的行为相同,但它的行为如下:

When you load the page with this converter, and the initial value was null, it would show nothing in the TextField. If the initial value that was previously saved was "1234", it would show € 1.234,00 in the TextField. Which is all good and well.
If you would change this initial value to "987", the binding with converter would save "987" in the object and immediately show € 987,00 in the TextField, as soon as you type in the TextField.



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

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

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

enter image description here

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

我创建了一个只有一个 TextField、一个绑定(bind)和一个转换器的测试页:
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);,它最初会在 UI 的 TextField 中显示 € 456,00
  • 如果我们在第 2 行而不是第 1 行添加 piggyBank.setAmount(456);,它仍然会在 UI 的 TextField 中显示 € 1.234,00!所以转换器的 convertToPresentation 方法在 binder.setBean(piggyBank) 之后根本不会被触发。
  • 如果我们在第 1 行添加 piggyBank.setAmount(456);,并删除 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(尽管在编号列表的情况 2 和 3 中它仍然错误地显示 € 1.234,00 而不是 € 456,00多于)。

    我们还尝试了内联 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;
        }
    }
    

    最佳答案

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

    关于java - Vaadin 8 Converter 的行为与 Vaadin 7 Converter 不同(不更新 UI)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44116251/

    相关文章:

    currency - 需要用于货币转换的 API

    java - Vaadin datefield 组件设置默认日期

    java - ResultSet 以 CSV 格式返回空白列

    java - Android Studio错误 "Cannot resolve method for method .add"

    android - 如何在 Android 中将 Html 文本转换为纯文本?

    r - 为数值因子水平赋值

    java - Vaadin 消耗 REST 和性能

    java - 如何在 Vaadin Binder 中确认验证

    java - 在Java中为数组对象赋值

    java - java比较两个字符串的元素