Skip to content

Commit

Permalink
Introduced binder change detection for readBean case
Browse files Browse the repository at this point in the history
  • Loading branch information
onuridrisoglu committed May 31, 2024
1 parent edc168d commit d201945
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 11 deletions.
56 changes: 45 additions & 11 deletions flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java
Original file line number Diff line number Diff line change
Expand Up @@ -1512,7 +1512,7 @@ private void handleFieldValueChange(

if (binder != null) {
// Inform binder of changes; if setBean: writeIfValid
getBinder().handleFieldValueChange(this);
getBinder().handleFieldValueChange(this, event);
getBinder().fireEvent(event);
}
}
Expand Down Expand Up @@ -1809,6 +1809,7 @@ void setIdentity() {

private BindingExceptionHandler exceptionHandler = new DefaultBindingExceptionHandler();

private Map<Binding<BEAN, ?>, Object> bindingInitialValuesMap = new HashMap<>();
private Set<Binding<BEAN, ?>> changedBindings = new LinkedHashSet<>();

private boolean validatorsDisabled = false;
Expand Down Expand Up @@ -1918,8 +1919,9 @@ public static <BEAN> Binder<BEAN> withPropertySet(
/**
* Informs the Binder that a value in Binding was changed.
*
* If {@link #readBean(Object)} was used, this method will only validate the
* changed binding and ignore state of other bindings.
* If {@link #readBean(Object)} was used, this method will check if the value is reverted
* to initial value, in this case the binding will be removed from the changed bindings.
* This method will also validate the changed binding and ignore state of other bindings.
*
* If {@link #setBean(Object)} was used, all pending changed bindings will
* be validated and non-changed ones will be ignored. The changed value will
Expand All @@ -1928,18 +1930,41 @@ public static <BEAN> Binder<BEAN> withPropertySet(
*
* @param binding
* the binding whose value has been changed
* @param event
* the value change event
*/
protected void handleFieldValueChange(Binding<BEAN, ?> binding) {
changedBindings.add(binding);

protected void handleFieldValueChange(Binding<BEAN, ?> binding, ValueChangeEvent<?> event) {
if (getBean() == null) {
if (!bindingInitialValuesMap.containsKey(binding)) {
// If the field is changed the first time, keep the old value as the initial value
bindingInitialValuesMap.put(binding, event.getOldValue());
changedBindings.add(binding);
} else if (isRevertedToInitialValue(binding, event.getValue())) {
changedBindings.remove(binding);
}
binding.validate();
} else {
changedBindings.add(binding);
doWriteIfValid(getBean(), changedBindings);
}
}


/**
* If {@link #readBean(Object)} was used, checks if the field value is
* reverted to the initial value in bean
*
* @param binding
* the binding whose value has been changed
* @param newValue
* the new value if the field
* @return true if the field value is reverted
*/
protected boolean isRevertedToInitialValue(Binding<BEAN, ?> binding, Object newValue) {
return Objects.equals(bindingInitialValuesMap.get(binding), newValue);
}

/**
* Returns the bean that has been bound with {@link #bind}, or null if a
* bean is not currently bound.
*
Expand Down Expand Up @@ -2307,7 +2332,7 @@ public void readBean(BEAN bean) {
binding.initFieldValue(bean, false);
}
});
changedBindings.clear();
clearChangedBindings();
getValidationStatusHandler().statusChange(
BinderValidationStatus.createUnresolvedStatus(this));
fireStatusChangeEvent(false);
Expand Down Expand Up @@ -2561,14 +2586,14 @@ private BinderValidationStatus<BEAN> doWriteIfValid(BEAN bean,
* Changes have been successfully saved. The set is only cleared
* when the changes are stored in the currently set bean.
*/
changedBindings.clear();
clearChangedBindings();
} else if (getBean() == null) {
/*
* When using readBean and writeBean there is no knowledge of
* which bean the changes come from or are stored in. Binder is
* no longer "changed" when saved succesfully to any bean.
*/
changedBindings.clear();
clearChangedBindings();
}
}

Expand Down Expand Up @@ -2738,7 +2763,15 @@ private void clearFields() {
if (hasChanges()) {
fireStatusChangeEvent(false);
}
changedBindings.clear();
clearChangedBindings();
}

/**
* Clear changed bindings and initial values cache
*/
private void clearChangedBindings() {
bindingInitialValuesMap.clear();
changedBindings.clear();
}

/**
Expand Down Expand Up @@ -3312,7 +3345,7 @@ public void setReadOnly(boolean readOnly) {
}

private void doRemoveBean(boolean fireStatusEvent) {
changedBindings.clear();
clearChangedBindings();
if (bean != null) {
bean = null;
}
Expand Down Expand Up @@ -3773,6 +3806,7 @@ protected void removeBindingInternal(Binding<BEAN, ?> binding) {
boundProperties.entrySet()
.removeIf(entry -> entry.getValue().equals(binding));
changedBindings.remove(binding);
bindingInitialValuesMap.remove(binding);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,37 @@ public void update_bound_propertyIsUpdated() throws ValidationException {
Assert.assertEquals(nameValue, updatedPerson.getFirstName());
Assert.assertEquals(0, updatedPerson.getAge());
}

@Test
public void update_to_initial_value_removes_binding_from_changedBindings() throws ValidationException {
Person person = new Person();
String initialName = "Foo";
person.setFirstName(initialName);
person.setAge(20);

Binder<Person> binder = new Binder<>();
Binding<Person, String> nameBinding = binder.bind(nameField, Person::getFirstName, Person::setFirstName);
Binding<Person, Integer> ageBinding = binder.forField(ageField)
.withConverter(new StringToIntegerConverter(""))
.bind(Person::getAge, Person::setAge);

binder.readBean(person);
nameField.setValue("Bar");

assertEquals(1, binder.getChangedBindings().size());
assertTrue(binder.getChangedBindings().contains(nameBinding));

ageField.setValue("21");
assertEquals(2, binder.getChangedBindings().size());

nameField.setValue(initialName);

assertEquals(1, binder.getChangedBindings().size());
assertTrue(binder.getChangedBindings().contains(ageBinding));

ageField.setValue("20");
assertTrue(binder.getChangedBindings().isEmpty());
}

@Test
public void save_bound_beanAsDraft() {
Expand Down

0 comments on commit d201945

Please sign in to comment.