diff --git a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/main/java/com/vaadin/flow/component/radiobutton/tests/RequiredValidationPage.java b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/main/java/com/vaadin/flow/component/radiobutton/tests/RequiredValidationPage.java
index 4b1aa54a60e..26270468248 100644
--- a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/main/java/com/vaadin/flow/component/radiobutton/tests/RequiredValidationPage.java
+++ b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/main/java/com/vaadin/flow/component/radiobutton/tests/RequiredValidationPage.java
@@ -19,19 +19,20 @@
import com.vaadin.flow.component.html.NativeButton;
import com.vaadin.flow.component.radiobutton.RadioButtonGroup;
import com.vaadin.flow.data.binder.Binder;
+import com.vaadin.flow.data.binder.Binder.Binding;
import com.vaadin.flow.router.Route;
@Route("vaadin-radio-button/radio-button-group-required-binder")
public class RequiredValidationPage extends Div {
public RequiredValidationPage() {
- RadioButtonGroup group = new RadioButtonGroup<>();
+ final RadioButtonGroup group = new RadioButtonGroup<>();
group.setItems("male", "female", "unknown");
group.setLabel("Gender");
Entity entity = new Entity();
Binder binder = new Binder<>(Entity.class);
- binder.forField(group).bind("gender");
+ Binding nonRequiredBinding = binder.forField(group).bind("gender");
group.setId("gender");
@@ -40,11 +41,34 @@ public RequiredValidationPage() {
add(group);
NativeButton off = new NativeButton(
- "Make required indicator invisible and set requied", event -> {
- group.setRequiredIndicatorVisible(false);
- group.setRequired(true);
+ "Make required and validate", event -> {
+ nonRequiredBinding.unbind();
+ binder.forField(group).asRequired("required").bind("gender");
+ binder.validate();
});
off.setId("hide");
add(off);
+
+ RadioButtonGroup radioGroupWithInvalidOption = new RadioButtonGroup<>();
+ radioGroupWithInvalidOption.setId("radio-button-with-invalid-option");
+ radioGroupWithInvalidOption.setItems("valid 1", "valid 2", "invalid");
+ Binder binderForInvalidOption = new Binder<>(Entity.class);
+ binderForInvalidOption.forField(radioGroupWithInvalidOption)
+ .withValidator(value->!"invalid".equals(value), "Value is invalid")
+ .bind("gender");
+ add(radioGroupWithInvalidOption);
+
+ RadioButtonGroup radioGroupInvalidOnAttach = new RadioButtonGroup<>();
+ radioGroupInvalidOnAttach.setId("radio-button-invalid-on-attach");
+ radioGroupInvalidOnAttach.setItems("valid 1", "valid 2", "invalid");
+ Binder binderForInvalidOnAttach = new Binder<>(Entity.class);
+ binderForInvalidOnAttach.forField(radioGroupInvalidOnAttach)
+ .withValidator(value->!"invalid".equals(value), "Value is invalid")
+ .bind("gender");
+ Entity invalidBean = new Entity();
+ invalidBean.setGender("invalid");
+ binderForInvalidOnAttach.setBean(invalidBean);
+ binderForInvalidOnAttach.validate();
+ add(radioGroupInvalidOnAttach);
}
}
diff --git a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/test/java/com/vaadin/flow/component/radiobutton/tests/RadioButtonGroupIT.java b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/test/java/com/vaadin/flow/component/radiobutton/tests/RadioButtonGroupIT.java
index 838b80a36a6..7fa9c27242d 100644
--- a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/test/java/com/vaadin/flow/component/radiobutton/tests/RadioButtonGroupIT.java
+++ b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/test/java/com/vaadin/flow/component/radiobutton/tests/RadioButtonGroupIT.java
@@ -313,30 +313,6 @@ public void assertThemeVariant() {
verifyThemeVariantsBeingToggled();
}
- @Test
- public void groupHasLabelAndErrorMessage_setInvalidShowEM_setValueRemoveEM() {
- TestBenchElement group = $(TestBenchElement.class)
- .id("group-with-label-and-error-message");
-
- Assert.assertEquals("Label Attribute should present with correct text",
- group.getAttribute("label"), "Group label");
-
- TestBenchElement errorMessage = group.$(TestBenchElement.class)
- .attribute("part", "error-message").first();
- verifyGroupValid(group, errorMessage);
-
- layout.findElement(By.id("group-with-label-button")).click();
- verifyGroupInvalid(group, errorMessage);
-
- Assert.assertEquals(
- "Correct error message should be shown after the button clicks",
- "Field has been set to invalid from server side",
- errorMessage.getText());
-
- executeScript("arguments[0].value=2;", group);
- verifyGroupValid(group, errorMessage);
- }
-
@Test
public void verifyHelper() {
RadioButtonGroupElement groupWithHelperText = $(RadioButtonGroupElement.class)
diff --git a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/test/java/com/vaadin/flow/component/radiobutton/tests/RequiredValidationIT.java b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/test/java/com/vaadin/flow/component/radiobutton/tests/RequiredValidationIT.java
index 4a591a07f94..21fa832cfd2 100644
--- a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/test/java/com/vaadin/flow/component/radiobutton/tests/RequiredValidationIT.java
+++ b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow-integration-tests/src/test/java/com/vaadin/flow/component/radiobutton/tests/RequiredValidationIT.java
@@ -38,10 +38,39 @@ public void requiredValidation_disabledWithBinder_enabledViaExpicitCall()
Boolean.parseBoolean(group.getAttribute("invalid")));
findElement(By.id("hide")).click();
- $("vaadin-radio-button").first().sendKeys(Keys.TAB);
Assert.assertTrue("Radio button group should be invalid",
Boolean.parseBoolean(group.getAttribute("invalid")));
}
+
+ @Test
+ public void groupWithInvalidOption() {
+ open();
+
+ WebElement group = findElement(By.id("radio-button-with-invalid-option"));
+ WebElement radioButton = group.findElements(By.tagName("vaadin-radio-button")).get(2);
+
+ Assert.assertFalse("Radio button group should be valid.",
+ Boolean.parseBoolean(group.getAttribute("invalid")));
+ radioButton.click();
+
+ Assert.assertTrue("Radio button group should be invalid.",
+ Boolean.parseBoolean(group.getAttribute("invalid")));
+
+ radioButton.sendKeys(Keys.TAB);
+ Assert.assertTrue("Radio button group should keep invalid.",
+ Boolean.parseBoolean(group.getAttribute("invalid")));
+ }
+
+ @Test
+ public void groupInvalidOnAttach() {
+ open();
+
+ WebElement group = findElement(By.id("radio-button-invalid-on-attach"));
+
+ Assert.assertTrue("Radio button group should be invalid.",
+ Boolean.parseBoolean(group.getAttribute("invalid")));
+ }
+
}
diff --git a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/FieldValidationUtil.java b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/FieldValidationUtil.java
new file mode 100644
index 00000000000..4228db66d30
--- /dev/null
+++ b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/FieldValidationUtil.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2020 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.radiobutton;
+
+
+class FieldValidationUtil {
+ private FieldValidationUtil() {
+
+ }
+
+ static void disableClientValidation(RadioButtonGroup component) {
+ // if the component was already attached override the validate()
+
+ component.addAttachListener(e -> overrideClientValidation(component));
+ }
+
+ private static void overrideClientValidation(RadioButtonGroup component) {
+ component.getElement()
+ .executeJs("this.validate = function () {" +
+ "return this.checkValidity();};");
+
+ component.getUI().ifPresent(ui -> ui.beforeClientResponse(component, (e) -> {
+ if (component.isInvalid()) {
+ // By default, the invalid flag is always false when a component is created.
+ // However, if the component is populated and validated in the same HTTP request,
+ // the server side state may have changed before the JavaScript disabling client
+ // side validation was properly executed. This can sometimes lead to a situation
+ // where the client side thinks the value is valid (before client side validation
+ // was disabled) and the server side thinks the value is invalid. This will lead to
+ // strange behavior until the two states are synchronized again. To avoid this, we will
+ // explicitly change the client side value if the server side is invalid.
+ component.getElement().executeJs("this.invalid = true");
+ }
+ }));
+ }
+}
\ No newline at end of file
diff --git a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/GeneratedVaadinRadioGroup.java b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/GeneratedVaadinRadioGroup.java
index bfaf4dcc92a..11bd64f3239 100644
--- a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/GeneratedVaadinRadioGroup.java
+++ b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/GeneratedVaadinRadioGroup.java
@@ -221,14 +221,9 @@ protected void setReadonly(boolean readonly) {
*
*
* This property is set to true when the value is invalid.
- *
- * This property is synchronized automatically from client side when a
- * 'invalid-changed' event happens.
- *
*
* @return the {@code invalid} property from the webcomponent
*/
- @Synchronize(property = "invalid", value = "invalid-changed")
protected boolean isInvalidBoolean() {
return getElement().getProperty("invalid", false);
}
diff --git a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/RadioButtonGroup.java b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/RadioButtonGroup.java
index 75c3c677810..1e131111fd4 100755
--- a/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/RadioButtonGroup.java
+++ b/vaadin-radio-button-flow-parent/vaadin-radio-button-flow/src/main/java/com/vaadin/flow/component/radiobutton/RadioButtonGroup.java
@@ -116,6 +116,7 @@ public RadioButtonGroup() {
RadioButtonGroup::modelToPresentation, true);
registerValidation();
+ FieldValidationUtil.disableClientValidation(this);
}
@Override