Skip to content

Commit 062eb66

Browse files
authored
feat: add support for accepting hidden fields in Binder (#24139)(CP: 25.0) (#24192)
Adds setAcceptHiddenFields(boolean) to Binder. setAcceptHiddenFields(true) makes Binder apply bindings for hidden fields by default in same way as with pre-Vaadin 25. PartOf: #24109
1 parent 0c65e98 commit 062eb66

2 files changed

Lines changed: 108 additions & 3 deletions

File tree

flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1841,9 +1841,13 @@ private void removeFromChangedBindingsIfReverted(
18411841
}
18421842

18431843
public SerializablePredicate<Binding<BEAN, TARGET>> getIsAppliedPredicate() {
1844-
return isAppliedPredicate == null
1845-
? Binding.super.getIsAppliedPredicate()
1846-
: isAppliedPredicate;
1844+
if (isAppliedPredicate != null) {
1845+
return isAppliedPredicate;
1846+
}
1847+
if (getBinder().isApplyBindingsToHiddenFields()) {
1848+
return binding -> true;
1849+
}
1850+
return Binding.super.getIsAppliedPredicate();
18471851
}
18481852

18491853
@Override
@@ -1976,6 +1980,8 @@ void setIdentity() {
19761980

19771981
private boolean changeDetectionEnabled = false;
19781982

1983+
private boolean applyBindingsToHiddenFields = false;
1984+
19791985
/**
19801986
* Creates a binder using a custom {@link PropertySet} implementation for
19811987
* finding and resolving property names for
@@ -4121,6 +4127,40 @@ public boolean isValidatorsDisabled() {
41214127
return validatorsDisabled;
41224128
}
41234129

4130+
/**
4131+
* Sets whether all bindings of this Binder should be applied to fields that
4132+
* are not currently visible. By default, bindings whose field is a
4133+
* {@link Component} with {@link Component#isVisible()} returning
4134+
* {@literal false} are skipped during validation and when writing values to
4135+
* the bean. Enabling this setting restores the pre-Vaadin 25 behavior where
4136+
* hidden fields are validated and written just like visible ones.
4137+
* <p>
4138+
* This is a Binder-level fallback: any binding that has its own predicate
4139+
* set via {@link Binding#setIsAppliedPredicate(SerializablePredicate)}
4140+
* continues to use that predicate and is not affected by this flag.
4141+
* <p>
4142+
* Defaults to {@literal false}.
4143+
*
4144+
* @param applyBindingsToHiddenFields
4145+
* {@literal true} to make all bindings apply to hidden fields,
4146+
* {@literal false} to skip hidden fields (the default)
4147+
*/
4148+
public void setApplyBindingsToHiddenFields(
4149+
boolean applyBindingsToHiddenFields) {
4150+
this.applyBindingsToHiddenFields = applyBindingsToHiddenFields;
4151+
}
4152+
4153+
/**
4154+
* Returns whether all bindings of this Binder apply to fields that are not
4155+
* currently visible.
4156+
*
4157+
* @return {@literal true} if bindings are applied to hidden fields,
4158+
* {@literal false} if hidden fields are skipped (the default)
4159+
*/
4160+
public boolean isApplyBindingsToHiddenFields() {
4161+
return applyBindingsToHiddenFields;
4162+
}
4163+
41244164
/**
41254165
* Control whether bound fields implementing {@link HasValidator} subscribe
41264166
* for field's {@code ValidationStatusChangeEvent}s and will

flow-data/src/test/java/com/vaadin/flow/data/binder/BinderTest.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,71 @@ public void withValidator_isAppliedIsEvaluated() {
972972
Assert.assertTrue(binder.isValid());
973973
}
974974

975+
@Test
976+
public void setApplyBindingsToHiddenFields_defaultsToFalse_appliesToHiddenFieldsWhenEnabled() {
977+
Binding<Person, String> nameBinding = binder.forField(nameField)
978+
.withValidator(name -> false, "")
979+
.bind(Person::getFirstName, Person::setFirstName);
980+
binder.setBean(item);
981+
982+
assertFalse(binder.isApplyBindingsToHiddenFields());
983+
984+
((Component) nameBinding.getField()).setVisible(false);
985+
assertTrue("Hidden field should be skipped by default",
986+
binder.isValid());
987+
988+
binder.setApplyBindingsToHiddenFields(true);
989+
assertFalse(
990+
"Hidden field should be validated when applyBindingsToHiddenFields is true",
991+
binder.isValid());
992+
993+
nameBinding.setIsAppliedPredicate(p -> false);
994+
assertTrue(
995+
"Per-binding predicate should override Binder-level applyBindingsToHiddenFields",
996+
binder.isValid());
997+
}
998+
999+
@Test
1000+
public void writeBean_applyBindingsToHiddenFields_writesHiddenFieldsWhenEnabled()
1001+
throws ValidationException {
1002+
Binding<Person, String> nameBinding = binder.forField(nameField)
1003+
.bind(Person::getFirstName, Person::setFirstName);
1004+
binder.readBean(item);
1005+
1006+
nameField.setValue("NewName");
1007+
((Component) nameBinding.getField()).setVisible(false);
1008+
1009+
binder.writeBean(item);
1010+
assertEquals("Hidden field should not be written by default",
1011+
"Johannes", item.getFirstName());
1012+
1013+
binder.setApplyBindingsToHiddenFields(true);
1014+
binder.writeBean(item);
1015+
assertEquals(
1016+
"Hidden field should be written when applyBindingsToHiddenFields is true",
1017+
"NewName", item.getFirstName());
1018+
}
1019+
1020+
@Test
1021+
public void writeBeanIfValid_applyBindingsToHiddenFields_writesHiddenFieldsWhenEnabled() {
1022+
Binding<Person, String> nameBinding = binder.forField(nameField)
1023+
.bind(Person::getFirstName, Person::setFirstName);
1024+
binder.readBean(item);
1025+
1026+
nameField.setValue("NewName");
1027+
((Component) nameBinding.getField()).setVisible(false);
1028+
1029+
assertTrue(binder.writeBeanIfValid(item));
1030+
assertEquals("Hidden field should not be written by default",
1031+
"Johannes", item.getFirstName());
1032+
1033+
binder.setApplyBindingsToHiddenFields(true);
1034+
assertTrue(binder.writeBeanIfValid(item));
1035+
assertEquals(
1036+
"Hidden field should be written when applyBindingsToHiddenFields is true",
1037+
"NewName", item.getFirstName());
1038+
}
1039+
9751040
@Test
9761041
public void writeBean_isAppliedIsEvaluated() {
9771042
AtomicInteger invokes = new AtomicInteger();

0 commit comments

Comments
 (0)