Skip to content

Commit 1101946

Browse files
authored
feat: add support for accepting hidden fields in Binder (#24139)
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 7463405 commit 1101946

2 files changed

Lines changed: 104 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
@@ -2011,9 +2011,13 @@ private void removeFromChangedBindingsIfReverted(
20112011
}
20122012

20132013
public SerializablePredicate<Binding<BEAN, TARGET>> getIsAppliedPredicate() {
2014-
return isAppliedPredicate == null
2015-
? Binding.super.getIsAppliedPredicate()
2016-
: isAppliedPredicate;
2014+
if (isAppliedPredicate != null) {
2015+
return isAppliedPredicate;
2016+
}
2017+
if (getBinder().isApplyBindingsToHiddenFields()) {
2018+
return binding -> true;
2019+
}
2020+
return Binding.super.getIsAppliedPredicate();
20172021
}
20182022

20192023
@Override
@@ -2156,6 +2160,8 @@ void setIdentity() {
21562160

21572161
private boolean changeDetectionEnabled = false;
21582162

2163+
private boolean applyBindingsToHiddenFields = false;
2164+
21592165
private ValueSignal<BinderValidationStatus<BEAN>> binderValidationStatusSignal;
21602166

21612167
/**
@@ -4324,6 +4330,40 @@ public boolean isValidatorsDisabled() {
43244330
return validatorsDisabled;
43254331
}
43264332

4333+
/**
4334+
* Sets whether all bindings of this Binder should be applied to fields that
4335+
* are not currently visible. By default, bindings whose field is a
4336+
* {@link Component} with {@link Component#isVisible()} returning
4337+
* {@literal false} are skipped during validation and when writing values to
4338+
* the bean. Enabling this setting restores the pre-Vaadin 25 behavior where
4339+
* hidden fields are validated and written just like visible ones.
4340+
* <p>
4341+
* This is a Binder-level fallback: any binding that has its own predicate
4342+
* set via {@link Binding#setIsAppliedPredicate(SerializablePredicate)}
4343+
* continues to use that predicate and is not affected by this flag.
4344+
* <p>
4345+
* Defaults to {@literal false}.
4346+
*
4347+
* @param applyBindingsToHiddenFields
4348+
* {@literal true} to make all bindings apply to hidden fields,
4349+
* {@literal false} to skip hidden fields (the default)
4350+
*/
4351+
public void setApplyBindingsToHiddenFields(
4352+
boolean applyBindingsToHiddenFields) {
4353+
this.applyBindingsToHiddenFields = applyBindingsToHiddenFields;
4354+
}
4355+
4356+
/**
4357+
* Returns whether all bindings of this Binder apply to fields that are not
4358+
* currently visible.
4359+
*
4360+
* @return {@literal true} if bindings are applied to hidden fields,
4361+
* {@literal false} if hidden fields are skipped (the default)
4362+
*/
4363+
public boolean isApplyBindingsToHiddenFields() {
4364+
return applyBindingsToHiddenFields;
4365+
}
4366+
43274367
/**
43284368
* Control whether bound fields implementing {@link HasValidator} subscribe
43294369
* for field's {@code ValidationStatusChangeEvent}s and will

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,67 @@ void withValidator_isAppliedIsEvaluated() {
956956
assertTrue(binder.isValid());
957957
}
958958

959+
@Test
960+
void setApplyBindingsToHiddenFields_defaultsToFalse_appliesToHiddenFieldsWhenEnabled() {
961+
Binding<Person, String> nameBinding = binder.forField(nameField)
962+
.withValidator(name -> false, "")
963+
.bind(Person::getFirstName, Person::setFirstName);
964+
binder.setBean(item);
965+
966+
assertFalse(binder.isApplyBindingsToHiddenFields());
967+
968+
((Component) nameBinding.getField()).setVisible(false);
969+
assertTrue(binder.isValid(),
970+
"Hidden field should be skipped by default");
971+
972+
binder.setApplyBindingsToHiddenFields(true);
973+
assertFalse(binder.isValid(),
974+
"Hidden field should be validated when applyBindingsToHiddenFields is true");
975+
976+
nameBinding.setIsAppliedPredicate(p -> false);
977+
assertTrue(binder.isValid(),
978+
"Per-binding predicate should override Binder-level applyBindingsToHiddenFields");
979+
}
980+
981+
@Test
982+
void writeBean_applyBindingsToHiddenFields_writesHiddenFieldsWhenEnabled()
983+
throws ValidationException {
984+
Binding<Person, String> nameBinding = binder.forField(nameField)
985+
.bind(Person::getFirstName, Person::setFirstName);
986+
binder.readBean(item);
987+
988+
nameField.setValue("NewName");
989+
((Component) nameBinding.getField()).setVisible(false);
990+
991+
binder.writeBean(item);
992+
assertEquals("Johannes", item.getFirstName(),
993+
"Hidden field should not be written by default");
994+
995+
binder.setApplyBindingsToHiddenFields(true);
996+
binder.writeBean(item);
997+
assertEquals("NewName", item.getFirstName(),
998+
"Hidden field should be written when applyBindingsToHiddenFields is true");
999+
}
1000+
1001+
@Test
1002+
void writeBeanIfValid_applyBindingsToHiddenFields_writesHiddenFieldsWhenEnabled() {
1003+
Binding<Person, String> nameBinding = binder.forField(nameField)
1004+
.bind(Person::getFirstName, Person::setFirstName);
1005+
binder.readBean(item);
1006+
1007+
nameField.setValue("NewName");
1008+
((Component) nameBinding.getField()).setVisible(false);
1009+
1010+
assertTrue(binder.writeBeanIfValid(item));
1011+
assertEquals("Johannes", item.getFirstName(),
1012+
"Hidden field should not be written by default");
1013+
1014+
binder.setApplyBindingsToHiddenFields(true);
1015+
assertTrue(binder.writeBeanIfValid(item));
1016+
assertEquals("NewName", item.getFirstName(),
1017+
"Hidden field should be written when applyBindingsToHiddenFields is true");
1018+
}
1019+
9591020
@Test
9601021
void writeBean_isAppliedIsEvaluated() {
9611022
AtomicInteger invokes = new AtomicInteger();

0 commit comments

Comments
 (0)