In the new Vaadin 7.4 release, the new Grid
widget debuted as an alternative to the venerable Table
.
After getting a Grid displayed, I later want to replace the entire set of data with fresh data. Rather than update the individual rows, I want to simply replace them.
I happen to be using a BeanItemContainer
for easy read-only display of some objects with JavaBeans-style getter methods.
I considered two approaches:
- Two step process of replacing bean items.
- (1) First remove all
BeanItem
objects with Container::removeAllItems
method.
- (2) Then add replacement
BeanItem
objects with the BeanItemContainer::addAll
method.
- Replace entire
BeanItemContainer
.
- Call
Grid::setContainerDataSource
and pass a new instance of BeanItemContainer
constructed with fresh data.
Below is a sample application (Vaadin 7.4.2) showing both approaches. A pair of identical Grid widgets appear. Each has a button that updates data with either approach.
Results
The first approach (removing items and adding items) works. The fresh data immediately appears.
The second approach (replacing container rather than items) seems like it should work, with nothing contrary suggested in the scant documentation. But nothing happens. No exceptions or errors occur, yet no fresh data appears. I opened Ticket # 17268 on Vaadin trac for this issue.
Perhaps there are other better ways. Please post or comment with any alternatives.
Example App
Three classes are displayed below. You should be able to copy-paste into a new Vaadin 7.4.x app.
- One class is the usual "MyUI" created in every new Vaadin app.
- Another is simple JavaBeans-style class, "Astronomer", providing data for the rows in our Grid. That Astronomer class includes a convenient static method for generating a List of instances. Each new Astronomer gets a random number of popularity votes, to show fresh data values.
- The meaty part of the example is in the "AstronomersLayout" class which creates the pair of Grids with their assigned buttons.
I use Java 8 Lambda syntax and the new java.time classes. So you may need to change your project's settings to use Java 8. In NetBeans 8 that means Project > Properties > Sources > Source/Binary Format (popup menu) > 1.8
.
MyUI.java
Get your Vaadin app going.
package com.example.vaadingridexample;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.annotations.Widgetset;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.UI;
/**
* Example app in Vaadin 7.4.2 experimenting with two ways to replace data in a
* displayed Grid.
*
* @author Basil Bourque
*/
@Theme ( "mytheme" )
@Widgetset ( "com.example.vaadingridexample.MyAppWidgetset" )
public class MyUI extends UI
{
@Override
protected void init ( VaadinRequest vaadinRequest )
{
this.setContent( new AstronomersLayout() );
}
@WebServlet ( urlPatterns = "/*" , name = "MyUIServlet" , asyncSupported = true )
@VaadinServletConfiguration ( ui = MyUI.class , productionMode = false )
public static class MyUIServlet extends VaadinServlet
{
}
}
AstronomersLayout.java
The main part of the example.
package com.example.vaadingridexample;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.shared.ui.grid.HeightMode;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Grid;
import com.vaadin.ui.VerticalLayout;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* Layout displays a pair of Grids, each with a Button to replace its contents
* with fresh data in either of two ways: (a) Replace all the items within the
* Container, or (b) Replace container itself.
*
* @author Basil Bourque
*/
@SuppressWarnings ( "serial" )
public class AstronomersLayout extends VerticalLayout
{
// -----| Member vars |--------------------------
Grid grid_ReplaceItems;
String gridCaption_ReplaceItems = "Astronomers - Replacing Items";
Button button_ReplaceItems;
Grid grid_ReplaceContainer;
String gridCaption_ReplaceContainer = "Astronomers - Replacing Container";
Button button_ReplaceContainer;
// -----| Constructor |--------------------------
public AstronomersLayout ()
{
this.prepareWidgets();
this.composeLayout();
}
// -----| Helper Methods |--------------------------
private void prepareWidgets ()
{
// Show updating a Grid by replacing the bean items within a container.
// Grid
List<Astronomer> listA = Astronomer.makeList();
BeanItemContainer<Astronomer> containerA = new BeanItemContainer<>( Astronomer.class , listA );
this.grid_ReplaceItems = new Grid( this.gridCaption_ReplaceItems , containerA );
//this.grid_ReplaceItems.setColumnOrder( "votes" , "givenName" , "surName" , "birthYear" );
this.grid_ReplaceItems.setColumnOrder( Astronomer.FIELD.VOTES.getName() , Astronomer.FIELD.GIVENNAME.getName() , Astronomer.FIELD.SURNAME.getName() , Astronomer.FIELD.BIRTHYEAR.getName() ); // Enum is a safer way of doing this: this.grid_ReplaceItems.setColumnOrder( "votes" , "givenName" , "surName" , "birthYear" );
this.grid_ReplaceItems.setHeightMode( HeightMode.ROW ); // Show all rows of data for this grid.
this.updateCaptionAndSize( this.grid_ReplaceItems , this.gridCaption_ReplaceItems );
// Button
this.button_ReplaceItems = new Button( "Replace Items" );
this.button_ReplaceItems.addClickListener( ( ClickEvent event ) -> {
@SuppressWarnings ( "unchecked" )
BeanItemContainer<Astronomer> bic = ( BeanItemContainer<Astronomer> ) this.grid_ReplaceItems.getContainerDataSource(); // Access existing container. Cast as need be.
bic.removeAllItems(); // Remove existing items.
bic.addAll( Astronomer.makeList() ); // Add fresh bean items to existing container.
this.updateCaptionAndSize( this.grid_ReplaceItems , this.gridCaption_ReplaceItems );
} );
// Show updating a Grid by replacing the container rather than its contents.
// Grid
List<Astronomer> listB = Astronomer.makeList();
BeanItemContainer<Astronomer> containerB = new BeanItemContainer<>( Astronomer.class , listB );
this.grid_ReplaceContainer = new Grid( this.gridCaption_ReplaceContainer , containerB );
this.grid_ReplaceContainer.setColumnOrder( Astronomer.FIELD.VOTES.getName() , Astronomer.FIELD.GIVENNAME.getName() , Astronomer.FIELD.SURNAME.getName() , Astronomer.FIELD.BIRTHYEAR.getName() );
this.grid_ReplaceContainer.setHeightMode( HeightMode.ROW ); // Show all rows of data for this grid.
this.updateCaptionAndSize( this.grid_ReplaceContainer , this.gridCaption_ReplaceContainer );
// Button
this.button_ReplaceContainer = new Button( "Replace Container" );
this.button_ReplaceContainer.addClickListener( ( ClickEvent event ) -> {
@SuppressWarnings ( "unchecked" )
BeanItemContainer<Astronomer> bic = new BeanItemContainer<>( Astronomer.class , listB ); // Create replacement container.
this.grid_ReplaceContainer.setContainerDataSource( bic );
this.updateCaptionAndSize( this.grid_ReplaceContainer , this.gridCaption_ReplaceContainer );
} );
}
private void updateCaptionAndSize ( final Grid grid , final String caption )
{
// Caption
grid.setCaption( caption + " ( updated " + this.now() + " )" ); // Update caption of Grid to indicate fresh data.
// Show all rows.
double h = grid.getContainerDataSource().size() > 0 ? grid.getContainerDataSource().size() : 3; // Cannot set height to zero rows. So if no data, set height to some arbitrary number of (empty) rows.
grid.setHeightByRows( h );
}
private void composeLayout ()
{
// Initialize this layout.
this.setMargin( true );
this.setSpacing( true );
// Content
this.addComponent( this.button_ReplaceItems );
this.addComponent( this.grid_ReplaceItems );
this.addComponent( this.button_ReplaceContainer );
this.addComponent( this.grid_ReplaceContainer );
}
// Helper method.
private String now ()
{
// Get current time in UTC. Truncate fractional seconds. Append a 'Z' to indicate UTC time zone.
return ZonedDateTime.now( ZoneOffset.UTC ).format( DateTimeFormatter.ISO_LOCAL_TIME ).substring( 0 , 8 ).concat( "Z" );
}
}
Astronomer.java
The data, the bean items, stored in a BeanItemContainer for display in a Grid.
A nested Enum provides a safer way to refer to the field names in the other class, AstronomersLayout
for call to setColumnOrder
.
package com.example.vaadingridexample;
import java.util.ArrayList;
import java.util.List;
/**
* Provides the beans to appear as rows in a BeanItemContainer backing a Grid.
*
* Note the static convenience method for generating a List of instances.
*
* @author Basil Bourque
*/
public class Astronomer
{
public enum FIELD
{
SURNAME( "surname" ),
GIVENNAME( "givenName" ),
BIRTHYEAR( "birthYear" ),
VOTES( "votes" );
private String name;
private FIELD ( String s )
{
this.name = s;
}
public String getName ()
{
return this.name;
}
}
// Members
private String surname;
private String givenName;
private Integer birthYear;
private Integer votes;
public Astronomer ( final String givenName , final String surName , final Integer birthYear )
{
this.surname = surName;
this.givenName = givenName;
this.birthYear = birthYear;
this.votes = this.random();
}
public static List<Astronomer> makeList ()
{
List<Astronomer> list = new ArrayList<>( 7 );
list.add( new Astronomer( "Hypatia" , "of Alexandria" , -370 ) );
list.add( new Astronomer( "Nicolaus" , "Copernicus" , 1473 ) );
list.add( new Astronomer( "Tycho" , "Brahe" , 1546 ) );
list.add( new Astronomer( "Giordano" , "Bruno" , 1548 ) );
list.add( new Astronomer( "Galileo" , "Galilei" , 1564 ) );
list.add( new Astronomer( "Johannes" , "Kepler" , 1571 ) );
list.add( new Astronomer( "Isaac" , "Newton" , 1643 ) );
list.add( new Astronomer( "Caroline" , "Herschel" , 1750 ) );
return list;
}
// ----| Helper Methods |----------------------------------
private Integer random ()
{
return ( int ) ( java.lang.Math.random() * 100 );
}
// ----| Bean Getters |----------------------------------
public String getSurname ()
{
return this.surname;
}
public String getGivenName ()
{
return this.givenName;
}
public Integer getBirthYear ()
{
return this.birthYear;
}
public Integer getVotes ()
{
return this.votes;
}
// ----| Object Superclass |----------------------------------
@Override
public String toString ()
{
return "Astronomer{ " + "surName=" + surname + " | givenName=" + givenName + " | birthYear=" + birthYear + " | votes=" + votes + " }";
}
}
You can simply get the record you have removed from button through clickListener with .getSelectedRow()
.
After this you can remove your item from your grid with .removeItem()
.
IE:
Grid yourGrid = new Grid();
yourGrid.setContainerDataSource(yourData);
Button removeItem = new Button("Remove item");
removeItem.addClickListener(l -> {
Item selectedItem = (Item) yourGrid.getSelectedRow();
yourGrid.getContainerDataSource().removeItem(selectedItem);
});
Bye!