Edit: I found the solution myself, there is no need to read all this if you are simply trying to help and not having a similar problem yourself ;)
First of all, I have asked this question in the Vaadin Forum, but I would like to ask here too in case it is not a bug and I am just doing something wrong.
A ComboBox (ComboBox<Bar>
) of mine that relies on the value of another ComboBox (ComboBox<Foo>
) does not update its value as expected when the ComboBox<Foo>
value changes.
At first, both comboBoxes are empty.
When I select an option for ComboBox<Foo>
the options of the other ComboBox are being manually rebuilt.
If I then select an option for ComboBox<Bar>
it's getting selected, all is fine until now.
But when I now select another option for ComboBox<Foo>
, let's say none/null, the Options of ComboBox<Bar>
are being rebuilt, but its value remains! (it should be null)
Here is my (simplified) code to eplain/reproduce my problem. I know it's a bit much but I really want to be clear what my problem is and what I did:
public class FooBar {
private Foo foo = null;
public FooBar(){
}
// getters and setters
...
}
public class Foo {
private Collection<Bar> bars;
private Bar selectedBar = null;
public Foo(Collection<Bar> bars){
this.bars = bars;
}
// getters and setters
...
}
public class Bar {
private String name;
public Bar(String name){
this.name = name;
}
// getters and setters
...
}
In my View, I have a Binder and there is a ComboBox and also a ComboBox. They are being set up like this:
Binder<FooBar> binder = new Binder<>(new FooBar());
binder.addValueChangeListener(event -> {
binder.setBean(binder.getBean()); // forces bound components to get the value again / refresh
refillBarOptions();
})
ComboBox<Foo> cbFoo = new ComboBox<>();
cbFoo.setCaption("foo");
Collection<Foo> allFoos = fooService.findAll(); // the foos do NOT have a selectedBar yet
ListDataProvider<Foo> ldpFoo = new ListDataProvider<Foo>(allFoos);
cbFoo.setDataProvider(ldpFoo);
binder.forField(cbFoo)
.bind(
fooBar -> fooBar.getFoo(), //valueprovider
(fooBar, foo) -> fooBar.setFoo(foo) //setter
);
// so initially this combobox' value is empty because the FooBar.getFoo() returns null. thats OK!
ComboBox<Bar> cbBar = new ComboBox<>();
cbBar.setCaption("bar");
cbBar.setDataProvider(new ListDataProvider<Bar>(new HashSet<>())); // no items initially because the Foo-ComboBox has no value yet
cbBar.setItemCaptionGenerator(Bar::getName);
binder.forField(cbBar)
.bind(
fooBar -> fooBar.getFoo() == null ? null : fooBar.getFoo().getSelectedBar(), //valueprovider
(fooBar, bar) -> {
if(fooBar.getFoo() != null){
fooBar.getFoo().setSelectedBar(bar);
}
}
);
cbBar.setEnabled(false) // disable because no items initially
/**
* gets called when binder value has changed.
* This way I circumvent the fact that a normal valueChangeListener of a component is fired before the binder-bean is updated
*/
private void refillBarOptions(){
Foo selectedFoo = binder.getBean().getFoo();
if(selectedFoo != null){
cbBar.setDataProvider<>(new ListDataProvider<>(selectedFoo.getBars())); //cbBar is actually not a local variable so it can be accessed here
cbBar.setEnabled(true);
} else {
// there is no foo selected, therefore no bars should be allowed to be selected
cbBar.setDataProvider(new ListDataProvider<Bar>(new HashSet<>()));
cbBar.setEnabled(false);
}
}
Should I open a new Issue about this or am I doing something wrong? Any help would be appreciated. Thanks
I could not see the wood for the trees here.. The problem was solved by calling binder.setBean(binder.getBean());
again at the end of refillBarOptions()
private void refillBarOptions(){
Foo selectedFoo = binder.getBean().getFoo();
if(selectedFoo != null){
cbBar.setDataProvider<>(new ListDataProvider<>(selectedFoo.getBars())); //cbBar is actually not a local variable so it can be accessed here
cbBar.setEnabled(true);
} else {
// there is no foo selected, therefore no bars should be allowed to be selected
cbBar.setDataProvider(new ListDataProvider<Bar>(new HashSet<>()));
cbBar.setEnabled(false);
}
binder.setBean(binder.getBean());
}