This patch adds an API to Binding for accessing the validation status handlers from a custom BinderValidationStatusHandler.tags/8.2.0.alpha2
@@ -126,6 +126,15 @@ public class Binder<BEAN> implements Serializable { | |||
*/ | |||
public BindingValidationStatus<TARGET> validate(); | |||
/** | |||
* Gets the validation status handler for this Binding. | |||
* | |||
* @return the validation status handler for this binding | |||
* | |||
* @since 8.2 | |||
*/ | |||
public BindingValidationStatusHandler getValidationStatusHandler(); | |||
/** | |||
* Unbinds the binding from its respective {@code Binder} | |||
* Removes any {@code ValueChangeListener} {@code Registration} from | |||
@@ -134,7 +143,6 @@ public class Binder<BEAN> implements Serializable { | |||
* @since 8.2 | |||
*/ | |||
public void unbind(); | |||
} | |||
/** | |||
@@ -990,8 +998,9 @@ public class Binder<BEAN> implements Serializable { | |||
return binder; | |||
} | |||
private void notifyStatusHandler(BindingValidationStatus<?> status) { | |||
statusHandler.statusChange(status); | |||
@Override | |||
public BindingValidationStatusHandler getValidationStatusHandler() { | |||
return statusHandler; | |||
} | |||
} | |||
@@ -1948,9 +1957,7 @@ public class Binder<BEAN> implements Serializable { | |||
protected void handleBinderValidationStatus( | |||
BinderValidationStatus<BEAN> binderStatus) { | |||
// let field events go to binding status handlers | |||
binderStatus.getFieldValidationStatuses() | |||
.forEach(status -> ((BindingImpl<?, ?, ?>) status.getBinding()) | |||
.notifyStatusHandler(status)); | |||
binderStatus.notifyBindingValidationStatusHandlers(); | |||
// show first possible error or OK status in the label if set | |||
if (getStatusLabel().isPresent()) { |
@@ -23,6 +23,7 @@ import java.util.Objects; | |||
import java.util.stream.Collectors; | |||
import com.vaadin.data.Binder.BindingBuilder; | |||
import com.vaadin.server.SerializablePredicate; | |||
/** | |||
* Binder validation status change. Represents the outcome of binder level | |||
@@ -33,8 +34,8 @@ import com.vaadin.data.Binder.BindingBuilder; | |||
* Note: if there are any field level validation errors, the bean level | |||
* validation is not run. | |||
* <p> | |||
* Use {@link Binder#setValidationStatusHandler(BinderValidationStatusHandler)} to handle | |||
* form level validation status changes. | |||
* Use {@link Binder#setValidationStatusHandler(BinderValidationStatusHandler)} | |||
* to handle form level validation status changes. | |||
* | |||
* @author Vaadin Ltd | |||
* | |||
@@ -188,4 +189,34 @@ public class BinderValidationStatus<BEAN> implements Serializable { | |||
return binderStatuses.stream().filter(ValidationResult::isError) | |||
.collect(Collectors.toList()); | |||
} | |||
/** | |||
* Notifies all validation status handlers in bindings. | |||
* | |||
* @see #notifyBindingValidationStatusHandlers(SerializablePredicate) | |||
* | |||
* @since 8.2 | |||
*/ | |||
public void notifyBindingValidationStatusHandlers() { | |||
notifyBindingValidationStatusHandlers(t -> true); | |||
} | |||
/** | |||
* Notifies validation status handlers for bindings that pass given filter. | |||
* The filter should return {@code true} for each | |||
* {@link BindingValidationStatus} that should be delegated to the status | |||
* handler in the binding. | |||
* | |||
* @see #notifyBindingValidationStatusHandlers() | |||
* | |||
* @param filter | |||
* the filter to select bindings to run status handling for | |||
* | |||
* @since 8.2 | |||
*/ | |||
public void notifyBindingValidationStatusHandlers( | |||
SerializablePredicate<BindingValidationStatus<?>> filter) { | |||
bindingStatuses.stream().filter(filter).forEach(s -> s.getBinding() | |||
.getValidationStatusHandler().statusChange(s)); | |||
} | |||
} |
@@ -21,8 +21,9 @@ import com.vaadin.ui.AbstractComponent; | |||
/** | |||
* Handler for {@link BinderValidationStatus} changes. | |||
* <p> | |||
* {@link Binder#setValidationStatusHandler(BinderValidationStatusHandler) Register} an | |||
* instance of this class to be able to customize validation status handling. | |||
* {@link Binder#setValidationStatusHandler(BinderValidationStatusHandler) | |||
* Register} an instance of this class to be able to customize validation status | |||
* handling. | |||
* <p> | |||
* The default handler will show | |||
* {@link AbstractComponent#setComponentError(com.vaadin.server.ErrorMessage) an | |||
@@ -53,5 +54,4 @@ public interface BinderValidationStatusHandler<BEAN> | |||
* the changed status | |||
*/ | |||
void statusChange(BinderValidationStatus<BEAN> statusChange); | |||
} |
@@ -749,6 +749,68 @@ public class BinderTest extends BinderTestBase<Binder<Person>, Person> { | |||
String.valueOf(item.getAge()), ageField.getValue()); | |||
} | |||
static class MyBindingHandler implements BindingValidationStatusHandler { | |||
boolean expectingError = false; | |||
int callCount = 0; | |||
@Override | |||
public void statusChange(BindingValidationStatus<?> statusChange) { | |||
++callCount; | |||
if (expectingError) { | |||
Assert.assertTrue("Expecting error", statusChange.isError()); | |||
} else { | |||
Assert.assertFalse("Unexpected error", statusChange.isError()); | |||
} | |||
} | |||
} | |||
@Test | |||
public void execute_binding_status_handler_from_binder_status_handler() { | |||
MyBindingHandler bindingHandler = new MyBindingHandler(); | |||
binder.forField(nameField) | |||
.withValidator(t -> !t.isEmpty(), "No empty values.") | |||
.withValidationStatusHandler(bindingHandler) | |||
.bind(Person::getFirstName, Person::setFirstName); | |||
String ageError = "CONVERSIONERROR"; | |||
binder.forField(ageField) | |||
.withConverter(new StringToIntegerConverter(ageError)) | |||
.bind(Person::getAge, Person::setAge); | |||
binder.setValidationStatusHandler(status -> { | |||
status.notifyBindingValidationStatusHandlers(); | |||
}); | |||
String initialName = item.getFirstName(); | |||
int initialAge = item.getAge(); | |||
binder.setBean(item); | |||
// Test specific error handling. | |||
bindingHandler.expectingError = true; | |||
nameField.setValue(""); | |||
// Test default error handling. | |||
ageField.setValue("foo"); | |||
Assert.assertTrue("Component error does not contain error message", | |||
ageField.getComponentError().getFormattedHtmlMessage() | |||
.contains(ageError)); | |||
// Restore values and test no errors. | |||
ageField.setValue(String.valueOf(initialAge)); | |||
Assert.assertNull("There should be no component error", | |||
ageField.getComponentError()); | |||
bindingHandler.expectingError = false; | |||
nameField.setValue(initialName); | |||
// Assert that the handler was called. | |||
Assert.assertEquals( | |||
"Unexpected callCount to binding validation status handler", 4, | |||
bindingHandler.callCount); | |||
} | |||
@Test | |||
public void removed_binding_not_updates_value() { | |||
Binding<Person, Integer> binding = binder.forField(ageField) | |||
@@ -766,6 +828,5 @@ public class BinderTest extends BinderTestBase<Binder<Person>, Person> { | |||
Assert.assertEquals("Binding still affects bean even after unbind", | |||
ageBeforeUnbind, String.valueOf(item.getAge())); | |||
} | |||
} |