vwc-data-grid
component provides a tabular data view.
As in opposite to a simple HTML table, this component is performance oriented, capable of handling large amounts of data efficiently and fully featured from both, API and UX/UI perspective.
Highlights:
- static (all upfront) and dynamic (chunks on demand) data provisioning
- sorting / filtering
- tree view (group view)
- expanded row view
- rows selection (one / all / partial)
- columns management
- hiding
- reordering
- resizing
- freezing
- customization
- columns management features opt in/out
- header custom rendering
- footer custom rendering
- cell custom rendering
- full row (aka row details, aka expanded row) custom rendering
Grid API may roughly split into 2 categories:
- configuration / customization
- data
Additionally there is a set of APIs of more general nature, like Events API.
More formal description of these APIs found below in the Grid Configuration / Customization / Management section.
There are 2 ways to supply data to grid, via the following grid component properties:
-
items: unknown[]
- simplest
- all data upfront
- suitable for small to medium amounts of data (in terms of memory occupation)
-
dataProvider: (params: { page: number, pageSize: number }, callback: (pageItems: unknown[], totalItems: number) => void) => void
- stream of chunks, on demand
- should be used when memory usage concern present (from the data perspective)
- should be used when pulling data from the backend on the fly
In case of collision between those 2 (both set to something contentful), error will be printed to the console and
dataProvider
will take precedence.
Some changes to the data, eg 'deep' change within items
or logical conditions that dataProvider
works with, won't trigger the refresh of the grid.
Use API below to refresh the data:
-
refreshData(): void
- will re-render the visible data in the grid
Each new assignment to grid's items
property will refresh the grid's content.
Array manipulation (eg grid.items.splice(0, 1)
) as well is 'deep' data mutations wont't trigger grid update.
In such a cases you need to trigger data refresh on demand, via refreshData
API.
Grid will call dataProvider
each time new chunk of data needed.
First argument will hold all needed chunk params, page number, page size etc.
Second argument is the grid's own callback to be called with the fetched / prepared data.
Similarly to the said above, in case the internal conditions changing and you'd like to refresh the data in the grid, call refreshData
API.
There are few APIs to manage items selection:
-
selectedItems: unknown[]
- an Array of selected items (item references taken from the
items
or those provided bydataProvider
)
- an Array of selected items (item references taken from the
-
selectItem(item: unknown, singleSelectMode: boolean = false): void
- will add the item to the selected ones (and reflect it in UI if selector column used, or any custom UI that reflects selection)
- if the
singleSelectMode
switch set totrue
, the API will unselect all previously selected items and leave the provided item as the only selected one
-
unselectItem(item: unkown): void
- will remove item from the selected ones
-
selectAll(): void
- will add the item to the selected ones (and reflect it in UI if selector column used, or any custom UI that reflects selection); this method will throw if the data provisioning is done via
dataProvider
method
- will add the item to the selected ones (and reflect it in UI if selector column used, or any custom UI that reflects selection); this method will throw if the data provisioning is done via
-
unselectAll(): void
- will unselect all selected items
- event
selected-items-changed
will be fired on any selection change
In addition to the selection APIs, vwc-data-grid
provides an OOTB selector UI, column that:
- will auto render checkbox per row, this checkbox will add/remove corresponding item to/from selection
- will auto render checkbox header to perform select / deselect all
- this checkbox will reflect the current selection state, being in indeterminate state when some of the items selected
- 'select all' header won't be rendered when the selector column is said to work in
single
select mode - 'select all' header won't be renderer when the data provisioning method is via
dataProvider
Some of the grid's features are configured via column/s configuration, see below on that. Others are related to the grid as a whole, we'll describe them first.
Some of the properties of grid are also reflected via attributes. All those cases explicitly mention the attribute name in the table below. In those cases attribute and property may be used interchangeably.
Property | Attribute | Type | Default | Description |
---|---|---|---|---|
heightByRows |
boolean |
false |
sets grid block-size to its total rows block-size | |
multiSort |
multi-sort |
boolean |
false |
sorting by multiple columns |
reordering |
reordering |
boolean |
false |
columns reordering via UI (drag'n'drop') |
columns |
DataGridColumn[] |
[] |
columns definitions, the majority of grid configuration, see below more | |
rowDetailsRenderer |
RowDetailsRenderer |
undefined |
when provided, will handle an expanded / detailed row part rendering; see more details on RowDetailsRenderer below |
|
items |
unknown[] |
undefined |
see Data API above | |
dataProvider |
(params: { page: number, pageSize: number }, callback: (pageItems: unknown[], treeLevelSize: number) => void): void |
undefined |
see Data API above | |
selectedItems |
unknown[] |
[] |
see Data API above |
These are the methods available on grid component:
Method | Signature | Description |
---|---|---|
refreshConfiguration |
(): void |
refreshes configuration on demand, if needed; use when performing changes directly on the columns object (if the array reference remains the same) |
openItemDetails |
(item: unknown): void |
marks the row metadata as expanded and triggers rowDetailsRenderer , if available (see above) |
closeItemDetails |
(item: unknown): void |
unmarks the row as expanded and collapses the additional visual space provided for the details |
refreshData |
(): void |
triggers visible data refresh; useful when undetectable data changes happen ('deep' changs in items or dataProvider internal logical conditions) |
selectItem |
(item: unknown, singleSelectMode: boolean = false): void |
selects a given item, adding it to the selectedItems array; when singleSelectMode is true all previously selected items will be unselected |
deselectItem |
(item: unknown): void |
unselects a given item |
selectAll |
(): void |
select all items; this method will throw when the data provisioning is set to work with dataProvider
|
deselectAll |
(): void |
unselects all selected items |
Configuration of the grid columns is the major part of the whole grid setup. There are 2 ways to configure grid's columns:
- via HTML elements bearing columns definitions
- the configuration conveyed via
vwc-data-grid-column
component's properties / attributes - changes to the columns defitions back-reflected to the
columns
configuration object - add/remove columns is not supported
- direct changes of the
columns
configuration object are NOT further-reflected in the configurational elements
- the configuration conveyed via
- via configuration object manipulation by JavaScript and
columns
property of the grid component- direct Array manipulations on
columns
as well as deep changes to theDataGridColumn
won't result in auto update of the grid, do<grid>.refreshConfiguration()
(see API above)
- direct Array manipulations on
Note: both flavors adhere to the same configuration interface, namely
DataGridColumn
API (see below).
Note: durting the initial bootstrapping of a grid instance declarative (
vwc-data-grid-column
) approach will take precedence over thecolumns
object. Yet, furter changes to the latter and former in an unordered and random fashion may lead to unexpected configuration, which still being valid and fully functional might not reflect the desired state. As such it is better to stick to a chosen flavor along the way.
vwc-data-grid-column
component adheres to the DataGridColumn
interface, therefore effectively there is a single API definition regardless of which configuration flavor used.
Property | Attribute | Type | Default | Description |
---|---|---|---|---|
path |
path |
string |
'' |
path into the item to get the data for this column's cells |
tree |
tree |
boolean |
false |
makes column to behave as tree-able (open/close control, shifted value rendering etc) |
hidden |
hidden |
boolean |
false |
show/hide column |
frozen |
frozen |
boolean |
false |
freeze/unfreeze column (horizontal scroll pinning) |
sortable |
sortable |
boolean |
false |
shows sorting UI and provides OOTB sorting support |
filterable |
filterable |
boolean |
false |
shows filtering UI and provides OOTB filtering support |
resizable |
resizable |
boolean |
false |
controls column resizeability support (UI) |
selector |
selector |
`string: 'single' | 'multi'` | undefined |
autoWidth |
auto-width |
boolean |
false |
should the column to auto calculate and set it's own width (based on currently rendered content) |
width |
width |
string |
undefined |
sets static column width (width CSS value) |
header |
header |
string |
'' |
header label |
headerRenderer |
MetaRenderer |
undefined |
custom header rendering; see MetaRenderer details below |
|
footer |
footer |
string |
'' |
footer text |
footerRenderer |
MetaRenderer |
undefined |
custom footer rendering; see MetaRenderer details below |
|
cellRenderer |
DataRenderer |
undefined |
custom cell rendering; see DataRenderer details below |
MetaRenderer
is a functional interface, defining the signature of the method to be used as renderer of meta elements like headers and footers.
Signature:
(container: HTMLElement, configuration: CellRendererConfiguration): void
Each time grid component will require fresh render of the header/s and/or footer/s, it'll call the custom renderers, if provided.
See more details on CellRendererConfiguration
below.
Attention: the renderer MAY and WILL be called several times for the same container, it is your responsibility to enforce idempotency of its logic.
MetaRenderer
API is used internally for sorting header and selector column's header. You may see the implementation details correspondingly.
DataRenderer
is a functional interface, defining the signature of the method to be used as renderer of cell contents.
Signature:
(container: HTMLElement, configuration: CellRendererConfiguration, data: { item: unknown, selected: boolean }): void
Each time grid component will require fresh render of the cell/s content, it'll call the custom renderer, if provided.
See more details on CellRendererConfiguration
below.
Attention: the renderer MAY and WILL be called several times for the same container, it is your responsibility to enforce idempotency of its logic.
DataRenderer
API is used internally for selector column's cells. You may see the implementation details there.
RowDetailsRenderer
is a functional interface, defining the signature of the method to be used as renderer of the row details (expanded row view).
Signature:
(container: HTMLElement, configuration: RowRendererConfiguration, data: { item: unknown, selected: boolean }): void
CellRendererConfiguration
supplied to any cell-level custom renderer, namely: header, footer and cell custom renderers.
Interface:
/**
* `grid` component, the renderer is belonging to
*/
grid: DataGrid,
/**
* column configuration, the renderer is belonging to
*/
column: DataGridColumn
RowRendererConfiguration
supplied to any row-level custom renderer, namely: expanded row details custom renderer.
Interface:
/**
* `grid` component, the renderer is belonging to
*/
grid: DataGrid,
vwc-data-grid
provides a convenient API to handle events happening within the grid.
It is possible and convenient to register a listener for the relevant event on the grid level and then retrieve the actual context the event occured in
dataGrid.addEventListener('click', event => {
const _dataGrid = event.target;
const eventContext = _dataGrid.getEventContext(event);
// work with context as appropriate
});
getEventContext(event: Event): EventContext | null
The method will return an event contex or null
in case of non-relevant event.
/**
* row number, index, of the interacted row
*/
row: number,
/**
* actual data item, underlying interacted row
*/
item: unknown
Few examples of grid definition in several flavors.
All of them employ LitHtml
binding notation, which of course can be replaced by any other data-centric framework.
Note: the dot (
.
) leaded notaion reads 'assign the said object to the said property by reference'. For the sake of simplicity we'll assume that all of the top level objects are reachable from the current templating scope.
<vwc-data-grid
.columns="${columns}"
.items="${items}"
.rowDetailsRenderer="${expandedRowRenderer}">
</vwc-data-grid>
Now the JavaScript part, assuming running in the same scope as the template above:
const columns = [
{ header: 'First Name', path: 'fname' },
{ header: 'Last Name', path: 'lname', sortable: true },
{ header: 'Expand Row', autoWidth: true, cellRenderer: cellRenderer }
];
const data = [
{ fname: 'Max', lname: 'Weber' },
...
];
const cellRenderer = (container, column, data) {
container.innerHTML = ...
};
In order to customize columns at runtime use the following pattern:
const grid = document.querySelector('vwc-data-grid');
grid.columns[0].frozen = true;
grid.refreshConfiguration();
Attention: the
refreshConfiguration
invokation is required as of now, due to internals ofcolumns
data structure not being observed for a change. In any change of this in the future further notice will be provided.
<vwc-data-grid .items="${items}" .rowDetailsRenderer="${expandedRowRenderer}">
<vwc-data-grid-column header="First Name" path="fname"></vwc-data-grid-column>
<vwc-data-grid-column header="Last Name" path="lname" sortable></vwc-data-grid-column>
<vwc-data-grid-column header="Expand Row" auto-width .cellRenderer="${cellRenderer}"></vwc-data-grid-column>
</vwc-data-grid>
Now the JavaScript part, assuming running in the same scope as the template above:
const data = [
{ fname: 'Max', lname: 'Weber' },
...
];
const cellRenderer = (container, column, data) {
container.innerHTML = ...
};
Pay attention, how the columns data structure is not maintained in JS anymore.
Moreover, attributes like sortable
, auto-width
etc can be bound to some data structure managed by data-binding framework, thus removing the whole customization part out of the scripting scope.