Table update problem

Hello,
I was trying to update table cell, but got infinite loop.
I make two columns table:
First column - combobox;
Second column - selected value from combobox.
When select combobox value I got infinite loop on valueChange event.

How to do this right way?

public class MyVaadinUI extends UI
{
    @WebServlet(value = "/*", asyncSupported = true)
    @VaadinServletConfiguration(productionMode = false, ui = MyVaadinUI.class, widgetset = "com.my.tabletest.AppWidgetSet")
    public static class Servlet extends VaadinServlet {
    }

    @Override
    protected void init(VaadinRequest request) {
        final VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        setContent(layout);
        
        final IndexedContainer cont = new IndexedContainer();
        cont.addContainerProperty("f1", String.class, null);
        cont.addContainerProperty("f2", String.class, null);
        Item item = cont.addItem(1);
        item.getItemProperty("f1").setValue("text1");
        item.getItemProperty("f2").setValue("text2");
        
        final Table table = new Table("test", cont);
        table.setImmediate(true);
        table.setEditable(true);
        table.setTableFieldFactory(new TableFieldFactory() {
            @Override
            public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) {
                if("f1".equals(propertyId)) {
                    final ComboBox combo = new ComboBox();
                    combo.setImmediate(true);
                    combo.setNullSelectionAllowed(false);
                    combo.addItem("item 1");
                    combo.addItem("item 2");
                    combo.addItem("item 3");
                    combo.addValueChangeListener(new Property.ValueChangeListener() {
                        @Override
                        public void valueChange(Property.ValueChangeEvent event) {
                            Object itemId = cont.getItemIds().get(0);
                            String val = event.getProperty().getValue().toString();
                            // infinite loop here
                            // vvvvvvvvvvvvvvvvvvvv
                            cont.getItem(itemId).getItemProperty("f2").setValue(val);
                        }
                    });
                    return combo;
                }
                return null;
            }
        });
        layout.addComponent(table);
    }
}

Hello Daniil,

The reason why your solution ends in an infinite loop is that when the table binds the property to the combobox, it also updates its value and dispatches a ValueChangeEvent, leading to an infinite loop. The use of TableFieldFactory is not really designed with ValueChangeListeners in mind, so one needs to be careful when combining the two.

One way to have the 2nd column display the selection of the combobox is to utilize Vaadin’s built-in data binding mechanism, which takes care of handling the value changes for you. Below you’ll find a solution using a generated column instead of another property, as in practice there is only one property that you want to show twice in the table:

Item item = cont.addItem(1);
item.getItemProperty("f1").setValue("text 1");
final Table table = new Table("test", cont);
table.setImmediate(true);
table.setEditable(true);
table.setTableFieldFactory(new DefaultFieldFactory(){
    
    @Override
    public Field createField(Container container, Object itemId, Object propertyId, Component uiContext) {
        if ("f1".equals(propertyId)){
            ComboBox combo = new ComboBox();
            combo.addItem("item 1");
            combo.addItem("item 2");
            combo.addItem("item 3");
            return combo;
        }
        // have the DefaultFieldFactory take care of fields for other properties
        return super.createField(container, itemId, propertyId, uiContext);
    }
});

table.addGeneratedColumn("secondField", new ColumnGenerator(){

    @Override
    public Object generateCell(Table source, Object itemId, Object columnId) {
        Label label = new Label();

        // use the same property for the label as the combobox is using
        label.setPropertyDataSource(cont.getContainerProperty(itemId, "f1"));
        return label;
    }
});

layout.addComponent(table);

Hopefully this helps you move forward.

Olli