Update Grid with a fresh set of data, in Vaadin 7.4 app

Basil Bourque picture Basil Bourque · Mar 23, 2015 · Viewed 11.5k times · Source

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.

Answer

Basil Bourque picture Basil Bourque · Mar 23, 2015

I considered two approaches:

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.

screen shot of a pair of Grid instances

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 + " }";
    }

}