Is it possible to removeBinding() and then bind() it again AFTER the bean has been read.
My use case I have an entity which has two sets of fields that need to be entered based on some boolean field.
E.g. If optionA is true then fields a, b & c are to be entered and validated.
If optionA is false then fields a, b, d and e are to be entered and validated.
Note that optionA is also a bound field.
I have a checkbox in the form for the optionA boolean value, and then bind and unbind the relevant fields triggered by a value change listener on the checkbox.
However this doesn’t seem to work because one can’t bind a field after the bean has been read. I can’t re-read the bean because that will overwrite the optionA field.
Does anyone know a better way to do this kind of dual form entry?
import java.lang.reflect.Field;
import java.util.Set;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.PropertySet;
/**
* This is a DIRTY workaround since at this version of Vaadin the Binder is not removing the Binding of a field so keeps validation on wrong
* fields..
* This 'hack' seems to do the job (until we move to a newer version perhaps?)
*
*
* @param <T>
*/
public class WorkaroundBinder<T> extends Binder<T> {
private static final long serialVersionUID = 1L;
public WorkaroundBinder() {
super();
}
public WorkaroundBinder(Class<T> beanType, boolean scanNestedDefinitions) {
super(beanType, scanNestedDefinitions);
}
public WorkaroundBinder(Class<T> beanType) {
super(beanType);
}
public WorkaroundBinder(PropertySet<T> propertySet) {
super(propertySet);
}
@SuppressWarnings("unchecked")
@Override
protected void removeBindingInternal(Binding<T, ?> binding) {
super.removeBindingInternal(binding);
try {
// if binder does not validate changedBindings is not cleared (possibly a mistake in doWriteIfValid method) as such
// a reference to binding might be left hanging in that set.
// This hack removes hanging reference from changedBindings .
Field field = Binder.class.getDeclaredField("changedBindings");
field.setAccessible(true);
Set<Binding<T, ?>> changedBindings = (Set<Binding<T, ?>>) field.get(this);
changedBindings.remove(binding);
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
}
}
Then I bind only the common ones at the object…
binder.forField(optionAField).asRequired…blah blah blah…
and I add a valueChangeListener on optionAField
if true
binder.removeBinding(optionBField);
binder.forField(optionCField)…blah blah blah…
if false
binder.removeBinding(optionCField);
binder.forField(optionBField);…
Note: I’m not using binder.readBean(x) but binder.setBean(x) (don’t know if it makes any difference) but it seems to work like this on Vaadin 13.x.x.
Thanks for the pointers. I didn’t realize there was an issue with binding and unbinding fields. I will try the workaorund and see how it goes.
But I suspect my issue might be different because I am using readBean(). If I bind a field after the readBean() then the field value of the underlying bean is not copied into the binder field buffer. Maybe what I need to do is bind ALL fields before the readBean() and then immediately unbind those that are not necessary. And use your WorkaroundBinder as well.