Browse Source

Add isValid to Binder (#8274)

* 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
tags/8.0.0.rc1
Aleksi Hietanen 7 years ago
parent
commit
560d161540

+ 2
- 2
documentation/datamodel/datamodel-forms.asciidoc View File

[source, java] [source, java]
---- ----
binder.addStatusChangeListener(event -> { 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); saveButton.setEnabled(hasChanges && isValid);
resetButton.setEnabled(hasChanges); resetButton.setEnabled(hasChanges);

+ 29
- 0
server/src/main/java/com/vaadin/data/Binder.java View File

fireStatusChangeEvent(validationStatus.hasErrors()); fireStatusChangeEvent(validationStatus.hasErrors());
return validationStatus; 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 * Validates the bindings and returns the result of the validation as a list

+ 8
- 7
server/src/main/java/com/vaadin/data/StatusChangeEvent.java View File

* <p> * <p>
* The {@link Binder} status is changed whenever any of the following happens: * The {@link Binder} status is changed whenever any of the following happens:
* <ul> * <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 * <li>{@link Binder#writeBean(Object)} or
* {@link Binder#writeBeanIfValid(Object)} is called * {@link Binder#writeBeanIfValid(Object)} is called
* <li>{@link Binder#readBean(Object)} is called * <li>{@link Binder#readBean(Object)} is called
private final boolean hasValidationErrors; 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 * @param binder
* the event source binder * the event source binder
* @param hasValidationErrors * @param hasValidationErrors
* the binder validation status
* the validation status associated with this event
*/ */
public StatusChangeEvent(Binder<?> binder, boolean hasValidationErrors) { public StatusChangeEvent(Binder<?> binder, boolean hasValidationErrors) {
super(binder); super(binder);
} }


/** /**
* 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() { public boolean hasValidationErrors() {
return hasValidationErrors; return hasValidationErrors;

+ 27
- 2
server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java View File

AtomicBoolean eventIsFired = new AtomicBoolean(false); AtomicBoolean eventIsFired = new AtomicBoolean(false);


binder.addStatusChangeListener(event -> { binder.addStatusChangeListener(event -> {
boolean isValid = !event.hasValidationErrors();
boolean isValid = event.getBinder().isValid();
boolean hasChanges = event.getBinder().hasChanges(); boolean hasChanges = event.getBinder().hasChanges();
eventIsFired.set(true); eventIsFired.set(true);


AtomicBoolean eventIsFired = new AtomicBoolean(false); AtomicBoolean eventIsFired = new AtomicBoolean(false);


binder.addStatusChangeListener(event -> { binder.addStatusChangeListener(event -> {
boolean isValid = !event.hasValidationErrors();
boolean isValid = event.getBinder().isValid();
boolean hasChanges = event.getBinder().hasChanges(); boolean hasChanges = event.getBinder().hasChanges();
eventIsFired.set(true); eventIsFired.set(true);


verifyEventIsFired(eventIsFired); 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) { private void verifyEventIsFired(AtomicBoolean flag) {
Assert.assertTrue(flag.get()); Assert.assertTrue(flag.get());
flag.set(false); flag.set(false);

+ 52
- 0
server/src/test/java/com/vaadin/data/BinderTest.java View File

Assert.assertFalse(ageField.isReadOnly()); 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();
}
} }

Loading…
Cancel
Save