Reputation: 1480
I have simple entity (for example)
import java.util.Date;
class People implements Serializable{
...
private Date birthdate; //(getters, setters)
...}
UI code:
final Binder<People> binder = new Binder<People>(People.class); ...
People bean=new People();
binder.setBean(bean);
DateField birthdate = new DateField("date of birth");
binder.bind(birthdate, "birthdate");
When I select date from calendar in UI I get:
Caused by: java.lang.ClassCastException: Cannot cast java.time.LocalDate to java.util.Date
at java.lang.Class.cast(Class.java:3369)
at com.vaadin.data.Binder$BindingBuilderImpl.lambda$createConverter$f6099586$1(Binder.java:800)
at com.vaadin.data.Converter.lambda$null$fdd4de71$1(Converter.java:105)
at com.vaadin.data.Result.of(Result.java:91)
I tried to use
DateField birthdate = new DateField("birthdate");
binder.bind(birthdate, "birthdate");
binder.forField(birthdate).withConverter(new LocalDateToDateConverter());
but have same result. How to bind Date to DateField properly?
Upvotes: 3
Views: 1666
Reputation: 26
If you use the conversion frequently you can extend BeanValidationBinder
and "add" a custom Converter
to DefaultConverterFactory
, and use the extended MyBeanValidationBinder
instead of the BeanValidationBinder
(the example uses java.time.Instant
instead of java.util.Date
):
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.Optional;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Result;
import com.vaadin.flow.data.binder.ValueContext;
import com.vaadin.flow.data.converter.Converter;
import com.vaadin.flow.data.converter.ConverterFactory;
import com.vaadin.flow.data.converter.DefaultConverterFactory;
public class MyBeanValidationBinder<BEAN> extends BeanValidationBinder<BEAN> {
private static final long serialVersionUID = 1L;
public static class MyConverterFactory implements ConverterFactory {
private static final long serialVersionUID = 1L;
@Override
public <P, M> Optional<Converter<P, M>> newInstance(Class<P> presentationType, Class<M> modelType) {
Optional<Converter<P, M>> defaultConverter = DefaultConverterFactory.INSTANCE.newInstance(presentationType, modelType);
if (defaultConverter.isEmpty() && presentationType.isAssignableFrom(LocalDate.class) && modelType.isAssignableFrom(Instant.class) ) {
return Optional.of(new Converter<P, M>() {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unchecked")
@Override
public Result<M> convertToModel(P value, ValueContext context) {
if (value == null) {
return Result.ok(null);
}
LocalDate valueLocalDate = (LocalDate) value;
return Result.ok((M) valueLocalDate.atStartOfDay().toInstant(ZoneOffset.ofHours(0)));
}
@SuppressWarnings("unchecked")
@Override
public P convertToPresentation(M value, ValueContext context) {
if (value == null) {
return null;
}
Instant valueLocalInstant = (Instant) value;
return (P) LocalDate.ofInstant(valueLocalInstant, ZoneOffset.ofHours(0));
}});
}
return defaultConverter;
}
}
MyConverterFactory myConverterFactory;
public MyBeanValidationBinder(Class<BEAN> beanType) {
super(beanType);
myConverterFactory = new MyConverterFactory();
}
@Override
protected ConverterFactory getConverterFactory() {
return this.myConverterFactory;
}
}
Creating the Binder
:
final Binder<People> binder = new MyBeanValidationBinder<People>(People.class);
Upvotes: 0
Reputation: 2749
The problem is how you make use of the binder
. Instead try
DateField birthdate = new DateField("birthdate");
binder.forField(birthdate).withConverter(new LocalDateToDateConverter()).bind("birthdate");
The forField
method returns an object following the builder design pattern. That means you call some (chained) methods on that object and finish it by a call to bind
.
Upvotes: 3