aboutsummaryrefslogtreecommitdiffstats
path: root/documentation/components/components-grid.asciidoc
diff options
context:
space:
mode:
authorMarkus Koivisto <markus@vaadin.com>2016-01-22 14:55:18 +0200
committerMarkus Koivisto <markus@vaadin.com>2016-01-22 14:55:18 +0200
commit99d6de546c74f0eed230ea8253dda6b85109d2e7 (patch)
tree10fc21c557566fe3241e6e13499df18d80f8dcb2 /documentation/components/components-grid.asciidoc
parent610736d9f373d4b37fd39ff8f90aabd13eab7926 (diff)
downloadvaadin-framework-99d6de546c74f0eed230ea8253dda6b85109d2e7.tar.gz
vaadin-framework-99d6de546c74f0eed230ea8253dda6b85109d2e7.zip
Add documentation to master branch
Change-Id: I2504bb10f1ae73ec0cbc08b7ba5a88925caa1674
Diffstat (limited to 'documentation/components/components-grid.asciidoc')
-rw-r--r--documentation/components/components-grid.asciidoc1407
1 files changed, 1407 insertions, 0 deletions
diff --git a/documentation/components/components-grid.asciidoc b/documentation/components/components-grid.asciidoc
new file mode 100644
index 0000000000..e2013b8daa
--- /dev/null
+++ b/documentation/components/components-grid.asciidoc
@@ -0,0 +1,1407 @@
+---
+title: Grid
+order: 24
+layout: page
+---
+
+[[components.grid]]
+= [classname]#Grid#
+
+ifdef::web[]
+[.sampler]
+image:{live-demo-image}[alt="Live Demo", link="http://demo.vaadin.com/sampler/#ui/grids-and-trees/grid]
+endif::web[]
+
+((("[classname]#Grid#")))
+[classname]#Grid# is many things, and perhaps the most versatile and powerful
+component in Vaadin. Like [classname]#Table#, it allows presenting and editing
+tabular data, but escapes many of [classname]#Table#'s limitations. Efficient
+lazy loading of data while scrolling greatly improves performance. Grid is
+scalable, mobile friendly, and extensible.
+
+[[components.grid.overview]]
+== Overview
+
+[classname]#Grid# is for displaying and editing tabular data laid out in rows
+and columns. At the top, a __header__ can be shown, and a __footer__ at the
+bottom. In addition to plain text, the header and footer can contain HTML and
+components. Having components in the header allows implementing filtering
+easily. The grid data can be sorted by clicking on a column header;
+shift-clicking a column header enables secondary sorting criteria.
+
+[[figure.components.grid.features]]
+.A [classname]#Grid# Component
+image::img/grid-features.png[]
+
+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.
+
+The grid data can be edited with a row-based editor after double-clicking a row.
+The fields are generated with a field factory, or set explicitly, and bound to
+data with a field group.
+
+Grid is fully themeable with CSS and style names can be set for all grid
+elements. For data rows and cells, the styles can be generated with a row or
+cell style generator.
+
+Finally, [classname]#Grid# is designed to be extensible and used just as well
+for client-side development - its GWT API is nearly identical to the server-side
+API, including data binding.
+
+[[components.grid.overview.table]]
+=== Differences to Table
+
+In addition to core features listed above, [classname]#Grid# has the following
+API-level and functional differences to Table:
+
+* Grid is not a [interfacename]#Container# itself, even though it can be bound to a container data source. Consequently, columns are defined differently, and so forth.
+* Rows can be added with [methodname]#addRow()# shorthand (during initialization) instead of [methodname]#addItem()#.
+* Use [methodname]#setHeightByRows()# and [methodname]#setHeightMode()# instead of [methodname]#setPageLength()# to set the height in number of rows.
+* Grid does not extend [classname]#AbstractSelect# and is not a field, but has its own selection API. [methodname]#addSelectionListener()# is called to define a [interfacename]#SelectionListener#. The listener also receives a collection of deselected items.
+* Grid does not support having all cells in editable mode, it only supports row-based editing, with a row mini-editor that allows saving or discarding the changes.
+* Grid has no generated columns. Instead, the container data source can be wrapped around a [classname]#GeneratedPropertyContainer#.
+* No column icons; you can implement them in a column with an [classname]#ImageRenderer#.
+* Components can not be shown in Grid cells; instead the much more efficient renderers can be used for the most common cases, and row editor for editing values.
+* Limited support for drag and drop: the user can drag columns to reorder them.
+
+In addition, Grid has the following visual changes:
+
+* Multiple selection is indicated with check boxes in addition to highlighting.
+* Grid does not show the row loading indicator like Table does.
+
+
+
+[[components.grid.data]]
+== Binding to Data
+
+[classname]#Grid# is normally used by binding it to a container data source,
+described in
+<<dummy/../../../framework/datamodel/datamodel-container#datamodel.container,"Collecting
+Items in Containers">>. The container must implement
+[interfacename]#Container.Indexed# interface. By default, it is bound to an
+[classname]#IndexedContainer#; Grid offers some shorthand methods to operate on
+the default container, as described later.
+
+You can set the container in the constructor or with
+[methodname]#setContainerDataSource()#.
+
+For example, if you have a collection of beans, you could wrap them in a Vaadin
+[classname]#BeanContainer# or [classname]#BeanItemContainer#, and bind to a [classname]#Grid# as follows
+
+
+[source, java]
+----
+// Have some data
+Collection<Person> people = Lists.newArrayList(
+ new Person("Nicolaus Copernicus", 1543),
+ new Person("Galileo Galilei", 1564),
+ new Person("Johannes Kepler", 1571));
+
+// Have a container of some type to contain the data
+BeanItemContainer<Person> container =
+ new BeanItemContainer<Person>(Person.class, people);
+
+// Create a grid bound to the container
+Grid grid = new Grid(container);
+grid.setColumnOrder("name", "born");
+layout.addComponent(grid);
+----
+
+Note that you need to override [methodname]#equals()# and [methodname]#hashcode()# for
+the bean ([classname]#Person#) class to make the [classname]#BeanItemContainer# work properly.
+
+[[components.grid.basic.manual]]
+=== Default Data Source and Shorthands
+
+Sometimes, when you have just a few fixed items that you want to display, you
+can define the grid columns and add data rows manually. [classname]#Grid# is by
+default bound to a [classname]#IndexedContainer#. You can define new columns
+(container properties) with [methodname]#addColumn()# and then add rows (items)
+with [methodname]#addRow()#. The types in the row data must match the defined
+column types.
+
+For example:
+
+
+[source, java]
+----
+// Create a grid
+Grid grid = new Grid();
+
+// Define some columns
+grid.addColumn("name", String.class);
+grid.addColumn("born", Integer.class);
+
+// Add some data rows
+grid.addRow("Nicolaus Copernicus", 1543);
+grid.addRow("Galileo Galilei", 1564);
+grid.addRow("Johannes Kepler", 1571);
+
+layout.addComponent(grid);
+----
+
+Or, if you have the data in an array:
+
+
+[source, java]
+----
+// Have some data
+Object[][] people = { {"Nicolaus Copernicus", 1543},
+ {"Galileo Galilei", 1564},
+ {"Johannes Kepler", 1571}};
+for (Object[] person: people)
+ grid.addRow(person);
+----
+
+Note that you can not use [methodname]#addRow()# to add items if the container
+is read-only or has read-only columns, such as generated columns.
+
+
+
+[[components.grid.selection]]
+== Handling Selection Changes
+
+Selection in [classname]#Grid# is handled a bit differently from other selection
+components, as it is not an [classname]#AbstractSelect#. Grid supports both
+single and multiple selection, defined by the __selection mode__. Selection
+events can be handled with a [interfacename]#SelectionListener#.
+
+[[components.grid.selection.mode]]
+=== Selection Mode
+
+A [classname]#Grid# can be set to be in [literal]#++SINGLE++# (default),
+[literal]#++MULTI++#, or [literal]#++NONE++# selection mode, defined in the
+[classname]#Grid.SelectionMode# enum.
+
+
+[source, java]
+----
+// Use single-selection mode (default)
+grid.setSelectionMode(SelectionMode.SINGLE);
+----
+
+Empty (null) selection is allowed by default, but can be disabled
+with [methodname]#setDeselectAllowed()# in single-selection mode.
+
+The selection is handled with a different selection model object in each
+respective selection mode: [classname]#SingleSelectionModel#,
+[classname]#MultiSelectionModel#, and [classname]#NoSelectionModel# (in which
+selection is always empty).
+
+
+[source, java]
+----
+// Pre-select an item
+SingleSelectionModel selection =
+ (SingleSelectionModel) grid.getSelectionModel();
+selection.select( // Select 3rd item
+ grid.getContainerDataSource().getIdByIndex(2));
+----
+
+
+[[components.grid.selection.single]]
+=== Handling Selection
+
+Changes in the selection can be handled with a
+[interfacename]#SelectionListener#. You need to implement the
+[methodname]#select()# method, which gets a [classname]#SelectionEvent# as
+parameter. In addition to selection, you can handle clicks on rows or cells with
+a [interfacename]#ItemClickListener#.
+
+You can get the new selection from the selection event with
+[methodname]#getSelected()#, which returns a set of item IDs, or more simply
+from the grid or the selection model with [methodname]#getSelectedRow()#, which
+returns the single selected item ID.
+
+For example:
+
+
+[source, java]
+----
+grid.addSelectionListener(selectionEvent -> { // Java 8
+ // Get selection from the selection model
+ Object selected = ((SingleSelectionModel)
+ grid.getSelectionModel()).getSelectedRow();
+
+ if (selected != null)
+ Notification.show("Selected " +
+ grid.getContainerDataSource().getItem(selected)
+ .getItemProperty("name"));
+ else
+ Notification.show("Nothing selected");
+});
+----
+
+The current selection can be obtained from the [classname]#Grid# object by
+[methodname]#getSelectedRow()# or [methodname]#getSelectedRows()#, which return
+one (in single-selection mode) or all (in multi-selection mode) selected items.
+
+
+[WARNING]
+====
+Note that changes to the item set of the container data source are not
+automatically reflected in the selection model and may cause the selection model
+to refer to stale item IDs. This always occurs, for example, when you delete the
+selected item or items. So, if you modify the item set of the container, you
+should synchronize or reset the selection with the container, such as by calling
+[methodname]#reset()# on the selection model.
+
+====
+
+
+
+
+[[components.grid.selection.multi]]
+=== Multiple Selection
+
+In the multiple selection mode, a user can select multiple items by clicking on
+the checkboxes in the leftmost column, or by using the kbd:[Space] to select/deselect the currently focused row.
+Space bar is the default key for toggling the selection, but it can be customized.
+
+[[figure.components.grid.selection.multi]]
+.Multiple Selection in [classname]#Grid#
+image::img/grid-selection-multi.png[]
+
+The selection is managed through the [classname]#MultiSelectionMode# class. The
+currently selected rows can be set with [methodname]#setSelected()# by a
+collection of item IDs, or you can use [methodname]#select()# to add items to
+the selection.
+
+
+[source, java]
+----
+// Grid in multi-selection mode
+Grid grid = new Grid(exampleDataSource());
+grid.setSelectionMode(SelectionMode.MULTI);
+
+// Pre-select some items
+MultiSelectionModel selection =
+ (MultiSelectionModel) grid.getSelectionModel();
+selection.setSelected( // Items 2-4
+ grid.getContainerDataSource().getItemIds(2, 3));
+
+----
+
+The current selection can be read with [methodname]#getSelectedRows()#; either
+in the [classname]#MultiSelectionMode# object or in the [classname]#Grid#.
+
+
+[source, java]
+----
+// Allow deleting the selected items
+Button delSelected = new Button("Delete Selected", e -> {
+ // Delete all selected data items
+ for (Object itemId: selection.getSelectedRows())
+ grid.getContainerDataSource().removeItem(itemId);
+
+ // Otherwise out of sync with container
+ grid.getSelectionModel().reset();
+
+ // Disable after deleting
+ e.getButton().setEnabled(false);
+});
+delSelected.setEnabled(grid.getSelectedRows().size() > 0);
+----
+
+Changes in the selection can be handled with a
+[interfacename]#SelectionListener#. The selection event object provides
+[methodname]#getAdded()# and [methodname]#getRemoved()# to allow determining the
+differences in the selection change.
+
+
+[source, java]
+----
+// Handle selection changes
+grid.addSelectionListener(selection -> { // Java 8
+ Notification.show(selection.getAdded().size() +
+ " items added, " +
+ selection.getRemoved().size() +
+ " removed.");
+
+ // Allow deleting only if there's any selected
+ deleteSelected.setEnabled(
+ grid.getSelectedRows().size() > 0);
+});
+----
+
+
+[[components.grid.selection.clicks]]
+=== Focus and Clicks
+
+In addition to selecting rows, you can focus individual cells. The focus can be
+moved with arrow keys and, if editing is enabled, pressing kbd:[Enter] opens the
+editor. Normally, pressing kbd:[Tab] or kbd:[Shift+Tab] moves the focus to another component,
+as usual.
+
+When editing or in unbuffered mode, kbd:[Tab] or kbd:[Shift+Tab] moves the focus to the next or
+previous cell. The focus moves from the last cell of a row forward to the
+beginning of the next row, and likewise, from the first cell backward to the
+end of the previous row. Note that you can extend [classname]#DefaultEditorEventHandler#
+to change this behavior.
+
+With the mouse, you can focus a cell by clicking on it. The clicks can be handled
+with an [interfacename]#ItemClickListener#. The [classname]#ItemClickEvent#
+object contains various information, most importantly the ID of the clicked row
+and column.
+
+
+[source, java]
+----
+grid.addItemClickListener(event -> // Java 8
+ Notification.show("Value: " +
+ container.getContainerProperty(event.getItemId(),
+ event.getPropertyId()).getValue().toString()));
+----
+
+The clicked grid cell is also automatically focused.
+
+The focus indication is themed so that the focused cell has a visible focus
+indicator style by default, while the row doesn't. You can enable row focus, as
+well as disable cell focus, in a custom theme. See <<components.grid.css>>.
+
+
+
+[[components.grid.columns]]
+== Configuring Columns
+
+Columns are normally defined in the container data source. The
+[methodname]#addColumn()# method can be used to add columns to a container that
+supports it, such as the default [classname]#IndexedContainer#.
+
+Column configuration is defined in [classname]#Grid.Column# objects, which can
+be obtained from the grid with [methodname]#getColumn()# by the column
+(property) ID.
+
+
+[source, java]
+----
+Grid.Column bornColumn = grid.getColumn("born");
+bornColumn.setHeaderCaption("Born");
+----
+
+In the following, we describe the basic column configuration.
+
+[[components.grid.columns.order]]
+=== Column Order
+
+You can set the order of columns with [methodname]#setColumnOrder()# for the
+grid. Columns that are not given for the method are placed after the specified
+columns in their natural order.
+
+
+[source, java]
+----
+grid.setColumnOrder("firstname", "lastname", "born",
+ "birthplace", "died");
+----
+
+Note that the method can not be used to hide columns. You can hide columns with
+the [methodname]#removeColumn()#, as described later, or by hiding them in a
+[classname]#GeneratedPropertyContainer#.
+
+
+[[components.grid.columns.removing]]
+=== Hiding Columns
+
+Columns can be hidden by removing them with [methodname]#removeColumn()#. You
+can remove all columns with [methodname]#removeAllColumns()#. The removed columns
+are only removed from the grid, not from the container data source.
+
+To restore a previously removed column, you can call [methodname]#addColumn()#
+with the property ID. Instead of actually adding another column to the data
+source, it merely restores the previously removed one. However, column settings
+such as header or editor are not restored, but must be redone.
+
+You can also hide columns at container-level. At least
+[classname]#GeneratedpropertyContainer# allows doing so, as described in
+<<dummy/../../../framework/datamodel/datamodel-container#datamodel.container.gpc,"GeneratedPropertyContainer">>.
+
+
+[[components.grid.columns.captions]]
+=== Column Captions
+
+Column captions are displayed in the grid header. The default captions are
+generated automatically from the property ID. You can set the header caption
+explicitly through the column object with [methodname]#setHeaderCaption()#.
+
+
+[source, java]
+----
+Grid.Column bornColumn = grid.getColumn("born");
+bornColumn.setHeaderCaption("Born");
+----
+
+This is equivalent to setting it with [methodname]#setText()# for the header
+cell; the [classname]#HeaderCell# also allows setting the caption in HTML or as
+a component, as well as styling it, as described later in
+<<components.grid.headerfooter>>.
+
+
+[[components.grid.columns.width]]
+=== Column Widths
+
+Columns have by default undefined width, which causes automatic sizing based on
+the widths of the displayed data. You can set column widths explicitly by pixel
+value with [methodname]#setWidth()#, or relatively using expand ratios with
+[methodname]#setExpandRatio()#.
+
+When using expand ratios, the columns with a non-zero expand ratio use the extra
+space remaining from other columns, in proportion to the defined ratios.
+
+You can specify minimum and maximum widths for the expanding columns with
+[methodname]#setMinimumWidth()# and [methodname]#setMaximumWidth()#,
+respectively.
+
+The user can resize columns by dragging their separators with the mouse. When resized manually,
+all the columns widths are set to explicit pixel values, even if they had
+relative values before.
+
+[[components.grid.columns.frozen]]
+=== Frozen Columns
+
+You can set the number of columns to be frozen with
+[methodname]#setFrozenColumnCount()#, so that they are not scrolled off when
+scrolling horizontally.
+
+
+[source, java]
+----
+grid.setFrozenColumnCount(2);
+----
+
+Setting the count to [parameter]#0# disables frozen data columns; setting it to
+[parameter]#-1# also disables the selection column in multi-selection mode.
+
+
+
+[[components.grid.generatedcolumns]]
+== Generating Columns
+
+Columns with values computed from other columns or in some other way can be
+generated with a container or data model that generates the property values. The
+[classname]#GeneratedPropertyContainer# can be used for this purpose. It wraps
+around any indexed container to extend its properties with read-only generated
+properties. The generated properties can have same IDs as the original ones,
+thereby replacing them with formatted or converted values. See
+<<dummy/../../../framework/datamodel/datamodel-container#datamodel.container.gpc,"GeneratedPropertyContainer">>
+for a detailed description of using it.
+
+Generated columns are read-only, so you can not add grid rows with
+[methodname]#addRow()#. In editable mode, editor fields are not generated for
+generated columns.
+
+Note that, while [classname]#GeneratedPropertyContainer# implements
+[interfacename]#Container.Sortable#, the wrapped container might not, and also
+sorting on the generated properties requires special handling. In such cases,
+generated properties or the entire container might not actually be sortable.
+
+
+[[components.grid.renderer]]
+== Column Renderers
+
+A __renderer__ is a feature that draws the client-side representation of a data
+value. This allows having images, HTML, and buttons in grid cells.
+
+[[figure.components.grid.renderer]]
+.Column Renderers: Image, Date, HTML, and Button
+image::img/grid-renderers.png[]
+
+Renderers implement the [interfacename]#Renderer# interface. You set the column
+renderer in the [classname]#Grid.Column# object as follows:
+
+
+[source, java]
+----
+grid.addColumn("born", Integer.class);
+...
+Grid.Column bornColumn = grid.getColumn("born");
+bornColumn.setRenderer(new NumberRenderer("born in %d AD"));
+----
+
+Renderers require a specific data type for the column. To convert to a property
+type to a type required by a renderer, you can pass an optional
+[interfacename]#Converter# to [methodname]#setRenderer()#, as described later in
+this section. A converter can also be used to (pre)format the property values.
+The converter is run on the server-side, before sending the values to the
+client-side to be rendered with the renderer.
+
+The following renderers are available, as defined in the server-side
+[package]#com.vaadin.ui.renderers# package:
+
+[classname]#ButtonRenderer#:: Renders the data value as the caption of a button. A
+[interfacename]#RendererClickListener# can be given to handle the button clicks.
+
+ifdef::web[]
+Typically, a button renderer is used to display buttons for operating on a data
+item, such as edit, view, delete, etc. It is not meaningful to store the button
+captions in the data source, rather you want to generate them, and they are
+usually all identical.
+
+
++
+[source, java]
+----
+BeanItemContainer<Person> people =
+ new BeanItemContainer<>(Person.class);
+
+people.addBean(new Person("Nicolaus Copernicus", 1473));
+people.addBean(new Person("Galileo Galilei", 1564));
+people.addBean(new Person("Johannes Kepler", 1571));
+
+// Generate button caption column
+GeneratedPropertyContainer gpc =
+ new GeneratedPropertyContainer(people);
+gpc.addGeneratedProperty("delete",
+ new PropertyValueGenerator<String>() {
+
+ @Override
+ public String getValue(Item item, Object itemId,
+ Object propertyId) {
+ return "Delete"; // The caption
+ }
+
+ @Override
+ public Class<String> getType() {
+ return String.class;
+ }
+});
+
+// Create a grid
+Grid grid = new Grid(gpc);
+
+// Render a button that deletes the data row (item)
+grid.getColumn("delete")
+ .setRenderer(new ButtonRenderer(e -> // Java 8
+ grid.getContainerDataSource()
+ .removeItem(e.getItemId())));
+----
+endif::web[]
+[classname]#ImageRenderer#:: Renders the cell as an image. The column type must be a
+[interfacename]#Resource#, as described in
+<<dummy/../../../framework/application/application-resources#application.resources,"Images
+and Other Resources">>; only [classname]#ThemeResource# and
+[classname]#ExternalResource# are currently supported for images in
+[classname]#Grid#.
+
+ifdef::web[]
+
+[source, java]
+----
+grid.addColumn("picture", Resource.class)
+ .setRenderer(new ImageRenderer());
+...
+// Add some data rows
+grid.addRow(new ThemeResource("img/copernicus-128px.jpg"),
+ "Nicolaus Copernicus", 1543);
+grid.addRow(new ThemeResource("img/galileo-128px.jpg"),
+ "Galileo Galilei", 1564);
+----
+
++
+Instead of creating the resource objects explicitly, as was done above, you
+could generate them dynamically from file name strings using a
+[interfacename]#Converter# for the column.
+
+
++
+[source, java]
+----
+// Define some columns
+grid.addColumn("picture", String.class); // Filename
+grid.addColumn("name", String.class);
+
+// Set the image renderer
+grid.getColumn("picture").setRenderer(new ImageRenderer(),
+ new Converter<Resource, String>() {
+ @Override
+ public String convertToModel(Resource value,
+ Class<? extends String> targetType, Locale l)
+ throws Converter.ConversionException {
+ return "not needed";
+ }
+
+ @Override
+ public Resource convertToPresentation(String value,
+ Class<? extends Resource> targetType, Locale l)
+ throws Converter.ConversionException {
+ return new ThemeResource("img/" + value);
+ }
+
+ @Override
+ public Class<String> getModelType() {
+ return String.class;
+ }
+
+ @Override
+ public Class<Resource> getPresentationType() {
+ return Resource.class;
+ }
+});
+
+// Add some data rows
+grid.addRow("copernicus-128px.jpg", "Nicolaus Copernicus");
+grid.addRow("galileo-128px.jpg", "Galileo Galilei");
+grid.addRow("kepler-128px.jpg", "Johannes Kepler");
+----
++
+You also need to define the row heights so that the images fit there. You can
+set it in the theme for all data cells or for the column containing the images.
+
++
+For the latter way, first define a CSS style name for grid and the column:
+
+
++
+[source, java]
+----
+grid.setStyleName("gridwithpics128px");
+grid.setCellStyleGenerator(cell ->
+ "picture".equals(cell.getPropertyId())?
+ "imagecol" : null);
+----
+ifdef::web[]
++
+Then, define the style in CSS (Sass):
+endif::web[]
+
+
++
+[source, css]
+----
+.gridwithpics128px .imagecol {
+ height: 128px;
+ background: black;
+ text-align: center;
+}
+----
+endif::web[]
+[classname]#DateRenderer#:: Formats a column with a [classname]#Date# type using string formatter. The
+format string is same as for [methodname]#String.format()# in Java API. The date
+is passed in the parameter index 1, which can be omitted if there is only one
+format specifier, such as " [literal]#++%tF++#".
+
+ifdef::web[]
+
+[source, java]
+----
+Grid.Column bornColumn = grid.getColumn("born");
+bornColumn.setRenderer(
+ new DateRenderer("%1$tB %1$te, %1$tY",
+ Locale.ENGLISH));
+----
+
++
+Optionally, a locale can be given. Otherwise, the default locale (in the
+component tree) is used.
+
+endif::web[]
+[classname]#HTMLRenderer#:: Renders the cell as HTML. This allows formatting cell content, as well as using
+HTML features such as hyperlinks.
+
+ifdef::web[]
+First, set the renderer in the [classname]#Grid.Column# object:
+
+
++
+[source, java]
+----
+grid.addColumn("link", String.class)
+ .setRenderer(new HtmlRenderer());
+----
+ifdef::web[]
++
+Then, in the grid data, give the cell content:
+endif::web[]
+
+
++
+[source, java]
+----
+grid.addRow("Nicolaus Copernicus", 1543,
+ "<a href='http://en.wikipedia.org/wiki/" +
+ "Nicolaus_Copernicus' target='_top'>info</a>");
+----
++
+You could also use a [interfacename]#PropertyFormatter# or a generated column to
+generate the HTML for the links.
+
+endif::web[]
+[classname]#NumberRenderer#:: Formats column values with a numeric type extending [classname]#Number#:
+[classname]#Integer#, [classname]#Double#, etc. The format can be specified
+either by the subclasses of [classname]#java.text.NumberFormat#, namely
+[classname]#DecimalFormat# and [classname]#ChoiceFormat#, or by
+[methodname]#String.format()#.
+
+ifdef::web[]
+For example:
+
+
++
+[source, java]
+----
+// Define some columns
+grid.addColumn("name", String.class);
+grid.addColumn("born", Integer.class);
+grid.addColumn("sletters", Integer.class);
+grid.addColumn("rating", Double.class);
+
+// Use decimal format
+grid.getColumn("born").setRenderer(new NumberRenderer(
+ new DecimalFormat("in #### AD")));
+
+// Use textual formatting on numeric ranges
+grid.getColumn("sletters").setRenderer(new NumberRenderer(
+ new ChoiceFormat("0#none|1#one|2#multiple")));
+
+// Use String.format() formatting
+grid.getColumn("rating").setRenderer(new NumberRenderer(
+ "%02.4f", Locale.ENGLISH));
+
+// Add some data rows
+grid.addRow("Nicolaus Copernicus", 1473, 2, 0.4);
+grid.addRow("Galileo Galilei", 1564, 0, 4.2);
+grid.addRow("Johannes Kepler", 1571, 1, 2.3);
+----
+endif::web[]
+[classname]#ProgressBarRenderer#:: Renders a progress bar in a column with a [classname]#Double# type. The value
+must be between 0.0 and 1.0.
+
+ifdef::web[]
+For example:
+
+
++
+[source, java]
+----
+// Define some columns
+grid.addColumn("name", String.class);
+grid.addColumn("rating", Double.class)
+ .setRenderer(new ProgressBarRenderer());
+
+// Add some data rows
+grid.addRow("Nicolaus Copernicus", 0.1);
+grid.addRow("Galileo Galilei", 0.42);
+grid.addRow("Johannes Kepler", 1.0);
+----
+endif::web[]
+[classname]#TextRenderer#:: Displays plain text as is. Any HTML markup is quoted.
+
+
+
+[[components.grid.renderer.custom]]
+=== Custom Renderers
+
+Renderers are component extensions that require a client-side counterpart. See
+<<dummy/../../../framework/clientsidewidgets/clientsidewidgets-grid#clientsidewidgets.grid.renderers,"Renderers">>
+for information on implementing custom renderers.
+
+
+[[components.grid.renderer.converter]]
+=== Converting for Rendering
+
+Optionally, you can give a [interfacename]#Converter# in the
+[methodname]#setRenderer()#, or define it for the column, to convert the data
+value to an intermediary representation that is rendered by the renderer. For
+example, when using an [classname]#ImageRenderer#, you could store the image file name
+in the data column, which the converter would convert to a resource, which would
+then be rendered by the renderer.
+
+In the following example, we use a converter and [classname]#HTMLRenderer# to display boolean
+values as [classname]#FontAwesome# icons
+[source, java]
+----
+// Have a column for hyperlink paths to Wikipedia
+grid.addColumn("truth", Boolean.class);
+Grid.Column truth = grid.getColumn("truth");
+truth.setRenderer(new HtmlRenderer(),
+ new StringToBooleanConverter(
+ FontAwesome.CHECK_CIRCLE_O.getHtml(),
+ FontAwesome.CIRCLE_O.getHtml()));
+...
+----
+
+In this example, we use a converter to format URL paths to complete
+HTML hyperlinks with [classname]#HTMLRenderer#:
+
+
+[source, java]
+----
+// Have a column for hyperlink paths to Wikipedia
+grid.addColumn("link", String.class);
+
+Grid.Column linkColumn = grid.getColumn("link");
+linkColumn.setRenderer(new HtmlRenderer(),
+ new Converter<String,String>(){
+ @Override
+ public String convertToModel(String value,
+ Class<? extends String> targetType, Locale locale)
+ throws Converter.ConversionException {
+ return "not implemented";
+ }
+
+ @Override
+ public String convertToPresentation(String value,
+ Class<? extends String> targetType, Locale locale)
+ throws Converter.ConversionException {
+ return "<a href='http://en.wikipedia.org/wiki/" +
+ value + "' target='_blank'>more info</a>";
+ }
+
+ @Override
+ public Class<String> getModelType() {
+ return String.class;
+ }
+
+ @Override
+ public Class<String> getPresentationType() {
+ return String.class;
+ }
+});
+
+// Data with a hyperlink path in the third column
+grid.addRow("Nicolaus Copernicus", 1473,
+ "Nicolaus_Copernicus");
+...
+----
+
+A [classname]#GeneratedPropertyContainer# could be used for much the same
+purpose.
+
+
+
+[[components.grid.headerfooter]]
+== Header and Footer
+
+A grid by default has a header, which displays column names, and can have a
+footer. Both can have multiple rows and neighbouring header row cells can be
+joined to feature column groups.
+
+[[components.grid.headerfooter.adding]]
+=== Adding and Removing Header and Footer Rows
+
+A new header row is added with [methodname]#prependHeaderRow()#, which adds it
+at the top of the header, [methodname]#appendHeaderRow()#, which adds it at the
+bottom of the header, or with [methodname]#addHeaderRowAt()#, which inserts it
+at the specified 0-base index. All of the methods return a
+[classname]#HeaderRow# object, which you can use to work on the header further.
+
+
+[source, java]
+----
+// Group headers by joining the cells
+HeaderRow groupingHeader = grid.prependHeaderRow();
+...
+
+// Create a header row to hold column filters
+HeaderRow filterRow = grid.appendHeaderRow();
+...
+----
+
+Similarly, you can add footer rows with [methodname]#appendFooterRow()#,
+[methodname]#prependFooterRow()#, and [methodname]#addFooterRowAt()#.
+
+
+[[components.grid.headerfooter.joining]]
+=== Joining Header and Footer Cells
+
+You can join two or more header or footer cells with the [methodname]#join()#
+method. For header cells, the intention is usually to create column grouping,
+while for footer cells, you typically calculate sums or averages.
+
+
+[source, java]
+----
+// Group headers by joining the cells
+HeaderRow groupingHeader = grid.prependHeaderRow();
+HeaderCell namesCell = groupingHeader.join(
+ groupingHeader.getCell("firstname"),
+ groupingHeader.getCell("lastname")).setText("Person");
+HeaderCell yearsCell = groupingHeader.join(
+ groupingHeader.getCell("born"),
+ groupingHeader.getCell("died"),
+ groupingHeader.getCell("lived")).setText("Dates of Life");
+----
+
+
+[[components.grid.headerfooter.content]]
+=== Text and HTML Content
+
+You can set the header caption with [methodname]#setText()#, in which case any
+HTML formatting characters are quoted to ensure security.
+
+
+[source, java]
+----
+HeaderRow mainHeader = grid.getDefaultHeaderRow();
+mainHeader.getCell("firstname").setText("First Name");
+mainHeader.getCell("lastname").setText("Last Name");
+mainHeader.getCell("born").setText("Born In");
+mainHeader.getCell("died").setText("Died In");
+mainHeader.getCell("lived").setText("Lived For");
+----
+
+To use raw HTML in the captions, you can use [methodname]#setHtml()#.
+
+
+[source, java]
+----
+namesCell.setHtml("<b>Names</b>");
+yearsCell.setHtml("<b>Years</b>");
+----
+
+
+[[components.grid.headerfooter.components]]
+=== Components in Header or Footer
+
+You can set a component in a header or footer cell with
+[methodname]#setComponent()#. Often, this feature is used to allow filtering, as
+described in <<components.grid.filtering>>, which also gives an example of the
+use.
+
+
+
+[[components.grid.filtering]]
+== Filtering
+
+The ability to include components in the grid header can be used to create
+filters for the grid data. Filtering is done in the container data source, so
+the container must be of type that implements
+[interfacename]#Container.Filterable#.
+
+[[figure.components.grid.filtering]]
+.Filtering Grid
+image::img/grid-filtering.png[]
+
+The filtering illustrated in <<figure.components.grid.filtering>> can be created
+as follows:
+
+
+[source, java]
+----
+// Have a filterable container
+IndexedContainer container = exampleDataSource();
+
+// Create a grid bound to it
+Grid grid = new Grid(container);
+grid.setSelectionMode(SelectionMode.NONE);
+grid.setWidth("500px");
+grid.setHeight("300px");
+
+// Create a header row to hold column filters
+HeaderRow filterRow = grid.appendHeaderRow();
+
+// Set up a filter for all columns
+for (Object pid: grid.getContainerDataSource()
+ .getContainerPropertyIds()) {
+ HeaderCell cell = filterRow.getCell(pid);
+
+ // Have an input field to use for filter
+ TextField filterField = new TextField();
+ filterField.setColumns(8);
+
+ // Update filter When the filter input is changed
+ filterField.addTextChangeListener(change -> {
+ // Can't modify filters so need to replace
+ container.removeContainerFilters(pid);
+
+ // (Re)create the filter if necessary
+ if (! change.getText().isEmpty())
+ container.addContainerFilter(
+ new SimpleStringFilter(pid,
+ change.getText(), true, false));
+ });
+ cell.setComponent(filterField);
+}
+----
+
+
+[[components.grid.sorting]]
+== Sorting
+
+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 other column headers while keeping the Shift key pressed adds a
+secondary or more sort criteria.
+
+[[figure.components.grid.sorting]]
+.Sorting Grid on Multiple Columns
+image::img/grid-sorting.png[]
+
+Defining sort criteria programmatically can be done with the various
+alternatives of the [methodname]#sort()# method. You can sort on a specific
+column with [methodname]#sort(Object propertyId)#, which defaults to ascending
+sorting order, or [methodname]#sort(Object propertyId, SortDirection
+direction)#, which allows specifying the sort direction.
+
+
+[source, java]
+----
+grid.sort("name", SortDirection.DESCENDING);
+----
+
+To sort on multiple columns, you need to use the fluid sort API with
+[methodname]#sort(Sort)#, which allows chaining sorting rules. Sorting rules are
+created with the static [methodname]#by()# method, which defines the primary
+sort column, and [methodname]#then()#, which can be used to specify any
+secondary sort columns. They default to ascending sort order, but the sort
+direction can be given with an optional parameter.
+
+
+[source, java]
+----
+// Sort first by city and then by name
+grid.sort(Sort.by("city", SortDirection.ASCENDING)
+ .then("name", SortDirection.DESCENDING));
+----
+
+The container data source must support sorting. At least, it must implement
+[interfacename]#Container.Sortable#. Note that when using
+[classname]#GeneratedPropertyContainer#, as described in
+<<components.grid.generatedcolumns>>, even though the container implements the
+interface, the wrapped container must also support it. Also, the generated
+properties are not normally sortable, but require special handling to enable
+sorting.
+
+
+[[components.grid.editing]]
+== Editing
+
+Grid supports line-based editing, where double-clicking a row opens the row
+editor. In the editor, the input fields can be edited, as well as navigated with
+kbd:[Tab] and kbd:[Shift+Tab] keys. If validation fails, an error is displayed and the user
+can correct the inputs.
+
+To enable editing, you need to call [methodname]#setEditorEnabled(true)# for the
+grid.
+
+
+[source, java]
+----
+Grid grid = new Grid(GridExample.exampleDataSource());
+grid.setEditorEnabled(true);
+----
+
+Grid supports two row editor modes - buffered and unbuffered. The default mode is
+buffered. The mode can be changed with [methodname]#setBuffered(false)#
+
+[[components.grid.editing.buffered]]
+=== Buffered Mode
+
+The editor has a [guibutton]#Save# button that commits
+the data item to the container data source and closes the editor. The
+[guibutton]#Cancel# button discards the changes and exits the editor.
+
+A row under editing is illustrated in <<figure.components.grid.editing>>.
+
+[[figure.components.grid.editing]]
+.Editing a Grid Row
+image::img/grid-editor-basic.png[]
+
+[[components.grid.editing.unbuffered]]
+=== Unbuffered Mode
+
+The editor has no buttons and all changed data is committed directly
+to the container. If another row is clicked, the editor for the current row is closed and
+a row editor for the clicked row is opened.
+
+[[components.grid.editing.fields]]
+=== Editor Fields
+
+The editor fields are by default generated with a [interfacename]#FieldFactory#
+and bound to the container data source with a [classname]#FieldGroup#, which
+also handles tasks such as validation, as explained later.
+
+To disable editing in a particular column, you can call
+[methodname]#setEditable()# in the [classname]#Column# object with
+[parameter]#false# parameter.
+
+[[components.grid.editing.editorfields]]
+=== Customizing Editor Fields
+
+The editor fields are normally created by the field factory of the editor's field
+group, which creates the fields according to the data types of their respective
+columns. To customize the editor fields of specific properties, such as to style
+them or to set up validation, you can provide them with
+[methodname]#setEditorField()# in the respective columns.
+
+In the following example, we configure a field with validation and styling:
+
+
+[source, java]
+----
+TextField nameEditor = new TextField();
+
+// Custom CSS style
+nameEditor.addStyleName("nameeditor");
+
+// Custom validation
+nameEditor.addValidator(new RegexpValidator(
+ "^\\p{Alpha}+ \\p{Alpha}+$",
+ "Need first and last name"));
+
+grid.getColumn("name").setEditorField(nameEditor);
+----
+
+Setting an editor field to [parameter]#null# deletes the currently existing
+editor field, whether it was automatically generated or set explicitly with the
+setter. It will be regenerated with the factory the next time it is needed.
+
+
+ifdef::web[]
+[[components.grid.editing.captions]]
+=== Customizing Editor Buttons
+
+In the buffered mode, the editor has two buttons: [guibutton]#Save# and [guibutton]#Cancel#. You can
+set their captions with [methodname]#setEditorSaveCaption()# and
+[methodname]#setEditorCancelCaption()#, respectively.
+
+In the following example, we demonstrate one way to translate the captions:
+
+
+[source, java]
+----
+// Captions are stored in a resource bundle
+ResourceBundle bundle = ResourceBundle.getBundle(
+ MyAppCaptions.class.getName(),
+ Locale.forLanguageTag("fi")); // Finnish
+
+// Localize the editor button captions
+grid.setEditorSaveCaption(
+ bundle.getString(MyAppCaptions.SaveKey));
+grid.setEditorCancelCaption(
+ bundle.getString(MyAppCaptions.CancelKey));
+----
+
+endif::web[]
+
+[[components.grid.editing.fieldgroup]]
+=== Binding to Data with a Field Group
+
+Data binding to the item under editing is handled with a
+[classname]#FieldGroup#, which you need to set with
+[methodname]#setEditorFieldGroup#. This is mostly useful when using
+special-purpose field groups, such as [classname]#BeanFieldGroup# to enable bean
+validation.
+
+For example, assuming that we want to enable bean validation for a bean such as
+the following:
+
+
+[source, java]
+----
+public class Person implements Serializable {
+ @NotNull
+ @Size(min=2, max=10)
+ private String name;
+
+ @Min(1)
+ @Max(130)
+ private int age;
+ ...]
+----
+
+We can now use a [classname]#BeanFieldGroup# in the [classname]#Grid# as
+follows:
+
+
+[source, java]
+----
+Grid grid = new Grid(exampleBeanDataSource());
+grid.setColumnOrder("name", "age");
+grid.setEditorEnabled(true);
+
+// Enable bean validation for the data
+grid.setEditorFieldGroup(
+ new BeanFieldGroup<Person>(Person.class));
+
+// Have some extra validation in a field
+TextField nameEditor = new TextField();
+nameEditor.addValidator(new RegexpValidator(
+ "^\\p{Alpha}+ \\p{Alpha}+$",
+ "Need first and last name"));
+grid.setEditorField("name", nameEditor);
+----
+
+To use bean validation as in the example above, you need to include an
+implementation of the Bean Validation API in the classpath, as described in
+<<dummy/../../../framework/datamodel/datamodel-itembinding#datamodel.itembinding.beanvalidation,"Bean
+Validation">>.
+
+
+ifdef::web[]
+[[components.grid.editing.validation]]
+=== Handling Validation Errors
+
+The input fields are validated when the value is updated. The default
+error handler displays error indicators in the invalid fields, as well as the
+first error in the editor.
+
+[[figure.components.grid.errors]]
+.Editing a Grid Row
+image::img/grid-editor-errors.png[]
+
+You can modify the error handling by implementing a custom
+[interfacename]#EditorErrorHandler# or by extending the
+[classname]#DefaultEditorErrorHandler#.
+
+endif::web[]
+
+[[components.grid.editing.fieldfactory]]
+=== Editor Field Factory
+
+The fields are generated by the [classname]#FieldFactory# of the field group;
+you can also set it with [methodname]#setEditorFieldFactory()#. Alternatively,
+you can create the editor fields explicitly with [methodname]#setEditorField()#.
+
+[[components.grid.scrolling]]
+== Programmatic Scrolling
+
+You can scroll to first item with [methodname]#scrollToStart()#, to end with
+[methodname]#scrollToEnd()#, or to a specific row with [methodname]#scrollTo()#.
+
+
+[[components.grid.stylegeneration]]
+== Generating Row or Cell Styles
+
+You can style entire rows with a [interfacename]#RowStyleGenerator# or
+individual cells with a [interfacename]#CellStyleGenerator#.
+
+[[components.grid.stylegeneration.row]]
+=== Generating Row Styles
+
+You set a [interfacename]#RowStyleGenerator# to a grid with
+[methodname]#setRowStyleGenerator()#. The [methodname]#getStyle()# method gets a
+[classname]#RowReference#, which contains various information about the row and
+a reference to the grid, and should return a style name or [parameter]#null# if
+no style is generated.
+
+For example, to add a style names to rows having certain values in one column,
+you can style them as follows:
+
+
+[source, java]
+----
+grid.setRowStyleGenerator(rowRef -> {// Java 8
+ if (! ((Boolean) rowRef.getItem()
+ .getItemProperty("alive")
+ .getValue()).booleanValue())
+ return "grayed";
+ else
+ return null;
+});
+----
+
+You could then style the rows with CSS as follows:
+
+
+[source, css]
+----
+.v-grid-row.grayed {
+ color: gray;
+}
+----
+
+
+[[components.grid.stylegeneration.cell]]
+=== Generating Cell Styles
+
+You set a [interfacename]#CellStyleGenerator# to a grid with
+[methodname]#setCellStyleGenerator()#. The [methodname]#getStyle()# method gets
+a [classname]#CellReference#, which contains various information about the cell
+and a reference to the grid, and should return a style name or [parameter]#null#
+if no style is generated.
+
+For example, to add a style name to a specific column, you can match on the
+property ID of the column as follows:
+
+
+[source, java]
+----
+grid.setCellStyleGenerator(cellRef -> // Java 8
+ "born".equals(cellRef.getPropertyId())?
+ "rightalign" : null);
+----
+
+You could then style the cells with a CSS rule as follows:
+
+
+[source, css]
+----
+.v-grid-cell.rightalign {
+ text-align: right;
+}
+----
+
+
+
+[[components.grid.css]]
+== Styling with CSS
+
+
+[source, css]
+----
+.v-grid {
+ .v-grid-scroller, .v-grid-scroller-horizontal { }
+ .v-grid-tablewrapper {
+ .v-grid-header {
+ .v-grid-row {
+ .v-grid-cell, .frozen, .v-grid-cell-focused { }
+ }
+ }
+ .v-grid-body {
+ .v-grid-row,
+ .v-grid-row-stripe,
+ .v-grid-row-has-data {
+ .v-grid-cell, .frozen, .v-grid-cell-focused { }
+ }
+ }
+ .v-grid-footer {
+ .v-grid-row {
+ .v-grid-cell, .frozen, .v-grid-cell-focused { }
+ }
+ }
+ }
+ .v-grid-header-deco { }
+ .v-grid-footer-deco { }
+ .v-grid-horizontal-scrollbar-deco { }
+ .v-grid-editor {
+ .v-grid-editor-cells { }
+ .v-grid-editor-footer {
+ .v-grid-editor-message { }
+ .v-grid-editor-buttons {
+ .v-grid-editor-save { }
+ .v-grid-editor-cancel { }
+ }
+ }
+ }
+}
+----
+
+A [classname]#Grid# has an overall [literal]#++v-grid++# style. The actual grid
+has three parts: a header, a body, and a footer. The scrollbar is a custom
+element with [literal]#++v-grid-scroller++# style. In addition, there are some
+decoration elements.
+
+Grid cells, whether thay are in the header, body, or footer, have a basic
+[literal]#++v-grid-cell++# style. Cells in a frozen column additionally have a
+[literal]#++frozen++# style. Rows have [literal]#++v-grid-row++# style, and
+every other row has additionally a [literal]#++v-grid-row-stripe++# style.
+
+The focused row has additionally [literal]#++v-grid-row-focused++# style and
+focused cell [literal]#++v-grid-cell-focused++#. By default, cell focus is
+visible, with the border stylable with [parameter]#$v-grid-cell-focused-border#
+parameter in Sass. Row focus has no visible styling, but can be made visible
+with the [parameter]#$v-grid-row-focused-background-color# parameter or with a
+custom style rule.
+
+In editing mode, a [literal]#++v-grid-editor++# overlay is placed on the row
+under editing. In addition to the editor field cells, it has an error message
+element, as well as the buttons.
+
+
+((()))
+
+