Table causes an infinity loop updating a property

Hi!

I have this example of a table with items with two factors
factor1 editable
factor2 not editable

factor2 is calculated from factor1, so each time the user changes the value of factor1, the value of factor2 has to be recalculated.

To get this I add a ValueChangeListener to the field hosting the factor1 so the factor2 is recalculated from the new value entered by the user. The problem starts here. After the user updates the value of the factor1, my listener updates the value of the factor 2 and then the table (guess) attempts to refresh the caché and somehow fires the ValueChangeEvent of the factor1 another time and then the infity loop is created…

Can you see if there is something wrong with my code (I don’t) or it may be a bug of the component? (Vaadin 7.1.13 btw)

Here it is my code (app example and Bean item)

Thanks!

public class MyVaadinUI extends UI {

private final static Table table = new Table();

private final static DummyBean[] data = new DummyBean[]

{
new DummyBean(1, “Dummy 1”, 13.2, 9.0),
new DummyBean(1, “Dummy 2”, 73.2, 98.0) };

private BeanItemContainer<DummyBean> bic = new BeanItemContainer<DummyBean>(
        DummyBean.class);

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

@Override
protected void init(VaadinRequest request) {
    final VerticalLayout layout = new VerticalLayout();
    layout.setMargin(true);
    setContent(layout);

    bic.addAll(Arrays.asList(data));

    table.setEditable(true);
    table.setImmediate(true);
    table.setContainerDataSource(bic);
    table.setTableFieldFactory(new MyFieldFactory());

    layout.addComponent(table);
}

private class MyFieldFactory implements TableFieldFactory {

    @Override
    public Field<?> createField(Container container, Object itemId,
            Object propertyId, Component uiContext) {
        TextField field = new TextField((String) propertyId);

        DummyBean bean = (DummyBean) itemId;

        if ("factor2".equals(propertyId)) {
            return null;
        } else if ("factor1".equals(propertyId)) {
            field.setImmediate(true);
            field.setData(itemId);
            if (bean != null) {
                field.setValue(String.valueOf(bean.getFactor1()));
            }


field.addValueChangeListener(new MyListener(bean));

field.setNullRepresentation(“0”);
return field;
}
return null;

    }
}

private class MyListener implements ValueChangeListener {

    private DummyBean data = null;

    public MyListener(DummyBean data) {
        super();
        this.data = data;
    }

    public DummyBean getData() {
        return data;
    }

    @Override
    public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
        
        System.out.println("Enter Value change");

        DummyBean bean = (DummyBean) ((BeanItem) table
                .getContainerDataSource().getItem(data)).getBean();
        
        [b]

table.getContainerDataSource().getItem(data)
.getItemProperty(“factor2”)
.setValue(data.getFactor2() * bean.getFactor1());
[/b]

        System.out.println("Exit Value change");
    }

}

}

public class DummyBean {
private int id;
private String name;
private Double factor1;
private Double factor2;
public DummyBean(int id, String name, Double factor1, Double factor2) {
super();
this.id = id;
this.name = name;
this.factor1 = factor1;
this.factor2 = factor2;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getFactor1() {
return factor1;
}
public void setFactor1(Double factor1) {
this.factor1 = factor1;
}
public Double getFactor2() {
return factor2;
}
public void setFactor2(Double factor2) {
this.factor2 = factor2;
}
}

13559.java (3.01 KB)
13560.java (791 Bytes)

What if you do this instead? In valueChange:

DummyBean bean = (DummyBean) ((BeanItem) table.getContainerDataSource().getItem(data)).getBean();
bean.setFactor2(data.getFactor1() * data.getFactor2());
table.refreshRowCache();

Hi Joacim, thanks for your answer! but I’m afraid that I’ve got the same result.

In fact the last line is not even executed because it enters on the infinity loop during the setValue(), actually because of an inner call to refreshRowCache();

Just in case anyone needs it, I have found a workaround to this problem.
The Table class has a disableContentRefreshing() method that is suppose to avoid the refresh of a component. the method is protected so you need to extends the Table to access it.
So the solution is to call this method just before the setValue(), and then just after enable it another time with the method enableContentRefreshing()

disableContentRefreshing();
getContainerDataSource().getItem(id).getItemProperty("property").setValue(5.5);
enableContentRefreshing(true);

One more solution that i didn’t know, declare the calculated property as a nested property with addNestedContainerProperty(). Not sure that it would work but i guess it would do it.

That is very strange. It works perfectly over here (without any loops).

public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
System.out.println(“Enter Value change”);

DummyBean bean = (DummyBean) ((BeanItem) table.getContainerDataSource().getItem(data)).getBean();
bean.setFactor2(data.getFactor1() * data.getFactor2());
table.refreshRowCache();

System.out.println("Exit Value change");

}

But if you fixed it then it’s all good. :slight_smile: