Skip to content

Instantly share code, notes, and snippets.

@TatuLund
Last active June 5, 2023 05:34
Show Gist options
  • Save TatuLund/0a0e7bdc1d182394c99fe2adfa3e7c6e to your computer and use it in GitHub Desktop.
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.
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