当前位置: 首页 > 知识库问答 >
问题:

Vaadin8转换器的行为与Vaadin7转换器不同(不更新UI)?

姜良哲
2023-03-14

TL;DR:在Vaadin8中有类似于Vaadin7的转换器的东西来更新UI中输入字段的表示吗?即。在输入字段失去焦点后立即从用户输入中删除所有非数字,还是将十进制转换为货币?

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

我们目前正在将Vaadin7项目迁移到Vaadin8。

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

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

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

另一个变化是,转换器现在放在Vaadin8中的绑定本身上,而不是Vaadin7中的TextField上。

下面是一个gif,上面的输入字段是Vaadin7/what的行为,下面的输入字段是当前的Vaadin8的行为:

编辑:我们尝试过的一些操作:

我创建了一个只有一个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. 如果我们在第1行添加piggybank.setamount(456);,它将首先在UI的TextField中显示456,00
  2. 如果我们在第2行而不是第1行添加piggybank.setamount(456);,它仍然会在UI的TextField中显示1.234,00!因此在binder.setBean(piggyBank)
  3. 之后,转换器的 converttopresentation方法就不再被触发了
  4. 如果在第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;试图以某种方式检索设置的活页夹,这是不可能的;等等,我们已经尝试了所有的方法,但是Vaadin8转换器不像在Vaadin7中那样工作,我们无法找到一个替代的甚至是适当的解决方案。

备注中的储蓄罐代码:

public class PiggyBank {

    private BigDecimal amount;

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount){
        this.amount = amount;
    }
}

共有1个答案

宫坚
2023-03-14

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

 类似资料:
  • 获取以下错误: 请帮我把结果设置到这个BO类中。

  • 我试图在java中实现不同类型之间的转换器:我有一个超级类foo,有2个子类: fo1和fo2,我也有2个不相关的其他类bar1和bar2,我试图实现一个转换器从bar1到fo1和bar2to fo2(与之相反)。 因为我在这里有继承,所以我想应该创建一个泛型类型的接口(将是bar1或bar2),它将定义转换所需的函数,我还想让转换器成为bar1和bar2的通用转换器,我认为它应该以某种方式使用接

  • 我正试图从Netsuite中获取数据,并使用Mulesoft Dataweave元素和when将其保存到salesforce对象中,否则,请参见下面的代码片段,但给出了错误: 找不到将“SimpleDataType{type=java.util.LinkedHashMap,mimetype='/',encoding='null'}”转换为“CollectionDataType{type=java.

  • 考虑下面这个简短的C++程序: 如果我在不同的编译器上编译它,我会得到不同的结果。对于CLANG3.4和GCC 4.4.7,它打印,而Visual Studio 2013打印,这意味着它们在调用不同的强制转换操作符。根据标准,哪一个是正确的行为? 根据我的理解,不需要转换,而需要到的转换,因此编译器应该选择第一个。对此做了什么吗?const-conversion是否被编译器认为更“昂贵”? 如果删

  • 最近我读到了关于运算符执行的智能强制转换,以及关于或更好的运算符用于显式强制转换。 kotlin docs的种类及其使用差异如下:- 请注意,当编译器无法保证变量不能在检查和使用之间更改时,智能强制转换不起作用。更具体地说,智能强制转换根据以下规则适用: > val局部变量-始终不包括局部委托属性; val属性-如果属性是私有的或内部的,或者检查是在声明属性的同一模块中执行的。智能强制转换不适用于

  • 我使用ModelMapper将一些对象转换为复杂的DTO,反之亦然。 尽管我试图理解留档,但我发现很难理解何时使用转换器、提供程序或抽象转换器。 现在,例如,如果我想将字符串属性转换为目标DTO中的小DTO,我将在抽象转换器中手动执行。 例如: 虽然这是正确的方法吗?我应该何时使用提供商? 如果我想用条件设置属性,我可以在转换器中使用条件吗?或者只有在使用PropertyMap时才能使用条件? 此