summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/src/main/java/com/vaadin/data/Binder.java30
-rw-r--r--server/src/test/java/com/vaadin/data/BinderTest.java92
2 files changed, 119 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 f9e076e13f..360daa73bf 100644
--- a/server/src/main/java/com/vaadin/data/Binder.java
+++ b/server/src/main/java/com/vaadin/data/Binder.java
@@ -728,6 +728,23 @@ public class Binder<BEAN> implements Serializable {
*/
public BindingBuilder<BEAN, TARGET> asRequired(
ErrorMessageProvider errorMessageProvider);
+
+ /**
+ * Sets the field to be required and delegates the required check to a custom validator.
+ * This means two things:
+ * <ol>
+ * <li>the required indicator will be displayed for this field</li>
+ * <li>the field value is validated by customRequiredValidator</li>
+ * </ol>
+ *
+ * @see HasValue#setRequiredIndicatorVisible(boolean)
+ * @param customRequiredValidator
+ * validator responsible for the required check
+ * @return this binding, for chaining
+ * @since
+ */
+ public BindingBuilder<BEAN, TARGET> asRequired(
+ Validator<TARGET> customRequiredValidator);
}
/**
@@ -885,11 +902,18 @@ public class Binder<BEAN> implements Serializable {
@Override
public BindingBuilder<BEAN, TARGET> asRequired(
ErrorMessageProvider errorMessageProvider) {
+ return asRequired(
+ Validator.from(
+ value -> !Objects.equals(value, field.getEmptyValue()),
+ errorMessageProvider));
+ }
+
+ @Override
+ public BindingBuilder<BEAN, TARGET> asRequired(
+ Validator<TARGET> customRequiredValidator) {
checkUnbound();
field.setRequiredIndicatorVisible(true);
- return withValidator(
- value -> !Objects.equals(value, field.getEmptyValue()),
- errorMessageProvider);
+ return withValidator(customRequiredValidator);
}
/**
diff --git a/server/src/test/java/com/vaadin/data/BinderTest.java b/server/src/test/java/com/vaadin/data/BinderTest.java
index 0163b65b51..5ca469011c 100644
--- a/server/src/test/java/com/vaadin/data/BinderTest.java
+++ b/server/src/test/java/com/vaadin/data/BinderTest.java
@@ -30,6 +30,7 @@ import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.tests.data.bean.Person;
import com.vaadin.tests.data.bean.Sex;
import com.vaadin.ui.TextField;
+import org.apache.commons.lang.StringUtils;
public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
@@ -502,6 +503,97 @@ public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
}
@Test
+ public void setRequired_withCustomValidator_fieldGetsRequiredIndicatorAndValidator() {
+ TextField textField = new TextField();
+ textField.setLocale(Locale.CANADA);
+ assertFalse(textField.isRequiredIndicatorVisible());
+
+ BindingBuilder<Person, String> binding = binder.forField(textField);
+ assertFalse(textField.isRequiredIndicatorVisible());
+ AtomicInteger invokes = new AtomicInteger();
+
+ Validator<String> customRequiredValidator = (value, context) -> {
+ invokes.incrementAndGet();
+ if (StringUtils.isBlank(value)) {
+ return ValidationResult.error("Input is required.");
+ }
+ return ValidationResult.ok();
+ };
+ binding.asRequired(customRequiredValidator);
+ assertTrue(textField.isRequiredIndicatorVisible());
+
+ binding.bind(Person::getFirstName, Person::setFirstName);
+ binder.setBean(item);
+ assertNull(textField.getErrorMessage());
+ assertEquals(0, invokes.get());
+
+ textField.setValue(" ");
+ ErrorMessage errorMessage = textField.getErrorMessage();
+ assertNotNull(errorMessage);
+ assertEquals("Input&#32;is&#32;required&#46;", errorMessage.getFormattedHtmlMessage());
+ // validation is done for all changed bindings once.
+ assertEquals(1, invokes.get());
+
+ textField.setValue("value");
+ assertNull(textField.getErrorMessage());
+ assertTrue(textField.isRequiredIndicatorVisible());
+ }
+
+ @Test
+ public void setRequired_withCustomValidator_modelConverterBeforeValidator() {
+ TextField textField = new TextField();
+ textField.setLocale(Locale.CANADA);
+ assertFalse(textField.isRequiredIndicatorVisible());
+
+ Converter<String, String> stringBasicPreProcessingConverter = new Converter<String, String>() {
+ @Override
+ public Result<String> convertToModel(String value, ValueContext context) {
+ if (StringUtils.isBlank(value)) {
+ return Result.ok(null);
+ }
+ return Result.ok(StringUtils.trim(value));
+ }
+
+ @Override
+ public String convertToPresentation(String value, ValueContext context) {
+ if (value == null) {
+ return "";
+ }
+ return value;
+ }
+ };
+
+ AtomicInteger invokes = new AtomicInteger();
+ Validator<String> customRequiredValidator = (value, context) -> {
+ invokes.incrementAndGet();
+ if (value == null) {
+ return ValidationResult.error("Input required.");
+ }
+ return ValidationResult.ok();
+ };
+
+ binder.forField(textField)
+ .withConverter(stringBasicPreProcessingConverter)
+ .asRequired(customRequiredValidator)
+ .bind(Person::getFirstName, Person::setFirstName);
+
+ binder.setBean(item);
+ assertNull(textField.getErrorMessage());
+ assertEquals(0, invokes.get());
+
+ textField.setValue(" ");
+ ErrorMessage errorMessage = textField.getErrorMessage();
+ assertNotNull(errorMessage);
+ assertEquals("Input&#32;required&#46;", errorMessage.getFormattedHtmlMessage());
+ // validation is done for all changed bindings once.
+ assertEquals(1, invokes.get());
+
+ textField.setValue("value");
+ assertNull(textField.getErrorMessage());
+ assertTrue(textField.isRequiredIndicatorVisible());
+ }
+
+ @Test
public void validationStatusHandler_onlyRunForChangedField() {
TextField firstNameField = new TextField();
TextField lastNameField = new TextField();