aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--documentation/datamodel/datamodel-forms.asciidoc4
-rw-r--r--server/src/main/java/com/vaadin/data/Binder.java29
-rw-r--r--server/src/main/java/com/vaadin/data/StatusChangeEvent.java15
-rw-r--r--server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java29
-rw-r--r--server/src/test/java/com/vaadin/data/BinderTest.java52
5 files changed, 118 insertions, 11 deletions
diff --git a/documentation/datamodel/datamodel-forms.asciidoc b/documentation/datamodel/datamodel-forms.asciidoc
index 1fd79dd9b9..0218140425 100644
--- a/documentation/datamodel/datamodel-forms.asciidoc
+++ b/documentation/datamodel/datamodel-forms.asciidoc
@@ -375,8 +375,8 @@ We can use that event to make the save and reset buttons of our forms become ena
[source, java]
----
binder.addStatusChangeListener(event -> {
- boolean isValid = !event.hasValidationErrors();
- boolean hasChanges = binder.hasChanges();
+ boolean isValid = event.getBinder().isValid();
+ boolean hasChanges = event.getBinder().hasChanges();
saveButton.setEnabled(hasChanges && isValid);
resetButton.setEnabled(hasChanges);
diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java
index d36c9996fd..5fa486319a 100644
--- a/server/src/main/java/com/vaadin/data/Binder.java
+++ b/server/src/main/java/com/vaadin/data/Binder.java
@@ -1579,6 +1579,35 @@ public class Binder<BEAN> implements Serializable {
fireStatusChangeEvent(validationStatus.hasErrors());
return validationStatus;
}
+
+ /**
+ * Runs all currently configured field level validators, as well as all bean
+ * level validators if a bean is currently set with
+ * {@link #setBean(Object)}, and returns whether any of the validators
+ * failed.
+ *
+ * @return whether this binder is in a valid state
+ * @throws IllegalStateException
+ * if bean level validators have been configured and no bean is
+ * currently set
+ */
+ public boolean isValid() {
+ if (getBean() == null && !validators.isEmpty()) {
+ throw new IllegalStateException(
+ "Cannot validate binder: "
+ + "bean level validators have been configured "
+ + "but no bean is currently set");
+ }
+ if (validateBindings().stream().filter(BindingValidationStatus::isError)
+ .findAny().isPresent()) {
+ return false;
+ }
+ if (getBean() != null && validateBean(getBean()).stream()
+ .filter(ValidationResult::isError).findAny().isPresent()) {
+ return false;
+ }
+ return true;
+ }
/**
* Validates the bindings and returns the result of the validation as a list
diff --git a/server/src/main/java/com/vaadin/data/StatusChangeEvent.java b/server/src/main/java/com/vaadin/data/StatusChangeEvent.java
index e4db2b8868..acef1bac75 100644
--- a/server/src/main/java/com/vaadin/data/StatusChangeEvent.java
+++ b/server/src/main/java/com/vaadin/data/StatusChangeEvent.java
@@ -26,7 +26,7 @@ import com.vaadin.server.Setter;
* <p>
* The {@link Binder} status is changed whenever any of the following happens:
* <ul>
- * <li>if it's bound and any of its bound field or select has been changed
+ * <li>if any of its bound fields or selects have been changed
* <li>{@link Binder#writeBean(Object)} or
* {@link Binder#writeBeanIfValid(Object)} is called
* <li>{@link Binder#readBean(Object)} is called
@@ -47,13 +47,14 @@ public class StatusChangeEvent extends EventObject {
private final boolean hasValidationErrors;
/**
- * Create a new status change event for given {@code binder} using its
- * current validation status.
+ * Create a new status change event for given {@code binder}, storing
+ * information of whether the change that triggered this event caused
+ * validation errors.
*
* @param binder
* the event source binder
* @param hasValidationErrors
- * the binder validation status
+ * the validation status associated with this event
*/
public StatusChangeEvent(Binder<?> binder, boolean hasValidationErrors) {
super(binder);
@@ -61,10 +62,10 @@ public class StatusChangeEvent extends EventObject {
}
/**
- * Gets the binder validation status.
+ * Gets the associated validation status.
*
- * @return {@code true} if the binder has validation errors, {@code false}
- * otherwise
+ * @return {@code true} if the change that triggered this event caused
+ * validation errors, {@code false} otherwise
*/
public boolean hasValidationErrors() {
return hasValidationErrors;
diff --git a/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java b/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java
index 59ca890740..f36a357617 100644
--- a/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java
+++ b/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java
@@ -724,7 +724,7 @@ public class BinderBookOfVaadinTest {
AtomicBoolean eventIsFired = new AtomicBoolean(false);
binder.addStatusChangeListener(event -> {
- boolean isValid = !event.hasValidationErrors();
+ boolean isValid = event.getBinder().isValid();
boolean hasChanges = event.getBinder().hasChanges();
eventIsFired.set(true);
@@ -782,7 +782,7 @@ public class BinderBookOfVaadinTest {
AtomicBoolean eventIsFired = new AtomicBoolean(false);
binder.addStatusChangeListener(event -> {
- boolean isValid = !event.hasValidationErrors();
+ boolean isValid = event.getBinder().isValid();
boolean hasChanges = event.getBinder().hasChanges();
eventIsFired.set(true);
@@ -824,6 +824,31 @@ public class BinderBookOfVaadinTest {
verifyEventIsFired(eventIsFired);
}
+ @Test
+ public void statusChangeListener_multipleRequiredFields() {
+ Button saveButton = new Button();
+
+ binder.addStatusChangeListener(event -> {
+ boolean isValid = event.getBinder().isValid();
+ boolean hasChanges = event.getBinder().hasChanges();
+
+ saveButton.setEnabled(hasChanges && isValid);
+ });
+
+ binder.forField(field).asRequired("").bind(BookPerson::getLastName,
+ BookPerson::setLastName);
+ binder.forField(emailField).asRequired("").bind(BookPerson::getEmail,
+ BookPerson::setEmail);
+
+ Assert.assertFalse(saveButton.isEnabled());
+ field.setValue("not empty");
+ Assert.assertFalse(saveButton.isEnabled());
+ emailField.setValue("not empty");
+ Assert.assertTrue(saveButton.isEnabled());
+ field.clear();
+ Assert.assertFalse(saveButton.isEnabled());
+ }
+
private void verifyEventIsFired(AtomicBoolean flag) {
Assert.assertTrue(flag.get());
flag.set(false);
diff --git a/server/src/test/java/com/vaadin/data/BinderTest.java b/server/src/test/java/com/vaadin/data/BinderTest.java
index e07aee9632..8dc4c76c3b 100644
--- a/server/src/test/java/com/vaadin/data/BinderTest.java
+++ b/server/src/test/java/com/vaadin/data/BinderTest.java
@@ -494,4 +494,56 @@ public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
Assert.assertFalse(ageField.isReadOnly());
}
+ @Test
+ public void isValidTest_bound_binder() {
+ binder.forField(nameField)
+ .withValidator(
+ Validator.from(
+ name -> !name.equals("fail field validation"),
+ ""))
+ .bind(Person::getFirstName, Person::setFirstName);
+
+ binder.withValidator(
+ Validator.from(person -> !person.getFirstName()
+ .equals("fail bean validation"), ""));
+
+ binder.setBean(item);
+
+ Assert.assertTrue(binder.isValid());
+
+ nameField.setValue("fail field validation");
+ Assert.assertFalse(binder.isValid());
+
+ nameField.setValue("");
+ Assert.assertTrue(binder.isValid());
+
+ nameField.setValue("fail bean validation");
+ Assert.assertFalse(binder.isValid());
+ }
+
+ @Test
+ public void isValidTest_unbound_binder() {
+ binder.forField(nameField)
+ .withValidator(Validator.from(
+ name -> !name.equals("fail field validation"), ""))
+ .bind(Person::getFirstName, Person::setFirstName);
+
+ Assert.assertTrue(binder.isValid());
+
+ nameField.setValue("fail field validation");
+ Assert.assertFalse(binder.isValid());
+
+ nameField.setValue("");
+ Assert.assertTrue(binder.isValid());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void isValidTest_unbound_binder_throws_with_bean_level_validation() {
+ binder.forField(nameField).bind(Person::getFirstName,
+ Person::setFirstName);
+ binder.withValidator(Validator.from(
+ person -> !person.getFirstName().equals("fail bean validation"),
+ ""));
+ binder.isValid();
+ }
} \ No newline at end of file