'vaadin cross form validation fails as bean not bound
<vaadin.version>22.0.7</vaadin.version>
I have built a vaadin edit form with an attached Save button. When the user clicks 'Save' I want to validate the form and then save the validated data to the bean.
This works as expected.
The problem comes when I went to add in cross field validation. In this case I want to validate that a start/end date pair are in the correct order.
The problem is that when I add the form validator, vaadin starts throwing exceptions when I call validate.
@Override
public void bindFields(CrudFieldBinder<DiscountCode> binder)
{
binder.forField(this.discountCode).asRequired("Please enter the unique Discount Code.").bind(
DiscountCode::getDiscountCode,
DiscountCode::setDiscountCode);
binder.forField(this.startDate).asRequired("Please enter a Start Date.").bind(DiscountCode::getStartDate,
DiscountCode::setStartDate);
binder.forField(this.endDate).asRequired("Please enter an End Date.")
.bind(DiscountCode::getEndDate,
DiscountCode::setEndDate);
binder.withValidator(new DateRangeValidator());
}
I've tried a few variations all with the same result. Here is the latest iteration:
protected void saveEdits(E currentEntity) {
try
{
binder.writeBean(currentEntity);
}
catch (ValidationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
// this line throws the below error
BinderValidationStatus<E> status = binder.validate();
}
The call to writeBean runs without error but the call to binder.validate() fails with:
java.lang.IllegalStateException: Cannot validate binder: bean level validators have been configured but no bean is currently set
at com.vaadin.flow.data.binder.Binder.validate(Binder.java:2479) ~[flow-data-9.0.8.jar:9.0.8]
at com.vaadin.flow.data.binder.Binder.validate(Binder.java:2463) ~[flow-data-9.0.8.jar:9.0.8]
... EditorFormLayout.saveEdits(EditorFormLayout.java:92) ~[classes/:?]
This seems to suggest that form level validation only works if you make a call to setBean, however my understanding is the call to setBean will result in the form autosaving rather than waiting for the user to click the save button.
Solution 1:[1]
I don't believe there is a supported method for doing this.
I've raised a feature request here: https://github.com/vaadin/platform/issues/2868
Here is my hack that relies on overloading the binder and making the validation method think a bean is bound.
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.BinderValidationStatus;
public class MyBinder<E> extends Binder<E>
{
public MyBinder()
{
}
/**
* Apparently when doing cross field validation by calling
* binder.withValidator there is no way to the list of errors back
* due to the following issue.
* https://github.com/vaadin/platform/issues/2868
*
* Which essentially says that unless you are using setBean you can't
* do cross form validation.
*
* This code caches the bean sent to the binder when readBean is called
* and then uses that bean to fake a bound bean during validation.
*
*/
private boolean validating = false;
private E bean;
@Override
public void readBean(E bean)
{
this.bean = bean;
super.readBean(bean);
}
@Override
public void setBean(E bean)
{
throw new RuntimeException("The MyBinder only works with read/writeBean");
}
@Override
public E getBean()
{
if (validating)
{
return bean;
}
/// this call should always return null as setBean hasn't been
// called but we do this try to reduce the likelihood of this overload
// causing problems if someone accicentially uses this class
// when using setBean.
return super.getBean();
}
@Override
public BinderValidationStatus<E> validate()
{
try
{
// force getBean to return a bean during validation.
validating = true;
return super.validate();
}
finally
{
validating = false;
}
}
}```
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Brett Sutton |
