0

I have two ComboBoxes in my editable Grid where the second ComboBox based on the first one. So for example you would have Car Make and then Car Model. When you change the make combobox the model combobox then changes accordingly.

With that in mind I have:

ComboBox<String> makeComboBox = new ComboBox<String>();
ComboBox<String> modelComboBox = new ComboBox<String>();

Specifically:

grid.addColumn(CarRental::getPerson)
    .setEditorBinding(binder.forField(personComboxBox).bind(CarRental::getPerson, CarRental::setPerson));
grid.addColumn(CarRental::getMake)
    .setEditorBinding(binder.forField(makeComboxBox).bind(CarRental::getMake, CarRental::setMake));
grid.addColumn(CarRental::getModel)
    .setEditorBinding(binder.forField(modelComboxBox).bind(CarRental::getModel, CarRental::setModel));

The key here is that I want the modelComboBox to change if the makeComboBox is changed. In other words if you select Honda then I want the model ComboBox to change to Fit, Civic, Accord, and so on. To do this I add a SelectionListener (but it could also be a ValueChangeListener, it doesn't matter, the effect is still the same).

Specifcally I have:

makeComboBox.addSelectionListener(event -> 
{
    modelComboBox.clear();
    modelComboBox.setItems(getModelsBasedOnMake(makeComboBox.getValue()));
    // Assuming someone has just edited the make value, 
    // say changed from Toyota to Honda, then I want the model selected to be empty 
});

Because the ComboBox can be different I've added some logic to update the components on theOpenListenerfor the Grid Editor. Specifically I do:

grid.getEditor().addOpenListener(open ->
{
   ...
   CarRental selectedCarRental = (CarRental)event.getBean();
   makeComboBox.setItems(makeList);
   modelComboBox.setItems(getModelsBasedOnMake(carRental.getMake()));
});

The problem here is that the modelComBoxbox tends to be unselected because if you look at it there's no guarantee which value it will be because there is a conflict.

I looked at temporarily disabling the selectionListener but this all the remove listeners have been deprecated with Vaadin 8. Therefore how can I setup the grid to be able to edit both the car make and model in the grid?

Stephane Grenier
  • 15,527
  • 38
  • 117
  • 192
  • When updating many fields with related ValueChangeListeners I have been using member field `isLoading` that is checked inside other listeners so that they know when they should not do anything. Would this approach help you as well? – Mika Apr 05 '18 at 10:27
  • "but this all the remove listeners have been deprecated with Vaadin 8" -> The `add*Listener` methods return an object of type `Registration` that has a method `remove`. Is that what you were meaning? – Steffen Harbich Apr 05 '18 at 12:49

1 Answers1

0

I tried it by a simple example. Looks ok for me. What is the exact problem? (I don't really get your sentence "because if you look at it there's no guarantee which value it will be because there is a conflict.")

@SpringUI
public class VaadinUI extends UI {

    @Override
    protected void init(VaadinRequest request) {
        VerticalLayout layout = new VerticalLayout();

        ComboBox<String> cmb1 = new ComboBox<>();
        ComboBox<String> cmb2 = new ComboBox<>();
        cmb1.setItems("1", "2", "3");
        cmb1.addSelectionListener(event -> {
            cmb2.clear();
            cmb2.setItems(getCmb2Content(event.getValue()));
        });

        Grid<MyBean> grid = new Grid<>();
        grid.setWidth("800px");
        grid.setHeightByRows(10);
        grid.addColumn(System::identityHashCode).setCaption("ID");
        grid.addColumn(MyBean::getProp1).setCaption("Prop 1")
                .setEditorBinding(grid.getEditor().getBinder().forField(cmb1).bind(MyBean::getProp1, MyBean::setProp1));
        grid.addColumn(MyBean::getProp2).setCaption("Prop 2")
                .setEditorBinding(grid.getEditor().getBinder().forField(cmb2).bind(MyBean::getProp2, MyBean::setProp2));
        grid.setItems(new MyBean(), new MyBean(), new MyBean());
        grid.getEditor().setEnabled(true);

        layout.addComponent(grid);

        setContent(layout);
    }

    private List<String> getCmb2Content(String cmb1Content) {
        return Arrays.asList(cmb1Content + "1", cmb1Content + "2", cmb1Content + "3");
    }

}

public static class MyBean {

    private String prop1;
    private String prop2;

    public String getProp1() {
        return prop1;
    }

    public void setProp1(String prop1) {
        this.prop1 = prop1;
    }

    public String getProp2() {
        return prop2;
    }

    public void setProp2(String prop2) {
        this.prop2 = prop2;
    }
}
Steffen Harbich
  • 2,639
  • 2
  • 37
  • 71
  • if part of your logic in the listener is to set cmb2 based on the value of cmb1 then this will fail because the binder will set the value for cmb1 which will then set the value for cmb2. But what if your value for cmb2 is an overriden value? So for example let's say your table is for car rentals. The columns are customerName, make, and model. Your combobox on customerName states that when John is selected you want the make and model to be Honda Civic. However in your table data your entry is John with a Honda Accord (he wanted something different then his usual). – Stephane Grenier Apr 06 '18 at 01:10
  • In that case what do you expect the make and model comboboxes to show? The binder will want to select Honda Accord whereas the combobox listener will want to select Honda Civic. You basically need a way to prevent the combobox listener from being activated until the editor is ready... – Stephane Grenier Apr 06 '18 at 01:11
  • IMO the data shown in the Grid must match the underlying data. So if you have fetched the data from the data source (e.g. database) to memory, then you can easily edit the data in-memory via UI and save data to data source on demand. – Steffen Harbich Apr 06 '18 at 10:20