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行为:
编辑:我们尝试过的一些事情:
我创建了一个只有一个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
)。
有些注意事项:
piggyBank.setAmount(456);
,它最初会在用户界面的TextField中显示€ 456,00
。piggyBank.setAmount(456);
,它仍会在UI的TextField中显示€ 1.234,00
!因此,在convertToPresentation
之后,转换器的binder.setBean(piggyBank)
方法不再被触发。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
(尽管它仍会错误地显示€ 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;
}
}