Skip to content

Commit

Permalink
fix: disable client side validation (#461) (#490)
Browse files Browse the repository at this point in the history
- Disable client side validation
- Validation from Binder will happen upon Binder#validate() or if the field binding uses withValidator
fix vaadin/vaadin-radio-button#179
  • Loading branch information
DiegoCardoso authored Dec 8, 2020
1 parent 148108b commit 379c836
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> group = new RadioButtonGroup<>();
final RadioButtonGroup<String> group = new RadioButtonGroup<>();
group.setItems("male", "female", "unknown");
group.setLabel("Gender");

Entity entity = new Entity();
Binder<Entity> binder = new Binder<>(Entity.class);
binder.forField(group).bind("gender");
Binding<Entity, String> nonRequiredBinding = binder.forField(group).bind("gender");

group.setId("gender");

Expand All @@ -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<String> radioGroupWithInvalidOption = new RadioButtonGroup<>();
radioGroupWithInvalidOption.setId("radio-button-with-invalid-option");
radioGroupWithInvalidOption.setItems("valid 1", "valid 2", "invalid");
Binder<Entity> binderForInvalidOption = new Binder<>(Entity.class);
binderForInvalidOption.forField(radioGroupWithInvalidOption)
.withValidator(value->!"invalid".equals(value), "Value is invalid")
.bind("gender");
add(radioGroupWithInvalidOption);

RadioButtonGroup<String> radioGroupInvalidOnAttach = new RadioButtonGroup<>();
radioGroupInvalidOnAttach.setId("radio-button-invalid-on-attach");
radioGroupInvalidOnAttach.setItems("valid 1", "valid 2", "invalid");
Binder<Entity> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")));
}

}
Original file line number Diff line number Diff line change
@@ -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 <T> void disableClientValidation(RadioButtonGroup<T> component) {
// if the component was already attached override the validate()

component.addAttachListener(e -> overrideClientValidation(component));
}

private static <T> void overrideClientValidation(RadioButtonGroup<T> 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");
}
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,9 @@ protected void setReadonly(boolean readonly) {
* </p>
* <p>
* This property is set to true when the value is invalid.
* <p>
* This property is synchronized automatically from client side when a
* 'invalid-changed' event happens.
* </p>
*
* @return the {@code invalid} property from the webcomponent
*/
@Synchronize(property = "invalid", value = "invalid-changed")
protected boolean isInvalidBoolean() {
return getElement().getProperty("invalid", false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public RadioButtonGroup() {
RadioButtonGroup::modelToPresentation, true);

registerValidation();
FieldValidationUtil.disableClientValidation(this);
}

@Override
Expand Down

0 comments on commit 379c836

Please sign in to comment.