]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add isValid to Binder (#8274)
authorAleksi Hietanen <aleksi@vaadin.com>
Fri, 3 Feb 2017 11:40:54 +0000 (13:40 +0200)
committerPekka Hyvönen <pekka@vaadin.com>
Fri, 3 Feb 2017 11:40:54 +0000 (13:40 +0200)
* Add isValid to Binder

isValid enables you to check the current validation
status of a given Binder, without firing events or invoking
handlers.

This patch also clarifies the javadocs of StatusChangeEvents'
hasValidationErrors.

* Throw in Binder.isValid if no bean is set and bean validators exist

* Add test cases to BinderTest

documentation/datamodel/datamodel-forms.asciidoc
server/src/main/java/com/vaadin/data/Binder.java
server/src/main/java/com/vaadin/data/StatusChangeEvent.java
server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java
server/src/test/java/com/vaadin/data/BinderTest.java

index 1fd79dd9b91dedd502c89f5e145aff922920c2e2..02181404253f044ad7410def60a7891ac61273a8 100644 (file)
@@ -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);
index d36c9996fd1f7dbe17002ede837c5dc614d4e314..5fa486319a05063a58fdc2efb894bbfe0b7af4c9 100644 (file)
@@ -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
index e4db2b886829b1a07db3bf339905b3670c33969c..acef1bac75c618eb63dc57e138251df45086ced0 100644 (file)
@@ -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;
index 59ca890740d581f171fcc096d048ac26de9cb6a6..f36a357617c3981ed6b7265c90ac7e3422028dc9 100644 (file)
@@ -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);
index e07aee96322c0f105898dd742c0916afbd5e177a..8dc4c76c3bb5e31bf51fc4139db5bbda8f193200 100644 (file)
@@ -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