Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Vaadin 7.0, how to refresh JavaBean data backing a Table? Replace Container? Replace Beans?

Tags:

vaadin

In Vaadin 7.0, when displaying JavaBean data in a Table with a BeanContainer, what is the proper way to refresh the Table with new data?

like image 815
Basil Bourque Avatar asked Apr 20 '13 00:04

Basil Bourque


2 Answers

The table monitors the the Properties of the Items of the table via Listeners. If you change the property of an item via the Item instance of the table, the table will be notified and updated : e.g.

container.getItem(beanInstance).getItemProperty("propertyName").setValue("newValue");

If, however, the bean is changed outside of the item instance, you need to tell the table to refresh. The easiest way (using the stock BeanContainer) is to simply remove then add the items.

Alternatively - and, I suggest, preferably - you could create an extension of BeanItemContainer that fires an ItemSetChange event, which will cause the table to refresh.

public class RefreshableBeanItemContainer<BEANTYPE> extends BeanItemContainer<BEANTYPE> {
  public RefreshableBeanItemContainer(Collection<? extends BEANTYPE> collection) throws IllegalArgumentException {
    super(collection);
  }

  public RefreshableBeanItemContainer(Class<? super BEANTYPE> type) throws IllegalArgumentException {
    super(type);
  }

  public RefreshableBeanItemContainer(Class<? super BEANTYPE> type, Collection<? extends BEANTYPE> collection) throws IllegalArgumentException {
    super(type, collection);
  }

  public void refreshItems(){
    fireItemSetChange();
  }
}
like image 87
Charles Anthony Avatar answered Oct 26 '22 16:10

Charles Anthony


Seems that one way to refresh the JavaBean data is by replacing the Table's Container with another Container. One side-effect I've noticed is that some of the Table's settings may be reset, such as collapsed columns no longer being collapsed.

Another way seems to be keeping the Container while replacing the contained Beans.

I'm not sure if both of these approaches are considered proper. And I'm not sure what the trade-offs may be to either of these approaches.

Here is an example app with a pair of tables and a button. When the user clicks the button, both tables get new data. One Table gets a new BeanContainer. The other Table keeps its BeanContainer, but gets new beans loaded.

Screen shot of Vaadin 7.0.4 app with a pair of Tables and a Button between them.

Just two classes in this app:

  • The main app class.
  • The JavaBean class used for storing data.
package com.example.replacebeansorcontainer;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import com.vaadin.data.util.BeanContainer;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Table;
import com.vaadin.ui.UI;

/**
 * Main UI class.
 * 
 * ReplaceBeansOrContainerUI.java
 * 
 * @author Basil Bourque
 * 
 *         Copyright © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
 * 
 */
@SuppressWarnings( "serial" )
public class ReplaceBeansOrContainerUI extends UI {
    Table tableThatGetsFreshContainer;
    Table tableThatGetsFreshBeans;

    @Override
    protected void init( VaadinRequest request ) {
        // Create widgets: table on the left, button, table on the right.

        this.tableThatGetsFreshContainer = new Table( "Table That Gets Fresh Container", this.makeBeanContainer() );
        this.tweakTable( this.tableThatGetsFreshContainer );

        this.tableThatGetsFreshBeans = new Table( "Table That Gets Fresh Beans", this.makeBeanContainer() );
        this.tweakTable( this.tableThatGetsFreshBeans );

        Button reloadButton = new Button( "Reload Data" );
        reloadButton.addClickListener( new Button.ClickListener() {
            @Override
            public void buttonClick( ClickEvent event ) {
                // Reload data in both tables when user clicks this button.
                System.out.println( "User clicked 'Reload Data' button. Replacing container (left) & beans (right)." + new java.util.Date() );
                // One table gets a new Container.
                ReplaceBeansOrContainerUI.this.tableThatGetsFreshContainer.setContainerDataSource( ReplaceBeansOrContainerUI.this.makeBeanContainer() );
                // The other table keeps its Container, but the Container's beans are replaced.
                ReplaceBeansOrContainerUI.this.tableThatGetsFreshBeans.getContainerDataSource().removeAllItems();
                // Cast the Container to BeanContainer to utilize the 'addAll' method.
                @SuppressWarnings( "unchecked" )
                BeanContainer<UUID, MomentBean> beanContainer = (BeanContainer<UUID, MomentBean>)ReplaceBeansOrContainerUI.this.tableThatGetsFreshBeans.getContainerDataSource();
                beanContainer.addAll( ReplaceBeansOrContainerUI.this.makeListOfBeans() );
            }
        } );

        // Compose Layout.
        final HorizontalLayout layout = new HorizontalLayout();
        layout.setMargin( true );
        layout.setSpacing( true );
        layout.addComponent( this.tableThatGetsFreshContainer ); // Table on the left.
        layout.addComponent( reloadButton );
        layout.addComponent( this.tableThatGetsFreshBeans ); // Table on the right.

        // Compose UI.
        this.setContent( layout );
    }

    private void tweakTable( Table table ) {
        table.setSelectable( true );
    }

    private List<MomentBean> makeListOfBeans() {
        List<MomentBean> beans = new ArrayList<MomentBean>();
        for ( int i = 0; i < 20; i++ ) {
            beans.add( new MomentBean() );
        }
        return beans;
    }

    private BeanContainer<UUID, MomentBean> makeBeanContainer() {
        // Instantiate empty container, with columns defined by my class’ JavaBean fields.
        BeanContainer<UUID, MomentBean> container = new BeanContainer<UUID, MomentBean>( MomentBean.class );
        try {
            // Indicate which field in Bean serves as the unique identifier.
            container.setBeanIdProperty( MomentBean.class.getDeclaredField( "uuid" ).getName() );
            container.addAll( this.makeListOfBeans() ); // Add entire Collection of beans to container.
        } catch ( NoSuchFieldException e ) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch ( SecurityException e ) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return container;
    }
}

The JavaBean class…

/**
 * 
 */
package com.example.replacebeansorcontainer;

import java.text.SimpleDateFormat;
import java.util.UUID;

/**
 * MomentBean.java
 * 
 * @author Basil Bourque
 * 
 *         Copyright © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
 * 
 */
public class MomentBean {
    // Bean fields.
    private final String clockTime;
    private final long nanoTime;
    private final UUID uuid;

    // Other member fields.
    private final SimpleDateFormat timeOnlyFormat = new SimpleDateFormat( "HH:mm:ss" );

    /**
     * 
     */
    public MomentBean() {
        this.clockTime = this.timeOnlyFormat.format( new java.util.Date() );
        this.nanoTime = System.nanoTime();
        this.uuid = UUID.randomUUID();
    }

    /**
     * @return the clockTime
     */
    public String getClockTime() {
        return this.clockTime;
    }

    /**
     * @return the nanoTime
     */
    public long getNanoTime() {
        return this.nanoTime;
    }

    /**
     * @return the uuid
     */
    public UUID getUuid() {
        return this.uuid;
    }

}
like image 22
Basil Bourque Avatar answered Oct 26 '22 14:10

Basil Bourque