Binding Data to Grid Flow
Grid supports connecting to various types of data sources through data providers. A data provider is a class that implements the DataProvider interface and encapsulates the logic for fetching items from a specific data source, with support for pagination, sorting, and filtering. For common use cases, Grid provides convenience methods so that you don’t have to deal with the full data provider setup, and it also supports custom data providers where more flexibility is needed.
Binding Items via setItems
The most straightforward way to bind data to a Grid is to use the setItems convenience method. It allows you to bind an in-memory collection or provide callbacks for lazy loading from a backend service without the need to explicitly create a data provider.
The example below shows how to display, filter and sort a static list of Person records:
Source code
PeopleView.java
package com.vaadin.demo.component.grid.databinding;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.dataview.GridListDataView;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.select.Select;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.data.value.ValueChangeMode;
public class BindingInMemoryList extends VerticalLayout {
public BindingInMemoryList() {
Grid<Person> grid = new Grid<>(Person.class);
// Binding
GridListDataView<Person> dataView = grid.setItems(
new Person("Michael Chen", "Engineering"),
new Person("Sarah Johnson", "Engineering"),
new Person("David Rodriguez", "Marketing"),
new Person("Emma Wilson", "HR"));
// Filtering
TextField searchInput = new TextField("Search", event -> {
if (!event.getValue().isEmpty()) {
// Set a filter to show only people whose name contains the search term
dataView.setFilter(person -> person
.name().toLowerCase().contains(event.getValue().toLowerCase()));
} else {
// Clear the filter if the search term is empty
dataView.removeFilters();
}
});
searchInput.setValueChangeMode(ValueChangeMode.EAGER);
// Sorting
Select<String> sortBySelect = new Select<>("Sort by", event -> {
switch (event.getValue()) {
case "Name (A-Z)" ->
// Set sort order to ascending by name
dataView.setSortOrder(Person::name, SortDirection.ASCENDING);
case "Name (Z-A)" ->
// Set sort order to descending by name
dataView.setSortOrder(Person::name, SortDirection.DESCENDING);
default ->
// Clear sorting
dataView.removeSorting();
}
});
sortBySelect.setItems("", "Name (A-Z)", "Name (Z-A)");
add(searchInput, sortBySelect, grid);
}
}
Person.java
package com.vaadin.demo.component.grid.databinding;
public record Person(String name, String department) {
}
The next example shows how to display, filter and sort data from a backend service with lazy loading by providing fetch and count callbacks to the setItems method:
Source code
PeopleView.java
package com.vaadin.demo.component.grid.databinding;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.select.Select;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
public class BindingLazyLoadedList extends VerticalLayout {
public BindingLazyLoadedList() {
Grid<Person> grid = new Grid<>(Person.class);
// Filtering
TextField searchInput = new TextField("Search", event -> {
grid.getLazyDataView().refreshAll();
});
searchInput.setValueChangeMode(ValueChangeMode.EAGER);
// Sorting
Select<String> sortBySelect = new Select<>("Sort by", event -> {
grid.getLazyDataView().refreshAll();
});
sortBySelect.setItems("", "Name (A-Z)", "Name (Z-A)");
// Binding
grid.setItems(
(query) -> PersonService.fetch(
searchInput.getValue(),
sortBySelect.getValue(),
query.getOffset(),
query.getLimit()),
(query) -> PersonService.count(searchInput.getValue()));
add(searchInput, sortBySelect, grid);
}
}
PersonService.java
package com.vaadin.demo.component.grid.databinding;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class PersonService {
private static final List<Person> PEOPLE = Arrays.asList(
new Person("Michael Chen", "Engineering"),
new Person("Sarah Johnson", "Engineering"),
new Person("David Rodriguez", "Marketing"),
new Person("Emma Wilson", "HR"));
public static Stream<Person> fetch(
String searchTerm, String sortOrder, int offset, int limit) {
return PEOPLE.stream()
.filter(createPredicate(searchTerm))
.sorted(createComparator(sortOrder))
.skip(offset)
.limit(limit);
}
public static int count(String searchTerm) {
return (int) PEOPLE.stream()
.filter(createPredicate(searchTerm))
.count();
}
private static Predicate<Person> createPredicate(String searchTerm) {
return person -> person.name().toLowerCase()
.contains(searchTerm != null ? searchTerm.toLowerCase() : "");
}
private static Comparator<Person> createComparator(String sortOrder) {
return switch (sortOrder) {
case "Name (A-Z)" -> Comparator.comparing(Person::name);
case "Name (Z-A)" -> Comparator.comparing(Person::name).reversed();
default -> (p1, p2) -> 0;
};
}
}
Person.java
package com.vaadin.demo.component.grid.databinding;
public record Person(String name, String department) {
}
More documentation and examples for using the setItems method are available in the Binding Items To Components article.
Custom Data Providers
A more advanced way to bind data to a Grid is to supply a custom data provider implementation via the setDataProvider(DataProvider<T, F>) method.
The example below shows a Grid backed by a custom data provider that imitates fetching data from a database, with support for filtering and sorting:
Source code
PersonDataProvider.java
package com.vaadin.demo.component.grid.databinding;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.vaadin.flow.data.provider.AbstractBackEndDataProvider;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.data.provider.QuerySortOrder;
import com.vaadin.flow.data.provider.SortDirection;
public class PersonDataProvider extends AbstractBackEndDataProvider<Person, PersonFilter> {
// In a real application, data would come from a real database or backend
// service. This example uses a static list for demonstration purposes only.
private static final List<Person> DATABASE = Arrays.asList(
new Person("Michael Chen", "Engineering"),
new Person("Sarah Johnson", "Engineering"),
new Person("David Rodriguez", "Marketing"),
new Person("Emma Wilson", "HR"));
@Override
protected Stream<Person> fetchFromBackEnd(Query<Person, PersonFilter> query) {
// SQL equivalent: SELECT ... FROM ...
Stream<Person> stream = DATABASE.stream();
// SQL equivalent: WHERE ...
stream = stream.filter(createPredicate(query.getFilter()));
// SQL equivalent: ORDER BY ...
stream = stream.sorted(createComparator(query.getSortOrders()));
// SQL equivalent: OFFSET ... LIMIT ...
stream = stream.skip(query.getOffset()).limit(query.getLimit());
return stream;
}
@Override
protected int sizeInBackEnd(Query<Person, PersonFilter> query) {
// SQL equivalent: SELECT COUNT(*) FROM ...
Stream<Person> stream = DATABASE.stream();
// SQL equivalent: WHERE ...
stream = stream.filter(createPredicate(query.getFilter()));
return (int) stream.count();
}
/**
* Creates a filter function (predicate) based on the filter object from the
* Grid.
*
* This is for demonstration purposes only. In a real application, you should do
* filtering in the database query e.g. by using SQL WHERE if you are using a
* relational database.
*/
private Predicate<Person> createPredicate(Optional<PersonFilter> filter) {
return filter.map(f -> (Predicate<Person>) f::test).orElse(p -> true);
}
/**
* Creates a sorting function (comparator) based on the sort orders from the
* Grid.
*
* This is for demonstration purposes only. In a real application, you should do
* sorting in the database query e.g. by using SQL ORDER BY if you are using a
* relational database.
*/
private Comparator<Person> createComparator(List<QuerySortOrder> sortOrders) {
return sortOrders.stream().map(sortOrder -> {
Comparator<Person> comparator = switch (sortOrder.getSorted()) {
case "name" -> Comparator.comparing(Person::name);
case "department" -> Comparator.comparing(Person::department);
default -> (p0, p1) -> 0;
};
return sortOrder.getDirection().equals(SortDirection.ASCENDING)
? comparator
: comparator.reversed();
}).reduce((p0, p1) -> 0, Comparator::thenComparing);
}
}
PersonFilter.java
package com.vaadin.demo.component.grid.databinding;
public record PersonFilter(String nameFilter, String departmentFilter) {
public boolean test(Person person) {
boolean matches = true;
if (nameFilter != null && !nameFilter.isEmpty()) {
matches &= person.name().toLowerCase().contains(nameFilter.toLowerCase());
}
if (departmentFilter != null && !departmentFilter.isEmpty()) {
matches &= person.department().equals(departmentFilter);
}
return matches;
}
}
Person.java
package com.vaadin.demo.component.grid.databinding;
public record Person(String name, String department) {
}
PeopleView.java
package com.vaadin.demo.component.grid.databinding;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.select.Select;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.provider.ConfigurableFilterDataProvider;
import com.vaadin.flow.data.value.ValueChangeMode;
public class BindingDataProvider extends VerticalLayout {
private TextField nameFilter;
private Select<String> departmentFilter;
public BindingDataProvider() {
Grid<Person> grid = new Grid<>(Person.class);
// Enable sorting for the columns. When the user sorts the columns,
// the updated sort orders are passed to the data provider and are
// available through Query#getSortOrders().
grid.setSortableColumns("name", "department");
// Create a data provider instance with a configurable filter allowing
// the filter value to be set programmatically via setFilter(). The filter
// value is passed to the data provider on change and is available through
// Query#getFilter().
ConfigurableFilterDataProvider<Person, Void, PersonFilter> dataProvider = new PersonDataProvider()
.withConfigurableFilter();
grid.setDataProvider(dataProvider);
// Create filter components
nameFilter = new TextField("Search by name", event -> {
dataProvider.setFilter(createFilterObject());
});
nameFilter.setValueChangeMode(ValueChangeMode.EAGER);
departmentFilter = new Select<>("Department", event -> {
dataProvider.setFilter(createFilterObject());
});
departmentFilter.setItems("", "Engineering", "Marketing", "HR");
add(nameFilter, departmentFilter, grid);
}
private PersonFilter createFilterObject() {
return new PersonFilter(nameFilter.getValue(), departmentFilter.getValue());
}
}
More examples of custom data providers are available in the Binding Items To Components article.