Grid
The data area can be scrolled both vertically and horizontally. The leftmost columns can be frozen, so that they are never scrolled out of the view. The data is loaded lazily from the server, so that only the visible data is loaded. The smart lazy loading functionality gives excellent user experience even with low bandwidth, such as mobile devices.
Binding to Data
Grid
is normally used by binding it to a data provider, described in . By default, it is bound to List of items. You can set the items with the setItems()
method.
For example, if you have a list of beans, you show them in a Grid
as follows
Java
Selection in Grid
is handled a bit differently from other selection components, as it is not a HasValue
. Grid supports single, multiple, or no-selection, each defined by a specific selection model. Each selection model has a specific API depending on the type of the selection.
For basic usage, switching between the built-in selection models is possible by using the setSelectionMode(SelectionMode)
. Possible options are SINGLE
(default), MULTI
, or NONE
.
Listening to selection changes in any selection model is possible with a SelectionListener
, which provides a generic SelectionEvent
for getting the selected value or values. Note that the listener is actually attached to the selection model and not the grid, and will stop getting any events if the selection mode is changed.
Java
Grid<Person> grid = new Grid<>();
// switch to multiselect mode
grid.setSelectionMode(SelectionMode.MULTI);
grid.addSelectionListener(event -> {
Set<Person> selected = event.getAllSelectedItems();
message.setText(selected.size() + " items selected");
});
Programmatically selecting the value is possible via select(T)
. In multiselect mode, this will add the given item to the selection.
Java
// in single-select, only one item is selected
grid.select(defaultItem);
// switch to multi select, clears selection
grid.setSelectionMode(SelectionMode.MULTI);
// Select items 2-4
people.subList(2,3).forEach(grid::select);
The current selection can be obtained from the Grid
by getSelectedItems(), and the returned `Set
contains either only one item (in single-selection mode) or several items (in multi-selection mode).
Warning | If you change the grid’s items with |
For more control over the selection, you can access the used selection model with getSelectionModel()
. The return type is GridSelectionModel
which has generic selection model API, but you can cast that to the specific selection model type, typically either SingleSelectionModel
or MultiSelectionModel
.
The selection model is also returned by the setSelectionMode(SelectionMode)
method.
Java
// the default selection model
GridSingleSelectionModel<Person> defaultModel = (GridSingleSelectionModel<Person>) grid
.getSelectionModel();
// Use multi-selection mode
GridMultiSelectionModel<Person> selectionModel = (GridMultiSelectionModel<Person>) grid
.setSelectionMode(SelectionMode.MULTI);
Single Selection Model
By obtaining a reference to the SingleSelectionModel
, you can access more fine grained API for the single-select case.
The addSingleSelect(SingleSelectionListener)
method provides access to SingleSelectionEvent
, which has some extra API for more convenience.
In single-select mode, it is possible to control whether the empty (null) selection is allowed. By default it is enabled, but can be disabled with setDeselectAllowed()
.
Java
// preselect value
grid.select(defaultItem);
GridSingleSelectionModel<Person> singleSelect = (GridSingleSelectionModel<Person>) grid
.getSelectionModel();
// disallow empty selection
singleSelect.setDeselectAllowed(false);
Multi-Selection Model
In the multi-selection mode, a user can select multiple items by clicking on the checkboxes in the leftmost column.
By obtaining a reference to the MultiSelectionModel
, you can access more fine grained API for the multi-select case.
The MultiSelectionModel
provides addMultiSelectionListener(MultiSelectionListener)
access to MultiSelectionEvent
, which allows to easily access differences in the selection change.
Java
// Grid in multi-selection mode
Grid<Person> grid = new Grid<>();
grid.setItems(people);
GridMultiSelectionModel<Person> selectionModel = (GridMultiSelectionModel<Person>) grid
.setSelectionMode(SelectionMode.MULTI);
selectionModel.selectAll();
selectionModel.addMultiSelectionListener(event -> {
event.getAddedSelection().size(),
event.getRemovedSelection().size()));
// Allow deleting only if there's any selected
deleteSelected.setEnabled(event.getNewSelection().isEmpty());
});
Configuring Columns
The addColumn()
method can be used to add columns to Grid
.
Column configuration is defined in Grid.Column
objects, which are returned by addColumn
.
The setter methods in Column
have fluent API, so you can easily chain the configuration calls for columns if you want to.
Java
Column<Person> nameColumn = grid.addColumn(Person::getName)
.setHeader("Name")
.setFlexGrow(0)
.setWidth("100px")
.setResizable(false);
In the following, we describe the basic column configuration.
Column Headers and Footers
By default, no header or footer is present for a column. These must be set explicitly using the methods setHeader
and setFooter
through the API of a column. The methods have two overloads, one which accepts a plain string and one that accepts a TemplateRenderer
. Template renderers are covered later in this tutorial.
Java
// Sets a simple text header
nameColumn.setHeader("Name");
// Sets a header containing a custom template,
// in this case simply bolding the caption "Name"
nameColumn.setHeader("<b>Name</b>");
// Similarly for the footer
nameColumn.setFooter("Name");
nameColumn.setFooter("<b>Name</b>");
Column Order
You can enable drag and drop user reordering of columns with setColumnReorderingAllowed()
.
Java
grid.setColumnReorderingAllowed(true);
Hiding Columns
Columns can be hidden by calling setVisible()
in Column
.
Columns have by default undefined width, which causes automatic sizing based on the widths of the displayed data. You can set column widths relatively using flex grow ratios with setFlexGrow()
, or explicitly by a CSS string value with setWidth()
when flex grow has been set to 0.
When setResizable()
is enabled the user can resize a column by dragging its separator with the mouse.
Frozen Columns
You can set columns to be frozen with the setFrozen()
method in Column
, so that they are not scrolled off when scrolling horizontally. Additionally, user reordering of frozen columns is limited between other frozen columns.
Java
nameColumn.setFrozen(true);
Grouping Columns
Multiple columns can be grouped together by adding them in the header row of the grid. After the HeaderRow
is retrieved via or appendHeaderRow
grid api, join
method can be used to group the columns. Additionally, setText
and setComponent
methods can be used on the join result to set the text or component for the joined columns.
Java
// Create a header row
HeaderRow topRow = grid.prependHeaderRow();
// group two columns under the same label
topRow.join(nameColumn, ageColumn)
.setComponent(new Label("Basic Information"));
// group the other two columns in the same header row
topRow.join(streetColumn, postalCodeColumn)
.setComponent(new Label("Address Information"));
Column Keys
Java
nameColumn.setKey("name");
grid.getColumnByKey("name").setWidth("100px");
Automatically Adding Columns
You can configure Grid to automatically add columns for every property in a bean. To do this, you need to pass the class of the bean type to the Grid’s constructor. The property names are set as the column keys, so you can use them for further configuring the columns.
Java
Grid<Person> grid = new Grid<>(Person.class);
grid.getColumnByKey("yearOfBirth").setFrozen(true);
This constructor adds columns only for the direct properties of the bean type and the values are displayed as Strings. To add columns for nested properties, you can use dot notation with setColumn(String)
method. For example, if Person
has a reference to an Address
object, which has a property postalCode
, you can add a column for the postal code with:
Java
The column’s key will be “address.postalCode” and its header will be “Postal Code”. Note that you need to use the Grid constructor that takes a bean class parameter in order to use these String
properties in addColumn
.
Columns can be configured to use Renderers to show the data in a more suitable way inside the cells. Conceptually renderers are split into the three categories listed below.
Basic renderers - the renderers used to render basic values, such as dates and numbers
Template renderer - allows the developer to define cells with HTML markup and Polymer data binding syntax
Component renderer - allows the developer to use an arbitrary component inside the cells
There are several basic renderers that can be used to configure Grid columns. The currently supported basic renderers are gathered here under their own subsections.
LocalDateRenderer
Suitable for rendering LocalDate
objects inside the grid cells.
Java
grid.addColumn(new LocalDateRenderer<>(Item::getEstimatedDeliveryDate,
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)))
.setHeader("Estimated delivery date");
The LocalDateRenderer
works with both a DateTimeFormatter
or a String format to properly render LocalDate
objects.
Java
grid.addColumn(new LocalDateRenderer<>(Item::getEstimatedDeliveryDate,
"dd/MM/yyyy")).setHeader("Estimated delivery date");
LocalDateTimeRenderer
Suitable for rendering LocalDateTime
objects inside the grid cells.
Java
grid.addColumn(new LocalDateTimeRenderer<>(Item::getPurchaseDate,
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT,
FormatStyle.MEDIUM)))
.setHeader("Purchase date and time");
Similar to the LocalDateRenderer
, it is possible to configure a DateTimeFormatter
(with separate style for date and time) or a String format to properly render LocalDateTime
objects.
Java
grid.addColumn(new LocalDateTimeRenderer<>(Item::getPurchaseDate,
"dd/MM HH:mm:ss")).setHeader("Purchase date and time");
NumberRenderer
Suitable for rendering any type of Number
inside the grid cells. It is specially useful for rendering floating point values.
Java
grid.addColumn(new NumberRenderer<>(Item::getPrice,
NumberFormat.getCurrencyInstance())).setHeader("Price");
It is also possible to setup the NumberRenderer
with a String format, and an optional null representation:
Java
grid.addColumn(new NumberRenderer<>(Item::getPrice, "$ %(,.2f",
Locale.US, "$ 0.00")).setHeader("Price");
NativeButtonRenderer
An easy way to create a clickable button inside the grid cells. It creates a native <button>
on the client side, and the click and tap events (for touch devices) are treated on the server side.
Java
grid.addColumn(new NativeButtonRenderer<>("Remove item", clickedItem -> {
// remove the item
}));
It is also possible to configure a custom label for each item:
Java
grid.addColumn(new NativeButtonRenderer<>(item -> "Remove " + item, clickedItem -> {
// remove the item
}));
Using Templates
You can define the contents of the grid cells with HTML markup and use Polymer notation for data binding and event handling. This is done by providing a TemplateRenderer
for the appropriate Column
.
The following example simply bolds the names of the persons.
Java
Grid<Person> grid = new Grid<>();
grid.setItems(people);
grid.addColumn(TemplateRenderer.<Person> of("<b>[[item.name]]</b>")
.withProperty("name", Person::getName)).setHeader("Name");
As you can see, the template-string is passed for the static TemplateRenderer.of()
method, and every property used in that template needs to be defined with the withProperty()
method.
Note | The [[item.name]] is Polymer syntax for binding properties for a list of items. Using this notation in this context is pretty straightforward, but you can refer to for more details. |
Using Custom Properties
You can also create and display new properties that the item doesn’t originally contain.
For example, based on the year of birth, you could roughly compute the age of each person and add a new column to display that.
Java
grid.addColumn(TemplateRenderer.<Person> of("[[item.age]] years old")
.withProperty("age",
person -> Year.now().getValue()
- person.getYearOfBirth()))
.setHeader("Age");
Binding Beans
If the object contains a bean property that has properties of its own, you only need to make the bean accessible by calling withProperty()
, and the sub-properties become accessible as well.
For example, suppose that Person
has a field for Address
bean, and Address
has fields street
, number
and postalCode
with corresponding getter and setter methods. You can use all of those properties in your template with only one withProperty()
call, as you can see in the following snippet.
Java
grid.addColumn(TemplateRenderer.<Person> of(
"<div>[[item.address.street]], number [[item.address.number]]<br><small>[[item.address.postalCode]]</small></div>")
.withProperty("address", Person::getAddress))
.setHeader("Address");
Handling Events
You can define event handlers for the elements inside your template, and hook them to server-side code by calling withEventHandler()
method on your TemplateRenderer
. This is useful for editing the items in the grid.
The following example adds a new column with two buttons: one for editing a property of the item, and another one for removing the item. Both buttons define a method to call for on-click
events, and withEventHandler()
is used to map those method-names to server-side code.
Java
grid.addColumn(TemplateRenderer.<Person> of(
"<button on-click='handleUpdate'>Update</button><button on-click='handleRemove'>Remove</button>")
person.setName(person.getName() + " Updated");
grid.getDataProvider().refreshItem(person);
}).withEventHandler("handleRemove", person -> {
ListDataProvider<Person> dataProvider = (ListDataProvider<Person>) grid
.getDataProvider();
dataProvider.getItems().remove(person);
dataProvider.refreshAll();
})).setHeader("Actions");
Note | TemplateRenderer has fluent API, so you can chain the commands, like TemplateRenderer.of().withProperty().withProperty().withEventHandler()… |
Using Components
You can use any component inside the grid cells by providing a ComponentRenderer
for the appropriate Column
.
To define how the component will be generated for each item, you need to pass a Function
for the ComponentRenderer
. The following example adds a column that contains an icon for each person, that is based on the person’s gender.
Java
You can also separately provide a Supplier
for creating the component and a for configuring it for each item.
Java
grid.addColumn(new ComponentRenderer<>(Div::new,
(div, person) -> div.setText(person.getName())))
.setHeader("Name");
Or if the component is the same for every item, you only need to provide the Supplier
.
Java
grid.addColumn(new ComponentRenderer<>(
() -> new Icon(VaadinIcon.ARROW_LEFT)));
Using the component APIs allows you to easily listen for events and wrap multiple components inside layouts, so you can create complex contents for the grid cells.
Java
grid.addColumn(new ComponentRenderer<>(person -> {
// text field for entering a new name for the person
TextField name = new TextField("Name");
name.setValue(person.getName());
// button for saving the name to backend
Button update = new Button("Update", event -> {
person.setName(name.getValue());
grid.getDataProvider().refreshItem(person);
});
// button that removes the item
Button remove = new Button("Remove", event -> {
ListDataProvider<Person> dataProvider = (ListDataProvider<Person>) grid
.getDataProvider();
dataProvider.getItems().remove(person);
dataProvider.refreshAll();
});
// layouts for placing the text field on top of the buttons
HorizontalLayout buttons = new HorizontalLayout(update, remove);
return new VerticalLayout(name, buttons);
})).setHeader("Actions");
Note | Editing the grid’s items requires refreshing its DataProvider , like explained above in the template tutorial. More information about DataProvider can be found . |
Showing Item Details
Often you don’t want to overwhelm the user with a complex grid with all the information about each item, but instead show just the basic information by default and hide the details. For this purpose, grid supports expanding its rows for showing additional details for the items. This is enabled with the setItemDetailsRenderer()
method. You can pass either a TemplateRenderer
or a ComponentRenderer
for the method to define how the details are rendered.
Java
grid.setItemDetailsRenderer(new ComponentRenderer<>(person -> {
VerticalLayout layout = new VerticalLayout();
layout.add(new Label("Address: " + person.getAddress().getStreet()
+ " " + person.getAddress().getNumber()));
layout.add(new Label("Year of birth: " + person.getYearOfBirth()));
return layout;
}));
By default you can open the details for a row simply by clicking on it. Clicking on the same row again or opening the details for another row closes the currently opened one. You can disable this default behavior by calling grid.setDetailsVisibleOnClick(false)
. You can show and hide item details programmatically with the setDetailsVisible()
method, and test whether the details for an item is visible with isDetailsVisible()
.
A user can sort the data in a grid on a column by clicking the column header. Clicking another time on the current sort column reverses the sort direction. Clicking on a third time resets the column to its unsorted state. If multisorting is enabled, clicking on other sortable column headers adds a secondary or more sort criteria.
Defining how a column is sorted
Before jumping to the code, it’s important to understand 2 key features of the sorting mechanism: in-memory sorting and backend sorting.
In-memory sorting is the sorting that is applied to the items that have been fetched from the backend, before returning them to the client.
Backend sorting is a list of QuerySortOrder
objects that can be used when implementing your own fetching logic within a DataProvider
. You can check more details about the backend sorting here.
You can have both in-memory and backend sorting at the same time, or you can configure them separately. Here is a list of options you can use to setup the sorting for your Grid:
1. Using a sort property name at the column construction (in-memory and backend sorting)
You can set the sort properties that will be used to do backend sorting at the moment you add the column to the grid. For example:
Java
grid.addColumn(Person::getAge, "age").setHeader("Age");
The Age
column will use the values returned by Person::getAge
method to do in-memory sorting, and use the age
String to build a QuerySortOrder
that will be sent to the DataProvider
to do the backend sorting.
You can use multiple properties as well:
Java
grid.addColumn(person -> person.getName() + " " + person.getLastName(),
"name", "lastName").setHeader("Name");
When using multiple properties, the QuerySortOrder
objects are created in the order they are declared.
You can use properties created for your TemplateRenderer
too. For example:
Java
grid.addColumn(TemplateRenderer.<Person> of(
"<div>[[item.name]]<br><small>[[item.email]]</small></div>")
.withProperty("name", Person::getName)
.withProperty("email", Person::getEmail), "name", "email")
.setHeader("Person");
Note | For the in-memory sorting to work properly, the values returned by the ValueProviders inside the TemplateRenderer (Person::getAge and Person::getEmail in the example) should implement Comparable . |
Note | When using TemplateRenderers , the names of the sort properties must match the names of the properties in the template (set via withProperty ). |
2. Using a Comparator (in-memory sorting)
When you need a custom logic to compare items to sort them properly, or if your underlying data is not Comparable
, you can set a Comparator
to your column:
Java
grid.addColumn(Person::getName)
.setComparator((person1, person2) -> person1.getName()
.compareToIgnoreCase(person2.getName()))
.setHeader("Name");
3. Setting sort properties (backend sorting)
You can set strings describing backend properties to be used when sorting the column.
Java
grid.addColumn(Person::getName).setSortProperty("name", "email")
.setHeader("Person");
Note | Unlike using the sorting properties in the addColumn method directly, calling setSortProperty doesn’t configure any in-memory sorting. |
When setting the sort properties, a SortOrderProvider
is created automatically for you.
4. Setting a SortOrderProvider (backend sorting)
If you need a fine control on how the QuerySortOrder
objects are created and sent to the DataProvider
, you can set a SortOrderProvider
:
Java
grid.addColumn(Person::getName)
.setSortOrderProvider(direction -> Arrays
.asList(new QuerySortOrder("name", direction),
new QuerySortOrder("email", direction))
.stream())
.setHeader("Person");
Enabling and disabling the sorting in a column
When using any of the 4 methods described above, the column is considered sortable
- in other words, it displays the sorter element in the header of the column. You can toggle the sorter display for a column by using:
Java
column.setSortable(false);
Setting a column as not sortable
doesn’t delete any Comparator
, sort property
or SortOrderProvider
previously set - so you can toggle the sortable
flag on and off without having to reconfigure it every time.
You can check if a given column is currently sortable
by calling:
Java
column.isSortable();
To enable users to sort the data by more than one sort criteria at the same time, you can enable multi-sorting at the Grid level:
Java
Receiving sort events
You can add a SortListener
to the Grid to receive general sort events. Every time the sorting of the Grid is changed, an event is fired. You can access the DataCommunicator
for sorting details. For example:
grid.addSortListener(event -> {
String currentSortOrder = grid.getDataCommunicator()
.getBackEndSorting().stream()
.map(querySortOrder -> String.format(
"{sort property: %s, direction: %s}",
querySortOrder.getSorted(),
querySortOrder.getDirection()))
.collect(Collectors.joining(", "));
System.out.println(String.format(
"Current sort order: %s. Sort originates from the client: %s.",
});