diff options
author | Pekka Hyvönen <pekka@vaadin.com> | 2016-09-12 12:37:12 +0300 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2016-09-13 08:45:46 +0000 |
commit | 5cc6b0e4e265783808ac258c830964ed7e888c34 (patch) | |
tree | 5f4dbc62b9383d726d10f9d768c35f200c80487a /server | |
parent | 29cdc9873442b8051066810e866bcc2b3f010a59 (diff) | |
download | vaadin-framework-5cc6b0e4e265783808ac258c830964ed7e888c34.tar.gz vaadin-framework-5cc6b0e4e265783808ac258c830964ed7e888c34.zip |
Clear binder errors after load/bind
Adds an unresolved validation status to make it possible to clear
visible field validation errors, even if status is not yet valid.
Change-Id: I227a8802b6a71be1533dc903bad1a8e2faef5ed2
Diffstat (limited to 'server')
4 files changed, 135 insertions, 3 deletions
diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java index c282a89e77..f58c69bb7b 100644 --- a/server/src/main/java/com/vaadin/data/Binder.java +++ b/server/src/main/java/com/vaadin/data/Binder.java @@ -677,6 +677,10 @@ public class Binder<BEAN> implements Serializable { public <FIELDVALUE> Binding<BEAN, FIELDVALUE, FIELDVALUE> forField( HasValue<FIELDVALUE> field) { Objects.requireNonNull(field, "field cannot be null"); + // clear previous errors for this field and any bean level validation + clearError(field); + getStatusLabel().ifPresent(label -> label.setValue("")); + return createBinding(field, Converter.identity(), this::handleValidationStatusChange); } @@ -767,6 +771,8 @@ public class Binder<BEAN> implements Serializable { bean = null; bindings.forEach(BindingImpl::unbind); } + getStatusHandler() + .accept(BinderValidationStatus.createUnresolvedStatus(this)); } /** @@ -788,6 +794,9 @@ public class Binder<BEAN> implements Serializable { Objects.requireNonNull(bean, "bean cannot be null"); setHasChanges(false); bindings.forEach(binding -> binding.setFieldValue(bean)); + + getStatusHandler() + .accept(BinderValidationStatus.createUnresolvedStatus(this)); } /** diff --git a/server/src/main/java/com/vaadin/data/BinderValidationStatus.java b/server/src/main/java/com/vaadin/data/BinderValidationStatus.java index f23d17e5b1..8b589f574c 100644 --- a/server/src/main/java/com/vaadin/data/BinderValidationStatus.java +++ b/server/src/main/java/com/vaadin/data/BinderValidationStatus.java @@ -55,6 +55,28 @@ public class BinderValidationStatus<BEAN> implements Serializable { private final List<Result<?>> binderStatuses; /** + * Convenience method for creating a unresolved validation status for the + * given binder. + * <p> + * In practice this status means that the values might not be valid, but + * validation errors should be hidden. + * + * @param source + * the source binder + * @return a unresolved validation status + * @param <BEAN> + * the bean type of the binder + */ + public static <BEAN> BinderValidationStatus<BEAN> createUnresolvedStatus( + Binder<BEAN> source) { + return new BinderValidationStatus<>(source, + source.getBindings().stream() + .map(b -> ValidationStatus.createUnresolvedStatus(b)) + .collect(Collectors.toList()), + Collections.emptyList()); + } + + /** * Creates a new binder validation status for the given binder and * validation results. * diff --git a/server/src/main/java/com/vaadin/data/ValidationStatus.java b/server/src/main/java/com/vaadin/data/ValidationStatus.java index 7203655e7b..f4b74c9d2c 100644 --- a/server/src/main/java/com/vaadin/data/ValidationStatus.java +++ b/server/src/main/java/com/vaadin/data/ValidationStatus.java @@ -46,13 +46,22 @@ public class ValidationStatus<TARGET> implements Serializable { * Status of the validation. * <p> * The status is the part of {@link ValidationStatus} which indicates - * whether the validation failed or not. + * whether the validation failed or not, or whether it is in unresolved + * state (e.g. after clear or reset). */ public enum Status { /** Validation passed. */ OK, /** Validation failed. */ - ERROR + ERROR, + /** + * Unresolved status, e.g field has not yet been validated because value + * was cleared. + * <p> + * In practice this status means that the value might be invalid, but + * validation errors should be hidden. + */ + UNRESOLVED; } private final Status status; @@ -60,6 +69,22 @@ public class ValidationStatus<TARGET> implements Serializable { private final Binding<?, ?, TARGET> binding; /** + * Convenience method for creating a {@link Status#UNRESOLVED} validation + * status for the given binding. + * + * @param source + * the source binding + * @return unresolved validation status + * @param <TARGET> + * the target data type of the binding for which the validation + * status was reset + */ + public static <TARGET> ValidationStatus<TARGET> createUnresolvedStatus( + Binding<?, ?, TARGET> source) { + return new ValidationStatus<>(source, Status.UNRESOLVED, null); + } + + /** * Creates a new validation status for the given binding and validation * result. * @@ -91,7 +116,9 @@ public class ValidationStatus<TARGET> implements Serializable { Objects.requireNonNull(source, "Event source may not be null"); Objects.requireNonNull(status, "Status may not be null"); if (Objects.equals(status, Status.OK) && result.isError() - || Objects.equals(status, Status.ERROR) && !result.isError()) { + || Objects.equals(status, Status.ERROR) && !result.isError() + || Objects.equals(status, Status.UNRESOLVED) + && result != null) { throw new IllegalStateException( "Invalid validation status " + status + " for given result " + (result == null ? "null" : result.toString())); diff --git a/server/src/test/java/com/vaadin/data/BinderTest.java b/server/src/test/java/com/vaadin/data/BinderTest.java index 0a82b527bb..81b4d965b4 100644 --- a/server/src/test/java/com/vaadin/data/BinderTest.java +++ b/server/src/test/java/com/vaadin/data/BinderTest.java @@ -1196,4 +1196,78 @@ public class BinderTest { binder.saveIfValid(new Person()); Assert.assertTrue(binder.hasChanges()); } + + @Test + public void binderLoad_clearsErrors() { + Binding<Person, String, String> binding = binder.forField(nameField) + .withValidator(notEmpty); + binding.bind(Person::getFirstName, Person::setFirstName); + binder.withValidator(bean -> bean.getFirstName().contains("error") + ? Result.error("error") : Result.ok(bean)); + Person person = new Person(); + person.setFirstName(""); + binder.bind(person); + + // initial value is invalid but no error + Assert.assertNull(nameField.getComponentError()); + + // make error show + nameField.setValue("foo"); + nameField.setValue(""); + Assert.assertNotNull(nameField.getComponentError()); + + // bind to another person to see that error is cleared + person = new Person(); + person.setFirstName(""); + binder.bind(person); + // error has been cleared + Assert.assertNull(nameField.getComponentError()); + + // make show error + nameField.setValue("foo"); + nameField.setValue(""); + Assert.assertNotNull(nameField.getComponentError()); + + // load should also clear error + binder.load(person); + Assert.assertNull(nameField.getComponentError()); + + // bind a new field that has invalid value in bean + TextField lastNameField = new TextField(); + person.setLastName(""); + Binding<Person, String, String> binding2 = binder + .forField(lastNameField).withValidator(notEmpty); + binding2.bind(Person::getLastName, Person::setLastName); + + // should not have error shown + Assert.assertNull(lastNameField.getComponentError()); + + // add status label to show bean level error + Label statusLabel = new Label(); + binder.setStatusLabel(statusLabel); + nameField.setValue("error"); + + // no error shown yet because second field validation doesn't pass + Assert.assertEquals("", statusLabel.getValue()); + + // make second field validation pass to get bean validation error + lastNameField.setValue("foo"); + Assert.assertEquals("error", statusLabel.getValue()); + + // reload bean to clear error + binder.load(person); + Assert.assertEquals("", statusLabel.getValue()); + + // unbind() should clear all errors and status label + nameField.setValue(""); + lastNameField.setValue(""); + Assert.assertNotNull(nameField.getComponentError()); + Assert.assertNotNull(lastNameField.getComponentError()); + statusLabel.setComponentError(new UserError("ERROR")); + + binder.unbind(); + Assert.assertNull(nameField.getComponentError()); + Assert.assertNull(lastNameField.getComponentError()); + Assert.assertEquals("", statusLabel.getValue()); + } } |