Vaadin 8 - ComboBox 值在 bean 更改后仍然存在

Vaadin 8 - ComboBox value remains after the bean has changed

编辑: 我自己找到了解决方案,如果您只是想提供帮助并且自己没有遇到类似问题,则无需阅读所有内容 ;)


首先,我问过this question in the Vaadin Forum,但我也想在这里问一下,以防万一这不是错误,我只是做错了什么。

我的一个 ComboBox (ComboBox<Bar>) 依赖于另一个 ComboBox (ComboBox<Foo>) 的值,当 ComboBox<Foo> 值改变时,它没有按预期更新它的值。

起初,两个组合框都是空的。
当我 select ComboBox<Foo> 的选项时,正在手动重建另一个 ComboBox 的选项。
如果我然后 select ComboBox<Bar> 的选项得到 selected,到现在为止一切都很好。
但是当我现在 select ComboBox<Foo> 的另一个选项时,比方说 none/null,正在重建 ComboBox<Bar> 的选项, 但它的值仍然存在!(应该为空)

这是 eplain/reproduce 我的问题的(简化)代码。我知道这有点多,但我真的想弄清楚我的问题是什么以及我做了什么:

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
    ...
}

在我的视图中,我有一个活页夹和一个组合框,还有一个组合框。它们的设置如下:

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);
    }
}

我应该为此开一个新的 Issue 还是我做错了什么? 任何帮助,将不胜感激。谢谢

这里只见树木不见森林..在refillBarOptions()

结束时再次调用binder.setBean(binder.getBean());解决了问题
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());
}