Grid
- Usage
- Flow Usage
Binding to Data
By default, Grid
is bound to a List
of items.
You can use the setItems()
method to set the items.
Example: Showing a list of beans in a Grid
.
// Have some data
List<Person> people = Arrays.asList(
new Person("Nicolaus Copernicus", 1543),
new Person("Galileo Galilei", 1564),
new Person("Johannes Kepler", 1571));
// Create a grid bound to the list
Grid<Person> grid = new Grid<>();
grid.setItems(people);
grid.addColumn(Person::getName).setHeader("Name");
grid.addColumn(Person::getYearOfBirth)
.setHeader("Year of birth");
layout.add(grid);
Behind the scenes, Grid
uses the DataProvider
interface to communicate with the backend.
The setItems()
method is a shorthand to create a ListDataProvider
.
For a large amount of data or other advanced use cases, you should probably use the DataProvider
interface and lazy loading.
See Data Providers for more.
Handling Selection Changes
The Grid
does not implement the HasValue
interface directly.
Other selection components do typically implement this interface.
For this reason, selection handling in the Grid
is different from typical selection components.
The Grid
supports three selection options: single selection, multiple selection, and no selection.
Each option is defined by a specific selection model.
For basic switching between selection models, you can use the setSelectionMode(SelectionMode)
method.
Possible options are SINGLE
(default), MULTI
, or NONE
.
To access the selection API or to use Grid as an input field with Binder
, you can use asSingleSelect()
or asMultiSelect()
, depending on the currently defined selection mode.
Both the SingleSelect
and MultiSelect
interfaces implement the HasValue
interface.
In the MultiSelect
interface, the value type is a Set
of the item type.
Example: Using the HasValue
interface with single and multi-select mode.
Grid<Person> grid = new Grid<>();
grid.setSelectionMode(SelectionMode.SINGLE);
SingleSelect<Grid<Person>, Person> personSelect =
grid.asSingleSelect();
// personSelect can now be used with Binder or
// HasValue interface
personSelect.addValueChangeListener(e -> {
Person selectedPerson = e.getValue();
});
grid.setSelectionMode(SelectionMode.MULTI);
MultiSelect<Grid<Person>, Person> multiSelect =
grid.asMultiSelect();
multiSelect.addValueChangeListener(e -> {
Set<Person> selectedPersons = e.getValue();
});
Alternatively, you can use a grid-specific selection API.
To get the selected value or values in any selection model, you can use a SelectionListener
, with the provided generic SelectionEvent
, to get the selected value or values.
Example: Using addSelectionListener()
to get all selected items.
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");
});
Note | The listener is attached to the selection model and not the grid. It stops getting events when the selection mode is changed. |
You can use the select(T)
method to programmatically select values.
In multi-selection mode, this adds the given item to the selection.
Example: Using the select(T)
method.
// 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);
You can get the current selection from the Grid
using the getSelectedItems()
method.
The returned Set
contains one item in single-selection mode, or several items in multi-selection mode.
Warning | If you change the grid’s selection mode, it clears the selection and fires a selection event.
To keep the previous selection, reset the selection afterwards, using the |
Warning | If you change the grid’s items with either |
Selection Models
You can access the used selection model using the getSelectionModel()
method.
The return type is the GridSelectionModel
, which has a generic selection model API, but you can cast that to the specific selection model type, typically either SingleSelectionModel
or MultiSelectionModel
.
You can also get the selection model using the setSelectionMode(SelectionMode)
method.
Example: Using the setSelectionMode(SelectionMode)
method to get the selection model.
// 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
Obtaining a reference to the SingleSelectionModel
allows you access to a fine-grained API for the single-selection use case.
You can use the addSingleSelect(SingleSelectionListener)
method to access SingleSelectionEvent
, which includes additional convenience methods and API options.
In single-selection mode, it is possible to control whether the empty (null) selection is allowed. This is enabled by default.
Example: Disallowing empty (null) selection using the setDeselectAllowed()
method.
// preselect value
grid.select(defaultItem);
GridSingleSelectionModel<Person> singleSelect =
(GridSingleSelectionModel<Person>) grid
.getSelectionModel();
// disallow empty selection
singleSelect.setDeselectAllowed(false);
Multi-Selection Model
In multi-selection mode, a user can select multiple items by selecting checkboxes in the left column.
Obtaining a reference to the MultiSelectionModel
allows you access to a fine-grained API for the multi-selection use case.
You can use the addMultiSelectionListener(MultiSelectionListener)
method to access MultiSelectionEvent
, which includes additional convenience methods and API options.
Example: Using the addMultiSelectionListener()
method to access selection changes.
// 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 -> {
message.setText(String.format(
"%s items added, %s removed.",
event.getAddedSelection().size(),
event.getRemovedSelection().size()));
// Allow deleting only if there's any selected
deleteSelected.setEnabled(
event.getNewSelection().isEmpty());
});
Handling Item-Click Events
It is possible to handle item-click or double-click events, in addition to handling selection events. These can be used with selection events or on their own.
Example: Disabling the selection mode using SelectionMode.NONE
, but still getting item-click events.
grid.setSelectionMode(SelectionMode.NONE);
grid.addItemClickListener(event -> System.out
.println(("Clicked Item: " + event.getItem())));
The clicked item, together with other information about the click, is available via the event.
Selection events are no longer available, and no visual selection is displayed when a row is clicked.
It is possible to get separate selection and click events.
Example: Using Grid
in multi-selection mode with an added click (or double-click) listener.
grid.setSelectionMode(SelectionMode.MULTI);
grid.addItemDoubleClickListener(event ->
copy(grid.getSelectedItems()));
In the example code, we call a local
copy()
method with the currently selected items when the user double-clicks a row.
Configuring Columns
The addColumn()
method allows you to add columns to the Grid
.
The column configuration is defined in Grid.Column
objects that are returned by the addColumn()
method.
The getColumns()
method returns a list of currently configured columns.
The setter methods in Column
have fluent-API functionality, making it easy to chain configuration calls for columns.
Example: Chaining column configuration calls.
Column<Person> nameColumn = grid
.addColumn(Person::getName)
.setHeader("Name")
.setFlexGrow(0)
.setWidth("100px")
.setResizable(false);
Column Keys
You can set an identifier key for a column using the setKey()
method.
This allows you to retrieve the column from the grid at any time.
Example: Using the setKey()
method to set an identifier key for a column.
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 by passing the class of the bean type to the grid’s constructor.
The property names are set as the column keys, and you can use them to further configure the columns.
Example: Automatically adding columns by passing the bean-type class to the constructor.
Grid<Person> grid = new Grid<>(Person.class);
grid.getColumnByKey("yearOfBirth").setFrozen(true);
This constructor only adds columns for the direct properties of the bean type
The values are displayed as strings.
You can add columns for nested properties by using the dot notation with the setColumn(String)
method.
Example: Adding a column for postalCode
.
Assumes Person
has a reference to an Address
object that has a postalCode
property.
grid.addColumn("address.postalCode");
The column’s key is "address.postalCode" and its header is "Postal Code".
To use these
String
properties inaddColumn()
, you need to use theGrid
constructor, which takes a bean-class parameter.
Defining and Ordering Automatically Added Columns
You can define which columns display, and the order in which they display, in the grid, using the setColumns()
method.
Example: Defining columns and their order using the setColumns()
method.
Grid<Person> grid = new Grid<>(Person.class);
grid.setColumns("name", "age", "address.postalCode");
Tip |
You can also use the setColumns() method to reorder the columns you already have.
|
Note |
When calling setColumns() , all columns that are currently present in the grid are removed, and only those passed as parameters are added.
|
To add custom columns before the auto-generated columns, use the addColumns()
method instead.
You can avoid creating the auto-generated columns using the Grid(Class, boolean)
constructor.
Example: Adding custom columns.
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(person -> person.getName().split(" ")[0])
.setHeader("First name");
grid.addColumns("age", "address.postalCode");
Note |
An IllegalArgumentException is thrown if you attempt to add columns that are already present the grid.
|
Sortable Automatic Columns
By default, all property-based columns are sortable, if the property type implements Comparable
.
Many data types, such as String
, Number
, primitive types and Date
/LocalDate
/LocalDateTime
are Comparable
, and therefore also sortable, by default.
To make the column of a non-comparable property type sortable, you need to define a custom Comparator
.
See Column Sorting for more.
You can disable sorting for a specific column using the setSortable()
method.
Example: Disabling sorting on the address.postalCode
column.
grid.getColumnByKey("address.postalCode")
.setSortable(false);
You can also define a list of columns as sortable using the setSortableColumns()
method.
This makes all other columns not sortable.
Example: Setting defined columns as sortable.
// All columns except "name" and "yearOfBirth"
// will be not sortable
grid.setSortableColumns("name", "yearOfBirth");
Column Headers and Footers
By default, columns do not have a header or footer.
These need to be set explicitly using the setHeader()
and setFooter()
methods.
Both methods have two overloads; one accepts a plain text string and the other a LitRenderer
.
Examples: Setting headers and footers.
// Sets a simple text header
nameColumn.setHeader("Name");
// Sets a header using Html component,
// in this case simply bolding the caption "Name"
nameColumn.setHeader(new Html("<b>Name</b>"));
// Similarly for the footer
nameColumn.setFooter("Name");
nameColumn.setFooter(new Html("<b>Name</b>"));
See Using Lit Renderers for more.
Column Reordering
Column reordering is not enabled by default.
You can use the setColumnReorderingAllowed()
method to allow drag and drop column reordering.
Example: Enabling column reordering.
grid.setColumnReorderingAllowed(true);
Hiding Columns
Columns can be hidden by calling the setVisible()
method in Column
.
Note | A hidden column still sends the data required to render it to the client side. Best practice is to remove (or not add) columns if the data is not needed on the client side. This reduces the amount of data sent and lessens the load on the client. |
Removing Columns
You can remove a single column using the removeColumn(Column)
and removeColumnByKey(String)
methods.
You can also remove all currently configured columns using the removeAllColumns()
method.
Setting Column Widths
By default, columns do not have a defined width. They resize automatically based on the data displayed.
You can set the column width:
relatively, using flex grow ratios, by using the
setFlexGrow()
method, orexplicitly, using a CSS string value with
setWidth()
(with flex grow set to0
).
You can also enable user column resizing using the setResizable()
method.
The column is resized by dragging the column separator.
Setting Frozen Columns
You can freeze a number of columns using the setFrozen()
method.
This ensures that the set number of columns on the left remain static (and visible) when the user scrolls horizontally.
When columns are frozen, user reordering is limited to the frozen columns.
Example: Setting a column as frozen.
nameColumn.setFrozen(true);
Grouping Columns
You can group multiple columns together by adding them in the HeaderRow
of the grid.
When you retrieve the HeaderRow
, using the prependHeaderRow()
or appendHeaderRow()
methods, you can then group the columns using the join()
method.
In addition, you can use the setText()
and setComponent()
methods on the join result to set the text or component for the joined columns.
Example: Grouping columns
// 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"));
Using Renderers in Columns
You can configure columns to use a renderer to show the data in the cells.
Conceptually, there are three types of renderer:
Basic renderer: Renders basic values, such as dates and numbers.
Lit renderer: Renders content using HTML markup and Lit data-binding syntax.
Component renderer: Renders content using arbitrary components.
Using Basic Renderers
There are several basic renderers that you can use to configure grid columns.
Local Date Renderer
Use LocalDateRenderer
to render LocalDate
objects in the cells.
Example: Using LocalDateRenderer
with the addColumn()
method.
grid.addColumn(new LocalDateRenderer<>(
Item::getEstimatedDeliveryDate,
DateTimeFormatter.ofLocalizedDate(
FormatStyle.MEDIUM)))
.setHeader("Estimated delivery date");
LocalDateRenderer
works with a DateTimeFormatter
or a String
format to properly render LocalDate
objects.
Example: Using a String
format to render the LocalDate
object.
grid.addColumn(new LocalDateRenderer<>(
Item::getEstimatedDeliveryDate,
"dd/MM/yyyy"))
.setHeader("Estimated delivery date");
Local Date Time Renderer
Use LocalDateTimeRenderer
to render LocalDateTime
objects in the cells.
Example: Using LocalDateTimeRenderer
with the addColumn()
method.
grid.addColumn(new LocalDateTimeRenderer<>(
Item::getPurchaseDate,
DateTimeFormatter.ofLocalizedDateTime(
FormatStyle.SHORT,
FormatStyle.MEDIUM)))
.setHeader("Purchase date and time");
LocalDateTimeRenderer
also works with DateTimeFormatter
(with separate styles for date and time) or a String
format to properly render LocalDateTime
objects.
Example: Using a String
format to render the LocalDateTime
object.
grid.addColumn(new LocalDateTimeRenderer<>(
Item::getPurchaseDate,
"dd/MM HH:mm:ss")
).setHeader("Purchase date and time");
Number Renderer
Use NumberRenderer
to render any type of Number
in the cells.
It is especially useful for rendering floating-point values.
Example: Using NumberRenderer
with the addColumn()
method.
grid.addColumn(new NumberRenderer<>(Item::getPrice,
NumberFormat.getCurrencyInstance())
).setHeader("Price");
It is possible to set up the NumberRenderer
with a String
format, and an optional null representation.
Example: Using a String
format to render a price.
grid.addColumn(new NumberRenderer<>(
Item::getPrice, "$ %(,.2f",
Locale.US, "$ 0.00")
).setHeader("Price");
Native Button Renderer
Use NativeButtonRenderer
to create a clickable button in the cells.
This creates a native <button>
on the client side.
Click and tap (for touch devices) events are handled on the server side.
Example: Using NativeButtonRenderer
with the addColumn()
method.
grid.addColumn(
new NativeButtonRenderer<>("Remove item",
clickedItem -> {
// remove the item
})
);
You can configure a custom label for each item.
Example: Configuring NativeButtonRenderer
to use a custom label.
grid.addColumn(new NativeButtonRenderer<>(
item -> "Remove " + item,
clickedItem -> {
// remove the item
})
);
Using Lit Renderers
Providing a LitRenderer
for a column allows you to define the content of cells using HTML markup, and to use Lit notations for data binding and event handling.
Example: Using LitRenderer
to embolden the names of the persons.
Grid<Person> grid = new Grid<>();
grid.setItems(people);
grid.addColumn(LitRenderer
.<Person>of("<b>${item.name}</b>")
.withProperty("name", Person::getName)
).setHeader("Name");
The template string is passed for the static
LitRenderer.of()
method.Every property in the template needs to be defined in the
withProperty()
method.${item.name}
is the Lit syntax for interpolating properties into the template. See the Lit documentation for more.When using a custom Web Component or a Vaadin element in a Lit renderer, remember to import the component. This can be done using
@JsModule
or@Uses
, if the component has a server-side counterpart. This will ensure that all StyleSheet, HtmlImport, and JavaScript dependencies for the component are loaded when the Grid is used.
Creating Custom Properties
You can use a LitRenderer
to create and display new properties (i.e., properties the item did not originally contain).
Example: Using LitRenderer
to compute the approximate age of each person and add it in a new column.
Age is the current year minus the birth year.
grid.addColumn(LitRenderer
.<Person>of("${item.age} years old")
.withProperty("age",
person -> Year.now().getValue()
- person.getYearOfBirth())
).setHeader("Age");
Using Expressions
Lit templates can include any type of JavaScript expression, not limited to binding single property values.
Example: By evaluating the person’s age in the template expression, the age column could also be written as:
grid.addColumn(LitRenderer
.<Person>of("${new Date().getFullYear() - item.yearOfBirth} years old")
.withProperty("yearOfBirth", Person::getYearOfBirth);
).setHeader("Age");
Binding Beans
If an object contains a bean property that has sub-properties, it is only necessary to make the bean accessible by calling the withProperty()
method.
The sub-properties become accessible automatically.
Warning | Note that even properties of the bean which are not used in the template are sent to the client, so use this feature with caution. |
Example: Using the withProperty()
method to access multiple sub-properties.
This assumes that Person
has a field for the Address
bean, which has street
, number
and postalCode
fields with corresponding getter and setter methods.
grid.addColumn(LitRenderer.<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 in your template, and hook them to server-side code, by calling the withFunction()
method on your LitRenderer
.
This is useful for editing items in the grid.
Example: Using the withFunction()
method to map defined method names to server-side code.
The snippet adds a new column with two buttons: one to edit a property of the item and one to remove the item.
Both buttons define a method to call for click
events.
grid.addColumn(LitRenderer.<Person>of(
"<button @click=\"${handleUpdate}\">Update</button>" +
"<button @click=\"${handleRemove}\">Remove</button>")
.withFunction("handleUpdate", person -> {
person.setName(person.getName() + " Updated");
grid.getDataProvider().refreshItem(person);
}).withFunction("handleRemove", person -> {
ListDataProvider<Person> dataProvider =
(ListDataProvider<Person>) grid
.getDataProvider();
dataProvider.getItems().remove(person);
dataProvider.refreshAll();
})).setHeader("Actions");
When the server-side data used by the grid is edited, the grid’s
DataProvider
is refreshed by calling therefreshItem()
method. This ensures that the changes show up in the element.When an item is removed, the
refreshAll()
method call ensures that all the data is updated.You need to use Lit notation for event handlers.
@click
is Lit syntax for the nativeclick
.LitRenderer
has a fluent API, so you can chain the commands, likeLitRenderer.of().withProperty().withProperty().withFunction()…
The withFunction()
handler can also receive more data in addition to the item.
In order to pass additional data from client to the server-side handler, you need to invoke the function in the Lit template with the desired extra parameters.
The additional data can be accessed via the second handler parameter (of type JsonArray
).
Example:
grid.addColumn(LitRenderer.<Person>of(
"<input .value=\"${item.profession}\" @change=\"${e => changed(e.target.value)}\">")
.withFunction("changed", (person, args) -> {
String profession = args.getString(0);
person.setProfession(profession);
grid.getDataProvider().refreshItem(person);
}).withProperty("profession", Person::getProfession));
The functions defined by the
withFunction()
method can be called with any number of additional parameters.The additional argument of type
String
(the updated profession) is obtained from the second handler parameter withargs.getString(0)
, where the number is the index of the argument in theJsonArray
.
Using Component Renderers
You can use any component in the grid cells by providing a ComponentRenderer
for a column.
To define how the component will be generated for each item, you need to pass a Function
for the ComponentRenderer
.
Example: Adding a column that contains a different icon depending on the person’s gender.
Grid<Person> grid = new Grid<>();
grid.setItems(people);
grid.addColumn(new ComponentRenderer<>(person -> {
if (person.getGender() == Gender.MALE) {
return VaadinIcon.MALE.create();
} else {
return VaadinIcon.FEMALE.create();
}
})).setHeader("Gender");
It is also possible to provide a separate Supplier
to create the component, and a Consumer
to configure it for each item.
Example: Using ComponentRenderer
with a Consumer
.
SerializableBiConsumer<Div, Person> consumer =
(div, person) -> div.setText(person.getName());
grid.addColumn(
new ComponentRenderer<>(Div::new, consumer))
.setHeader("Name");
If the component is the same for every item, you only need to provide the Supplier
.
Example: Using ComponentRenderer
with a Supplier
.
grid.addColumn(
new ComponentRenderer<>(
() -> VaadinIcon.ARROW_LEFT.create()));
You can create complex content for the grid cells by using the component APIs.
Example: Using ComponentRenderer
to create complex content that listens for events and wraps multiple components in layouts.
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 | addComponentColumn() is a shorthand for addColumn() with a ComponentRenderer .
|
Editing grid items requires refreshing the grid’s
DataProvider
. The reasoning is the same as for Handling Events mentioned earlier.
See Data Providers for more.
Enabling Expanding Rows
The Grid
supports expanding rows that reveal more detail about the items.
The additional information is hidden, unless the user chooses to reveal it, keeping the grid appearance clean and simple, while simultaneously allowing detailed explanations.
You can enable expanding rows using the setItemDetailsRenderer()
method, which allows either a LitRenderer
or a ComponentRenderer
to define how the details are rendered.
Example: Using the setItemDetailsRenderer()
method with a ComponentRenderer
.
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, the row’s detail opens by clicking the row.
Clicking the row again, or clicking another row (to open its detail), automatically closes the first row’s detail.
You can disable this behavior by calling the grid.setDetailsVisibleOnClick(false)
method.
You can show and hide item details programmatically using the setDetailsVisible()
method, and test whether an item’s detail is visible using the isDetailsVisible()
method.
Note |
By default, items are selected by clicking them.
If you want the click action only to show the item details without selection, you need to use the grid.setSelectionMode(SelectionMode.NONE) method.
|
Column Sorting
By default, this is how column sorting in the grid works:
The first click on the column header sorts the column.
The second click reverses the sort order.
The third click resets the column to its unsorted state.
If multi-sorting is enabled, the user can sort by multiple columns. The first click sorts the first column. Subsequent clicks on second and more sortable column headers add secondary and more sort criteria.
Defining Column Sorting
The difference between in-memory and backend sorting is key to understanding the sorting mechanism:
In-memory sorting is sorting that is applied by the framework to items fetched from the backend, before returning them to the client.
Backend sorting is applied by providing a list of
QuerySortOrder
objects to yourDataProvider
. These typically pass the sort hints to the backend code and, in some cases, all the way to database queries. See Data Providers for more.
The sorting mechanism is flexible; you can configure in-memory and backend sorting together or separately.
The sections that follow detail options you can use to set up sorting for your grid.
Using a Sort Property Name
By using a sort property, you can override or customize the property or multiple properties that are used for sorting the column. This option includes both in-memory and backend sorting. The property is defined at the time of column construction and uses a sort property name.
You can use the addColumn()
method to set a sort property to be used for backend sorting when the column is added to the grid.
Example: Using the addColumn()
method to set a column sort property.
grid.addColumn(Person::getAge, "age").setHeader("Age");
The
Age
column uses the values returned by thePerson::getAge()
method to do in-memory sorting.The column uses the
age
string to build aQuerySortOrder
that is sent to theDataProvider
to do the backend sorting.
You can also define multiple properties.
Example: Using the addColumn()
method to set multiple column sort properties.
grid.addColumn(person -> person.getName() + " " +
person.getLastName(), "name", "lastName"
).setHeader("Name");
With multiple properties, the
QuerySortOrder
objects are created in the order they are declared.
You can also use properties created for your LitRenderer
.
Example: Using the addColumn()
method with LitRenderer
to set column sort properties.
grid.addColumn(LitRenderer.<Person>of(
"<div>${item.name}<br>" +
"<small>${item.email}</small></div>")
.withProperty("name", Person::getName)
.withProperty("email", Person::getEmail),
"name", "email")
.setHeader("Person");
For in-memory sorting to work correctly, the values returned by the
ValueProviders
in theLitRenderer
(Person::getName()
andPerson::getEmail()
in this example) should implementComparable
.The names of the sort properties must match the names of the properties in the template (set via
withProperty()
).
Using a Comparator
This option is for in-memory sorting only, and uses a custom comparator.
If you need custom logic to compare items for sorting, or if your underlying data is not Comparable
, you can set a Comparator
for your column.
Example: Using the setComparator()
method to configure a comparator for a column.
grid.addColumn(Person::getName)
.setComparator((person1, person2) ->
person1.getName()
.compareToIgnoreCase(person2.getName()))
.setHeader("Name");
Setting Backend Sort Properties
This option is for backend sorting only, and uses a sort property name. It is similar to Using a Sort Property Name, but excludes in-memory sorting.
You can use the setSortProperty()
method to set strings describing backend properties to be used when sorting the column.
Example: Using the setSortProperty()
method to define sorting.
grid.addColumn(Person::getName)
.setSortProperty("name", "email")
.setHeader("Person");
Unlike using the sorting properties in the
addColumn()
method directly, callingsetSortProperty()
does not configure any in-memory sorting.A
SortOrderProvider
is created automatically when the sort properties are set.
Setting a SortOrderProvider
This option is for backend sorting and uses a SortOrderProvider
.
If you need fine-grained control over how QuerySortOrder
objects are created and sent to the DataProvider
, you can define a SortOrderProvider
.
Example: Defining a SortOrderProvider
for backend sorting.
grid.addColumn(Person::getName)
.setSortOrderProvider(direction -> Arrays
.asList(new QuerySortOrder("name", direction),
new QuerySortOrder("email", direction))
.stream())
.setHeader("Person");
Enabling and Disabling Column Sorting
When a column is sortable
, it displays the sorter element in the column header.
You can use the setSortable()
method to toggle the sorter element on and off.
Example: Using the setSortable()
method to disable sorting.
column.setSortable(false);
Setting a column as not sortable
does not delete a Comparator
, sort property, or SortOrderProvider
that was previously set.
You can toggle the sortable
flag on and off, without reconfiguration.
To check if a column is currently sortable
, you can use the isSortable()
method.
Example: Checking if a column is sortable.
column.isSortable();
Enabling Multi-Sorting
To allow users to sort by more than one column at the same time, you can use the setMultiSort()
method to enable multi-sorting at the grid level.
Example: Using the setMultiSort()
method to enable multi-sorting.
grid.setMultiSort(true);
Receiving Sort Events
You can add a SortListener
to the grid to receive general sort events.
Every time sorting of the grid is changed, an event is fired.
You can access the DataCommunicator
to receive the sorting details.
Example: Using the addSortListener()
method to add a SortListener
.
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. User-clicked: %s.",
currentSortOrder, event.isFromClient()));
});
Styling the Grid
Styling the Grid
component (or any Vaadin component) requires some Web Component and shadow-DOM knowledge.
Styling depends on the component’s position in the DOM:
If the component is in the shadow DOM, you can apply styling within the component or using variables.
If the component is in the "normal" DOM (not in the shadow DOM), normal CSS styling applies.
In addition, the Grid
supports the theme
attribute, which allows you to easily customize component styling.
Example: Celebrity
grid used in styling examples below.
Grid<Celebrity> grid = new Grid<>();
grid.setItems(Celebrity.getPeople());
grid.addClassName("styled");
grid.addColumn(new ComponentRenderer<>(person -> {
TextField textField = new TextField();
textField.setValue(person.getName());
textField.addClassName("style-" +
person.getGender());
textField.addValueChangeListener(
event -> person.setName(event.getValue()));
return textField;
})).setHeader("Name");
grid.addColumn(new ComponentRenderer<>(person -> {
DatePicker datePicker = new DatePicker();
datePicker.setValue(person.getDob());
datePicker.addValueChangeListener(event -> {
person.setDob(event.getValue());
});
datePicker.addClassName("style-" +
person.getGender());
return datePicker;
})).setHeader("DOB");
grid.addColumn(new ComponentRenderer<>(person -> {
Image image = new Image(person.getImgUrl(),
person.getName());
return image;
})).setHeader("Image");
Styling With the Theme Property
The default Lumo theme includes different variations that you can use to style the grid. You can provide one or more variations.
Example: Using the addThemeVariants()
method to define theme variations for the grid.
grid.addThemeVariants(GridVariant.LUMO_NO_ROW_BORDERS,
GridVariant.LUMO_NO_BORDER, GridVariant.LUMO_ROW_STRIPES);
Styling With CSS
You can use normal CSS styling for the content in the grid cells.
Although the Grid
component itself is in the shadow DOM, the actual values (cell contents) are in slots and therefore in the light DOM.
Example: Setting the maximum size for images in the grid cells.
vaadin-grid vaadin-grid-cell-content img {
max-height: 4em;
}
vaadin-grid-cell-content
is in the light DOM, and the selectorvaadin-grid vaadin-grid-cell-content
points to all the grid’s cells.
You can also use a class to apply styles to a specific component instance.
Example: Applying rounded borders and centering images in a grid with a "styled" class name.
vaadin-grid.styled vaadin-grid-cell-content img {
border-radius: 2em;
margin-left: 50%;
transform: translate(-50%);
}
To learn how to customize styles inside a component’s shadow DOM, see Styling Components.
F478ED56-7248-4788-BC75-4E05605EB33F