how to export to csv/excel using vaadin grids?

泪湿孤枕 提交于 2020-04-11 06:45:34

问题


In Vaadin 14+, I'm creating grids and would like the users to have a stable/easy way to export the grid's contents to csv or, preferablly, Excel. To do so, I was surprised that Vaadin does not appear to provide this functionality, and so one has to use 3rd party developer plugins (such as https://vaadin.com/directory/component/exporter/overview). However, these plugins have numerous bugs (eg can't export grids with date values correctly to Excel etc.). Is there a recommended approach in Vaadin 14 to supporting what I would assume is a highly requested feature of any web-based grid widget?


回答1:


No need for plugins (called add-ons in Vaadin).

DataProvider

You need to understand that a Grid widget is for presentation, not data storage.

Each Grid object is backed by a DataProvider, which is responsible for accessing the data store. The data to be displayed in a Grid might come from some objects in memory, or from a data feed, or from the results of a database query, or from some other source. Following the design principle of separation of concerns, the Grid class is concerned only with displaying data, not managing data access. The DataProvider interface is concerned with managing data access, not displaying data. So Grid and DataProvider work together.

For a limited number of data objects based all in memory, we can use a ListDataProvider implementation of DataProvider. This list data provider can be built automatically for us when we pass a collection of our data objects.

So you do not export data from the Grid object. Instead, you want to listen for changes to the DataProvider, and then offer exporting data obtained through that data provider.

There is no built-in export feature in DataProvider. You can write your own export feature while drawing upon the data made available in the DataProvider implementation. You can choose between many Java-based libraries to assist in writing data files for your exported data. In the code shown below, we use the Apache Commons CSV library for writing tab-delimited or comma-separated values.

Here is a complete example app.

We have a simple Person class to hold a name and a phone number.

package work.basil.example;

import java.util.Objects;

public class Person
{
    //---------------|  Member vars  |--------------------------------
    private String name, phone;


    //---------------|  Constructors  |--------------------------------

    public Person ( String name , String phone )
    {
        this.name = name;
        this.phone = phone;
    }


    //---------------|  Accessors  |--------------------------------

    public String getName ( ) { return this.name; }

    public void setName ( String name ) { this.name = name; }

    public String getPhone ( ) { return this.phone; }

    public void setPhone ( String phone ) { this.phone = phone; }


    //---------------|  Object  |--------------------------------


    @Override
    public boolean equals ( Object o )
    {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        Person person = ( Person ) o;
        return getName().equals( person.getName() );
    }

    @Override
    public int hashCode ( )
    {
        return Objects.hash( getName() );
    }
}

This is an entire Vaadin 14.1.18 app that generates 4 Person objects as a sample data set. Those objects are fed to a Grid, which produces a ListDataProvider for our convenience.

We have a text field for editing the phone number of the selected Person object represented in the grid.

And we have an export button which uses the Apache Commons CSV library to write out a CSV file. Notice the key line where we access the data items from the ListDataProvider. First we cast the data provider to ListDataProvider, then we extract a Collection of all the Person objects stored within. Java Generics provides type-safety, and empowers the compiler to know that the data provider contains Person objects.

Collection < Person > persons = ( ( ListDataProvider < Person > ) grid.getDataProvider() ).getItems();

Complete Vaadin 14.1 app code follows.

package work.basil.example;

import com.vaadin.flow.component.AbstractField;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridSingleSelectionModel;
import com.vaadin.flow.component.html.Input;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.PWA;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

/**
 * The main view contains a button and a click listener.
 */
@Route ( "" )
//@PWA ( name = "Project Base for Vaadin", shortName = "Project Base" )
@CssImport ( "./styles/shared-styles.css" )
@CssImport ( value = "./styles/vaadin-text-field-styles.css", themeFor = "vaadin-text-field" )
public class MainView extends VerticalLayout
{

    Grid < Person > grid;
    TextField phoneField;
    Button phoneSaveButton, exportButton;

    public MainView ( )
    {
        // Widgets
        List < Person > personList = new ArrayList <>( 4 );
        personList.add( new Person( "Alice" , "555.123.1234" ) );
        personList.add( new Person( "Bob" , "555.688.4787" ) );
        personList.add( new Person( "Carol" , "555.632.2664" ) );
        personList.add( new Person( "David" , "555.543.2323" ) );

        // Create a grid bound to the list
        grid = new Grid <>();
        grid.setItems( personList );
        grid.addColumn( Person :: getName ).setHeader( "Name" );
        grid.addColumn( Person :: getPhone ).setHeader( "Phone" );
        GridSingleSelectionModel < Person > singleSelect = ( GridSingleSelectionModel < Person > ) grid.getSelectionModel();
        singleSelect.setDeselectAllowed( false );
        singleSelect.addSingleSelectionListener( singleSelectionEvent -> {
                    Optional < Person > personOptional = singleSelectionEvent.getSelectedItem();
                    if ( personOptional.isPresent() )
                    {
                        this.phoneField.setValue( personOptional.get().getPhone() );
                    }
                }
        );

        phoneField = new TextField( "Phone:" );

        phoneSaveButton = new Button( "Update phone on person " );
        phoneSaveButton.addClickListener(
                ( ClickEvent < Button > clickEvent ) -> {
                    Optional < Person > personOptional = ( ( GridSingleSelectionModel < Person > ) grid.getSelectionModel() ).getSelectedItem();
                    if ( personOptional.isEmpty() )
                    {
                        Notification.show( "First, select a person in list." );
                    } else
                    {
                        Person person = personOptional.get();
                        person.setPhone( phoneField.getValue() );
                        grid.getDataProvider().refreshItem( person );
                    }
                }
        );

        exportButton = new Button( "Export" );
        exportButton.setEnabled( false );
        exportButton.addClickListener(
                ( ClickEvent < Button > clickEvent ) -> {
                    String fileName = "Persons_" + Instant.now().toString() + ".csv";
                    final String fileNamePath = "/Users/basilbourque/" + fileName;
                    try (
                            BufferedWriter writer = Files.newBufferedWriter( Paths.get( fileNamePath ) ) ;
                            CSVPrinter csvPrinter = new CSVPrinter( writer , CSVFormat.RFC4180.withHeader( "Name" , "Phone" ) ) ;
                    )
                    {
                        Collection < Person > persons = ( ( ListDataProvider < Person > ) grid.getDataProvider() ).getItems();
                        for ( Person person : persons )
                        {
                            csvPrinter.printRecord( person.getName() , person.getPhone() );
                        }
                    }
                    catch ( IOException e )
                    {
                        e.printStackTrace();
                    }

                    // Tell user.
                    Notification.show( "Exported to file in your home folder: " + fileName );
                }
        );
        grid.getDataProvider().addDataProviderListener( dataChangeEvent -> {
            exportButton.setEnabled( true );
        } );


        // Arrange
        this.add( grid , phoneField , phoneSaveButton , exportButton );
    }
}

By the way, Apache Commons CSV offers several varieties of file formats. Usually, the best would be the standard format defined in RFC 4180. But you mentioned Microsoft Excel, for which the library supports that variant. See the CSVFormat class.



来源:https://stackoverflow.com/questions/60581348/how-to-export-to-csv-excel-using-vaadin-grids

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!