How to update client after table's container data source bean is updated

Hi,

I created a table that is bound to a container using table.setContainerDataSource(…). When I update the bean in the container, the UI (client) isn’t updated until a table refresh/repaint event occurs. I am currently relying on a crude table.setValue(table.getValue()) call to trigger the client update, but I was wondering if there is a best/better way to do this?

Here’s a code snippet to illustrate my example:


public class TableRepaintApplication extends Application {

	@Override
	public void init() {
		Window mainWindow = new Window("Table Repaint Application");
		final Table table = new Table();
		table.addContainerProperty("A", String.class, null, "A", null, null);		
		BeanContainer<Integer, TestBean> beanContainer = new BeanContainer<Integer, TestBean>(TestBean.class);
		TestBean bean = new TestBean();
		bean.setId(1);
		bean.setA("A");
		beanContainer.setBeanIdProperty("id");
		beanContainer.addBean(bean);
		table.setContainerDataSource(beanContainer);
		mainWindow.addComponent(table);
		Button button = new Button("Test");
		button.addListener(new ClickListener() {
			public void buttonClick(ClickEvent event) {
				BeanContainer container = (BeanContainer) table.getContainerDataSource();
				BeanItem item = container.getItem(1);
				TestBean bean = (TestBean) item.getBean();
				bean.setA(event.getButton().getCaption());
				// this seems to be the quickest/easiest way to trigger the client to be updated with the changes...is it the best way?
				table.setValue(table.getValue());
			}
		});
		mainWindow.addComponent(button);
		setMainWindow(mainWindow);
	}

}

Hi,

The table can’t know things have changed if you update the bean directly, but if you update trough the Vaadin APIs it will automatically update. Something like:

item.getItemProperty("A").setValue(event.getButton().getCaption());

Best Regards,
Marc

I don’t know if that was an example or the real case, but In that case I would update the BeanItem instead of the bean itself. Changes to beanitem is passed forward to the bean itself and the container listens to these changes, and the table listens on the container. That will make the update immediate.

There are other ways to force the table to update as well, but all of the needed methods are marked protected, so you would have extend the Table to turn them into public. It is a while since I did it the last time but I think it was protected void enableContentRefreshing(boolean), protected void refreshRenderedCells(), protected void resetPageBuffer() or a combination of those.

Thanks, Marc. When I try your suggestion, I get an NPE because item.getItemProperty(“A”) is null. I don’t see how I can update the bean through the BeanItem if I’m using a container for the datasource. Am I missing something?

It’s strange that you got an NPE. I tried to run your sample by modifying a tiny bit of the code (adding a random string generator and invoking additional methods to the table) and I found everything work as expected. Maybe this could be useful for you.


public class TableRepaintApplication extends Application {		
    @Override
    public void init() {
        Window mainWindow = new Window("Table Repaint Application");
        final Table table = new Table();
        table.addContainerProperty("A", String.class, null, "A", null, null);        
        BeanContainer<Integer, TestBean> beanContainer = new BeanContainer<Integer, TestBean>(TestBean.class);
        TestBean bean = new TestBean();
        bean.setId(1);
        bean.setA("A");
        beanContainer.setBeanIdProperty("id");
        beanContainer.addBean(bean);
        table.setContainerDataSource(beanContainer);
        table.setImmediate(true);        
        table.setWidth("100px");
        mainWindow.addComponent(table);
        Button button = new Button("Test");
        button.addListener(new ClickListener() {
            public void buttonClick(ClickEvent event) {
                BeanContainer container = (BeanContainer) table.getContainerDataSource();
                BeanItem item = container.getItem(1);                
                item.getItemProperty("a").setValue(getRandomString(6));
            }
        });        
        mainWindow.addComponent(button);
        setMainWindow(mainWindow);
    }   
    
    private String getRandomString(int length) {
    	if(length > 0) {
    		String s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_-";
    		String o = "";
    		for(int i=0; i<length; i++) {
    			o = o + "" + String.valueOf(s.charAt((int) Math.floor(Math.random()*s.length())));
    		}    		
    		return o;
    	}
    	return null;
    }
}

I think the difference in big “A” and small “a”. For example, getName() translates into the property “name” and not “Name”. getA() seems to tranlaste into “a”. Then there is the special case with words with only upper case letters like getURL which, if I recall correctly, turns into the property “URL”.

These rules are pitfalls when you start using beanitems but you learn them quite fast.

Note that this is not specific to Vaadin BeanItem, but rather conforms to the
JavaBeans specification
- section 8.8 describes the standard capitalization rules.

Thanks for pointing out my silly mistake…you’re right, now that I’ve been shown this pitfall, I am becoming more comfortable with the BeanItem APIs and I am picking it up quite fast…and it served as a reminder to not be so sloppy about my bean spec integration implementation.

Thanks for all the feedback!