Open mvysny opened 1 year ago
Workaround: clear Binder.validators
via reflection, then call validate()
, then populate Binder.validators
back. I'll prototype this solution then I'll post a demo code.
Example code of a buffered binder which:
public class BinderUtils {
/**
* Writes values to given bean, but only if all validations pass. If any of the validation fails, a nice validation error message
* is shown to the user.
* @param binder the binder, not null.
* @param bean the bean to write the values to, not null.
* @param <T> the type of the bean
* @return true if everything went okay and the new values are written to the bean, false if not.
*/
public static <T> boolean writeBeanIfValid(@NotNull Binder<T> binder, @NotNull T bean) {
// temporarily remove bean-level validators. Workaround for https://github.com/vaadin/flow/issues/15701
final List<Validator<? super T>> beanValidators = getBeanValidators(binder);
setBeanValidators(binder, new ArrayList<>());
final boolean isFieldLevelValidationsOk;
try {
isFieldLevelValidationsOk = binder.validate().isOk();
} finally {
setBeanValidators(binder, beanValidators);
}
String errorMessage = null;
try {
if (isFieldLevelValidationsOk) {
binder.writeBean(bean);
// all OK, return true.
return true;
}
} catch (ValidationException ex) {
if (!ex.getBeanValidationErrors().isEmpty()) {
final ValidationResult err = ex.getBeanValidationErrors().get(0);
errorMessage = err.getErrorMessage();
}
}
if (errorMessage == null || errorMessage.isBlank()) {
errorMessage = tr(I18NCommon.ERRORS_IN_FORM);
}
MessageNotification.warningMessage(errorMessage);
return false;
}
@SuppressWarnings("unchecked")
@NotNull
private static <T> List<Validator<? super T>> getBeanValidators(@NotNull Binder<T> binder) {
try {
final Field f = Binder.class.getDeclaredField("validators");
f.setAccessible(true);
return new ArrayList<>((List<Validator<? super T>>) f.get(binder));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static <T> void setBeanValidators(@NotNull Binder<T> binder, @NotNull List<Validator<? super T>> validators) {
try {
final Field f = Binder.class.getDeclaredField("validators");
f.setAccessible(true);
f.set(binder, new ArrayList<>(validators));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
Describe your motivation
Vaadin 14.9.4.
I am using binder in a buffered mode:
binder.readBean(bean)
if (binder.validate().isOk() && binder.writeBeanIfValid(bean)) { dialog.close() } else {Notification.show("Errors in the form"); }
I have to call
binder.validate()
otherwise theHasValue.errorMessage
wouldn't get populated.The problem is that
binder.validate()
fails in buffered mode withDescribe the solution you'd like
validate()
works now, the change is nowhere being big.validate(bean)
would run bean-level validations and roll-back changes afterwards. Also works for me.setBeanLevelValidationsEnabled(false)
function which temporarily disables bean-level validations, so thatvalidate()
can be called.Essentially a reopen of https://github.com/vaadin/flow/issues/13393
I don't think that
Binder.setValidationStatusHandler(...)
would be helpful in this case:validate()
at some point, in order to populateHasValue.errorMessage
binder.writeBeanIfValid(bean)
), I'm hitting a problem soon: there's no way to roll back the changes done to the bean.