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

时间:2017-05-22 15:01:21

标签: 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;
    }
}

使用此转换器加载页面时,初始值为null,它将在TextField中不显示任何内容。如果之前保存的初始值为"1234",则会在TextField中显示€ 1.234,00。这一切都很好 如果要将此初始值更改为"987",则只要您键入TextField,与转换器的绑定就会在对象中保存“987”,并立即在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中的行为相同,但它的行为如下:

  

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

它不会更新TextField中的值..它仍会显示987。看起来好像Vaadin 8转换器只是通过绑定从用户输入到域模型使用,而不是改变已放入的内容。

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

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

enter image description here

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

我创建了一个只有一个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);

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

有些注意事项:

  1. 如果我们在第1行添加piggyBank.setAmount(456);,它最初会在用户界面的TextField中显示€ 456,00
  2. 如果我们在第2行而不是第1行添加piggyBank.setAmount(456);,它仍会在UI的TextField中显示€ 1.234,00!因此,在convertToPresentation之后,转换器的binder.setBean(piggyBank)方法不再被触发。
  3. 如果我们在第1行添加piggyBank.setAmount(456);并删除binder.setBean(piggyBank);,则仍会显示€ 1.234,00而不是€ 456,00
  4. 我们已经找到了一个丑陋的解决办法,如下:

    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在上面编号列表的第2和第3例中€ 456,00。{/ p>

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

1 个答案:

答案 0 :(得分:1)

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