Last active
June 5, 2023 05:34
-
-
Save TatuLund/0a0e7bdc1d182394c99fe2adfa3e7c6e to your computer and use it in GitHub Desktop.
Grid CSV export with Download button in a Dialog. You need have Anchor attached to view in order to not detach it while Dialog is closed by Button click.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.vaadin.tatu; | |
/* | |
* Dependencies to pom.xml | |
* | |
* <dependency> | |
* <groupId>org.vaadin.artur.exampledata</groupId> | |
* <artifactId>exampledata</artifactId> | |
* <version>1.1.0</version> | |
* </dependency> | |
* <dependency> | |
* <groupId>com.opencsv</groupId> | |
* <artifactId>opencsv</artifactId> | |
* <version>5.4</version> | |
* </dependency> | |
*/ | |
import com.opencsv.bean.StatefulBeanToCsv; | |
import com.opencsv.bean.StatefulBeanToCsvBuilder; | |
import com.opencsv.exceptions.CsvDataTypeMismatchException; | |
import com.opencsv.exceptions.CsvRequiredFieldEmptyException; | |
import com.vaadin.flow.component.button.Button; | |
import com.vaadin.flow.component.dialog.Dialog; | |
import com.vaadin.flow.component.grid.Grid; | |
import com.vaadin.flow.component.grid.Grid.SelectionMode; | |
import com.vaadin.flow.component.grid.GridSortOrder; | |
import com.vaadin.flow.component.html.Anchor; | |
import com.vaadin.flow.component.html.Span; | |
import com.vaadin.flow.component.orderedlayout.VerticalLayout; | |
import com.vaadin.flow.data.provider.CallbackDataProvider; | |
import com.vaadin.flow.data.provider.DataProvider; | |
import com.vaadin.flow.data.provider.Query; | |
import com.vaadin.flow.data.provider.QuerySortOrder; | |
import com.vaadin.flow.function.SerializableComparator; | |
import com.vaadin.flow.router.Route; | |
import com.vaadin.flow.server.StreamResource; | |
import com.vaadin.flow.server.VaadinSession; | |
import java.io.ByteArrayInputStream; | |
import java.io.StringWriter; | |
import java.time.LocalDate; | |
import java.util.Collection; | |
import java.util.List; | |
import java.util.Set; | |
import java.util.function.BinaryOperator; | |
import java.util.stream.Collectors; | |
import java.util.stream.Stream; | |
import org.vaadin.artur.exampledata.DataType; | |
import org.vaadin.artur.exampledata.ExampleDataGenerator; | |
@Route("grid-csv-export") | |
public class GridCsvExportDialog extends VerticalLayout { | |
public static class Person { | |
private String firstName, lastName; | |
private LocalDate birthDate; | |
public LocalDate getBirthDate() { | |
return birthDate; | |
} | |
public void setBirthDate(LocalDate birthDate) { | |
this.birthDate = birthDate; | |
} | |
public String getFirstName() { | |
return firstName; | |
} | |
public void setFirstName(String firstName) { | |
this.firstName = firstName; | |
} | |
public String getLastName() { | |
return lastName; | |
} | |
public void setLastName(String lastName) { | |
this.lastName = lastName; | |
} | |
} | |
private Collection<Person> createExamplePersons(int count) { | |
ExampleDataGenerator<Person> generator = new ExampleDataGenerator<>( | |
Person.class, 123); | |
generator.setData(Person::setFirstName, DataType.FIRST_NAME); | |
generator.setData(Person::setLastName, DataType.LAST_NAME); | |
generator.setData(Person::setBirthDate, DataType.DATE_OF_BIRTH); | |
return generator.create(count); | |
} | |
Collection<Person> persons = createExamplePersons(5000); | |
private Anchor anchor; | |
public GridCsvExportDialog() { | |
// Setup a grid with random data | |
Grid<Person> grid = new Grid<>(Person.class); | |
System.out.println("Push id: "+VaadinSession.getCurrent().getPushId()); | |
grid.setItems(createExamplePersons(1000)); | |
CallbackDataProvider<Person, Void> dataProvider = DataProvider.fromCallbacks(query -> fetchPersons(query.getOffset(), query.getLimit()), query -> 5000); | |
grid.setItems(dataProvider); | |
grid.setSelectionMode(SelectionMode.MULTI); | |
add(grid); | |
Button exportButton = new Button("Export as CSV", e -> { | |
this.export(grid); | |
}); | |
anchor = new Anchor(); | |
anchor.setText("Download"); | |
anchor.getElement().getStyle().set("display", "none"); | |
anchor.getElement().setAttribute("download", true); | |
add(exportButton,anchor); | |
} | |
private void export(Grid<Person> grid) { | |
// Fetch all data from the grid in the current sorted order | |
Dialog dialog = new Dialog(); | |
Stream<Person> persons = null; | |
Set<Person> selection = grid.asMultiSelect().getValue(); | |
if (selection != null && selection.size() > 0) { | |
persons = selection.stream(); | |
} else { | |
// Alternative approach without DataView | |
persons = ((DataProvider<Person, String>) grid.getDataProvider()) | |
.fetch(createQuery(grid)); | |
} | |
StringWriter output = new StringWriter(); | |
StatefulBeanToCsv<Person> writer = new StatefulBeanToCsvBuilder<Person>( | |
output).build(); | |
try { | |
writer.write(persons); | |
StreamResource resource = new StreamResource("export.csv", | |
() -> new ByteArrayInputStream(output.toString().getBytes())); | |
anchor.setHref(resource); | |
Button button = new Button("Download"); | |
button.addClickListener(event -> { | |
anchor.getElement().callJsFunction("click"); | |
dialog.close(); | |
}); | |
dialog.setHeaderTitle("Success"); | |
dialog.add(button); | |
} catch (CsvDataTypeMismatchException | |
| CsvRequiredFieldEmptyException e) { | |
Span span = new Span("An error occured during writing: " + e.getMessage()); | |
Button button = new Button("Close"); | |
button.addClickListener(event -> { | |
dialog.close(); | |
}); | |
dialog.setHeaderTitle("Error"); | |
dialog.add(span, button); | |
} | |
dialog.open(); | |
} | |
/* | |
* This method is needed if using Vaadin 14, which does not have DataView | |
* API yet | |
*/ | |
private Query<Person, String> createQuery(Grid<Person> grid) { | |
List<GridSortOrder<Person>> gridSort = grid.getSortOrder(); | |
List<QuerySortOrder> sortOrder = gridSort.stream().map( | |
order -> order.getSorted().getSortOrder(order.getDirection())) | |
.flatMap(orders -> orders).collect(Collectors.toList()); | |
BinaryOperator<SerializableComparator<Person>> operator = (comparator1, | |
comparator2) -> { | |
return comparator1.thenComparing(comparator2)::compare; | |
}; | |
SerializableComparator<Person> inMemorySorter = gridSort.stream().map( | |
order -> order.getSorted().getComparator(order.getDirection())) | |
.reduce(operator).orElse(null); | |
return new Query<Person, String>(0, Integer.MAX_VALUE, sortOrder, | |
inMemorySorter, null); | |
} | |
public Stream<Person> fetchPersons(int offset, int limit) { | |
final Stream<Person> filtered = persons.stream().skip(offset).limit(limit); | |
try { | |
// Simulated delay of the back end call | |
Thread.sleep(200); | |
} catch (Exception e) { | |
} | |
return filtered; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment