summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorPekka Hyvönen <pekka@vaadin.com>2016-09-12 12:37:12 +0300
committerVaadin Code Review <review@vaadin.com>2016-09-13 08:45:46 +0000
commit5cc6b0e4e265783808ac258c830964ed7e888c34 (patch)
tree5f4dbc62b9383d726d10f9d768c35f200c80487a /server
parent29cdc9873442b8051066810e866bcc2b3f010a59 (diff)
downloadvaadin-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')
-rw-r--r--server/src/main/java/com/vaadin/data/Binder.java9
-rw-r--r--server/src/main/java/com/vaadin/data/BinderValidationStatus.java22
-rw-r--r--server/src/main/java/com/vaadin/data/ValidationStatus.java33
-rw-r--r--server/src/test/java/com/vaadin/data/BinderTest.java74
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());
+ }
}