gridy-grid
v2.1.2
Published
gridy grid datagrid web components library
Downloads
4
Maintainers
Readme
Gridy Grid
Web Components based data grid (table).
Features
- W3C browser standards and native modern technologies compilant
- Modular and flexible to use and extend for any complexity level
- Can be bootstrapped completely from markup as far as from js code (dynamic render)
- Skinnable and templatable UI, can mimic to framework of your choice
- Modern code style with no transpilations required, but you are still free to do that
- Options to have wide browser support (including legacy browsers like IE11)
If you are looking for web components based widgets collection check out skinny-widgets project.
Install
As for the moment you must install one of theme packages by your choice, e.g. gridy-grid-default, gridy-grid-antd or gridy-grid-jquery in addition to this package and specify the chosen theme as sk-config or gridy-grid element's attribute.
Prebuilt bundle can be found here: gridy-grid-bundle
npm i gridy-grid gridy-grid-jquery sk-theme-jquery --save
Bundles that are prebuilt js and html code for static plug can be found in gridy-grid-bundle package
Simple Usage
Let's see the example for the data loaded from json within javascript execution context.
<gridy-table base-path="/node_modules/gridy-grid/src" theme="jquery" id="gridyTable"></gridy-table>
<script type="module">
import { GridyTable } from '/node_modules/gridy-grid/src/table/gridy-table.js';
import { DataSourceLocal } from '/node_modules/gridy-grid/src/datasource/data-source-local.js';
let dataSource = new DataSourceLocal();
dataSource.fields = [
{ title: 'Title', path: '$.title' },
{ title: 'Price', path: '$.price' }
];
let data = [];
for (let i = 0; i < 10; i++) {
data.push({ title: 'row' + i, price: 100 * i })
}
// local datasource data load should be called explicitly
gridyTable.dataSource = dataSource;
customElements.define('gridy-table', GridyTable);
dataSource.loadData(data);
</script>
GridyGrid is the container element that simplifies components configuration
GridyGrid is the container element that simplifies components configuration and instantiation. Just wrap your components with it and bind DataSource instance or use gridy-data-source element.
gridy-grid will instantiate and bind dataSource when found gridy-data-source subelement. Don't forget to start demo server to check outh the code sample below.
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.19.8/antd.css" />
<gridy-grid theme="antd" id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
<gridy-data-source fields='[{ "title": "Title", "path": "$.title" },{ "title": "Price", "path": "$.price" }]'
datasource-type="ajax" url='http://127.0.0.1:8080/unpaged' datapath="$.data"></gridy-data-source>
<gridy-spinner></gridy-spinner>
<gridy-table-info id="gridyTableInfo"></gridy-table-info>
<gridy-filter id="gridyFilter"></gridy-filter>
<gridy-table id="gridyTable"></gridy-table>
<gridy-pager id="gridyPager"></gridy-pager>
<gridy-table-info id="gridyTableInfo"></gridy-table-info>
</gridy-grid>
gridy-data-source
<gridy-data-source fields='[{ "title": "Title", "displayTitle": "Some title", "path": "$.title" },{ "title": "Price", "path": "$.price"},{ "title": "Created", "path": "$.created"}]'
req-type="POST" req-param-map='{"perPage": "per_page", "sortField": {"name": "sort_field", "type": "body"}}' datasource-type="ajax" url='http://127.0.0.1:8080/paged' back-paged data-path="$.data"></gridy-data-source>
Attributes
datasource-type - attribute can have values either 'ajax' or 'local' (used when no value is defined) or any class
with name DataSource${type}
assigned to window
fields - row fields configuration json
data-path - json path for row data to be extracted
http-client-type - ajax only, used to switch between XmlHttpClient and FetchHttpClient or any custom ${Type}HttpClient assigned to window
http-client-cn - ajax only custom http client class name, completely overrides classname
http-client-options - ajax only custom http client class constructor args json
back-paged - ajax only defines if all data if fetched with one request or backend pagination and filtration is used.
Response may have the following payload (Note: data-path is set to "$.data"):
{"data":[{"field1": "FooBar11", "field2": 22}, ...], "page":2, "perPage":10, "totalResults": 16 }
and understand the following request parameters (that can be remapped with req-param-map attribute configuration) returning different pages:
?page=2&perPage=10
url - ajax only request url
req-type - request type, possible values: "GET", "POST", default: (node set -> "GET")
req-param-map - ajax only request parameter map json for backend pagination, in case your backend needs special parameter names, the following parameters can be used by dataSource: page, perPage, sortField, sortDirection. table-sort-field-id gridy-table element attribute can be used to specify sortField value from fields configuration property like name, title, path etc
req-headers - ajax only request headers json
resp-param-map - ajax only response paramter map json, value can be provided as property name or json path, fields supported: totalResults, sortField, sortDirection
<gridy-data-source fields='[{ "title": "Name", "path": "$.name", "name": "name" },{ "title": "Url", "path": "$.url", "name": "url"}]'
datasource-type="ajax" url='http://127.0.0.1:8080/view_paged' back-paged data-path="$.data" resp-param-map='{ "totalResults": "total" }'
></gridy-data-source>
Fields properties
title - more like field id
displayTitle - attribute and its equal json property displayTitle can be used to alter title in table headings, e.g. with localized labels
path json path relative to row
noRender any truable value prevents field from rendering in table
Render from markup
You can alter grid layout with own markup
<gridy-grid id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
<style>
.gridy-heading {
display: flex;
width: 100%;
justify-content: space-between;
}
</style>
<div class="gridy-heading">
<gridy-table-info id="gridyTableInfo"></gridy-table-info>
<gridy-per-page id="gridyPerPage"></gridy-per-page>
</div>
<gridy-filter id="gridyFilter"></gridy-filter>
<gridy-table id="gridyTable"></gridy-table>
<gridy-pager id="gridyPager"></gridy-pager>
</gridy-grid>
For local data source we may need less args:
<gridy-data-source fields='[{ "title": "Title", "path": "$.title" },{ "title": "Price", "path": "$.price" }]'
dataref="gridData"></gridy-data-source>
It's also possible to configure data-source with markup instead of attribute:
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.19.8/antd.css">
<gridy-grid theme="antd" id="gridyGrid" base-path="/node_modules/gridy-grid/src">
<gridy-data-source dataref="gridData">
<gridy-data-field title="Title" path="$.title"></gridy-data-field>
<gridy-data-field title="Price" path="$.price"></gridy-data-field>
</gridy-data-source>
<gridy-spinner></gridy-spinner>
<gridy-table id="gridyTable"></gridy-table>
<gridy-pager id="gridyPager"></gridy-pager>
</gridy-grid>
You can load data programmatically by markup defined datasource:
<gridy-grid theme="jquery" id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
<gridy-data-source fields='[{ "title": "Title", "path": "$.title" },{ "title": "Price", "path": "$.price" }]'
datapath="$.data"></gridy-data-source>
<gridy-spinner></gridy-spinner>
<gridy-table-info id="gridyTableInfo"></gridy-table-info>
<gridy-filter id="gridyFilter"></gridy-filter>
<gridy-table id="gridyTable"></gridy-table>
<gridy-pager id="gridyPager"></gridy-pager>
<gridy-table-info id="gridyTableInfo"></gridy-table-info>
</gridy-grid>
<script type="module">
import { SkConfig } from './node_modules/sk-core/src/sk-config.js';
customElements.define('sk-config', SkConfig);
import { GridyGrid } from '/node_modules/gridy-grid/src/gridy-grid.js';
window.data = [];
for (let i = 0; i < 25; i++) {
window.data.push({ title: 'row' + i, price: 100 * i })
}
let grid = document.querySelector('#gridyGrid');
customElements.define('gridy-grid', GridyGrid);
grid.whenBootstraped(() => {
grid.dataSource.loadData(data);
});
</script>
And you are still free to instantiate and configure dataSource programmatically:
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.19.8/antd.css">
<gridy-grid theme="antd" id="gridyGrid">
<gridy-filter id="gridyFilter"></gridy-filter>
<gridy-table id="gridyTable"></gridy-table>
<gridy-pager id="gridyPager"></gridy-pager>
</gridy-grid>
<style>
th.sort.sort-desc::after {
content: '▼';
}
th.sort.sort-asc::after {
content: '▲';
}
</style>
<script type="module">
import { GridyGrid } from '/node_modules/gridy-grid/src/gridy-grid.js';
import { DataSourceAjax } from '/node_modules/gridy-grid/src/datasource/data-source-ajax.js';
let dataSource = new DataSourceAjax();
dataSource.fields = [
{ title: 'Title', path: '$.title' },
{ title: 'Price', path: '$.price', fmt: (field, value, row) => `<b>$ ${value}</b>`, html: true }
];
dataSource.url = 'http://127.0.0.1:8080/paged';
let grid = document.querySelector('#gridyGrid');
grid.dataSource = dataSource;
customElements.define('gridy-grid', GridyGrid);
</script>
Separate declarative elements configuration
Without gridy-grid container you'll have to provider every element with configurations and link with the following attributes:
ds-ref - to specify DataSource element id/global name
pg-ref - to link table to pager
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.19.8/antd.css">
<gridy-data-source dataref="gridData" id="dataSource">
<gridy-data-field title="Title" path="$.title"></gridy-data-field>
<gridy-data-field title="Price" path="$.price"></gridy-data-field>
</gridy-data-source>
<gridy-table theme="antd" id="gridyGrid" base-path="/node_modules/gridy-grid/src" id="gridyTable"
ds-ref="dataSource" pg-ref="gridyPager" sort-field="$.title"></gridy-table>
<gridy-pager theme="antd" base-path="/node_modules/gridy-grid/src" id="gridyPager" ds-ref="dataSource"></gridy-pager>
<script type="module">
import { GridyTable } from '/node_modules/gridy-grid/src/table/gridy-table.js';
let data = [];
for (let i = 0; i < 25; i++) {
data.push({ title: 'row' + i, price: 100 * i })
}
window.gridData = data;
customElements.define('gridy-table', GridyTable);
</script>
Configuration with external element host
Gridy elements can load common configuration attributes from special elements in DOM so you don't need to repeat them for each element, currently gridy-config and sk-config (from skinny-widgets) are supported. You can alternate config element lookup selector with config-sl attribute.
<sk-config
theme="antd"
base-path="/node_modules/gridy-grid/src"
lang="ru_RU"
id="myConfig"
></sk-config>
Adding filters
to use date filter you will have to install datepicker packages. Also filter package have to be installed or you will have to plug preloaded templates
npm i sk-datepicker sk-datepicker-antd gridy-filter-date
<sk-config
lang="ru" id="skConfig" theme="antd"></sk-config>
<gridy-grid theme="antd" id="gridyGrid">
<gridy-data-source dataref="gridData">
<gridy-data-field title="Title" path="$.title"></gridy-data-field>
<gridy-data-field title="Price" path="$.price"></gridy-data-field>
<gridy-data-field title="Created" path="$.created"></gridy-data-field>
</gridy-data-source>
<gridy-spinner></gridy-spinner>
<gridy-filter dri="date" filter-mode="ge" field-title="Created" id="gridyFilter"></gridy-filter>
<gridy-table id="gridyTable">
</gridy-table>
<gridy-pager id="gridyPager"></gridy-pager>
<gridy-table-info id="gridyTableInfo"></gridy-table-info>
</gridy-grid>
<script type="module">
import { GridyGrid } from '/node_modules/gridy-grid/src/gridy-grid.js';
import { SkDatePicker } from './node_modules/sk-datepicker/src/sk-datepicker.js';
let data = [];
for (let i = 0; i < 25; i++) {
let today = new Date();
today.setDate(today.getDate() + i);
data.push({title: 'row' + i, price: 100 * i, created: today.toLocaleDateString("en-US")})
}
let grid = document.querySelector('#gridyGrid');
grid.addEventListener('bootstrap', () => {
grid.dataSource.loadData(data);
grid.table.whenRendered(() => {
console.log('table rendered');
});
});
customElements.define('gridy-grid', GridyGrid);
customElements.define('sk-datepicker', SkDatePicker);
</script>
Pagination
By default data is displayed without pagination.
gridy-grid element will enable pagination when gridy-pager element is found in internals or it has per-page configuration attribute specified by forwarding it to supported subelements.
Build grid from markup
You can generate some usual table markup with data by yourself and init Gridy Grid for it.
<gridy-table id="gridyTable" pgref="gridyPager" base-path="/node_modules/gridy-grid/src" theme="antd">
<table>
<tbody class="ant-table-tbody">
<tr><td>row hand 1</td><td><b>$ 0</b></td></tr>
<tr><td>row hand 2</td><td><b>$ 100</b></td></tr>
<tr><td>row hand 3</td><td><b>$ 100</b></td></tr>
<tr><td>row hand 4</td><td><b>$ 100</b></td></tr>
<tr><td>row hand 5</td><td><b>$ 100</b></td></tr>
</tbody>
</table>
</gridy-table>
<gridy-pager id="gridyPager" dsref="gridyTable.dataSource" base-path="/node_modules/gridy-grid/src" theme="antd"></gridy-pager>
<script type="module">
import { GridyTable } from '/node_modules/gridy-grid/src/table/gridy-table.js';
customElements.define('gridy-table', GridyTable);
</script>
or with datasource:
<gridy-grid id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
<gridy-data-source fields='[{ "title": "Title", "path": "$.[0]" },{ "title": "Price", "path": "$.[1]"}]'></gridy-data-source>
<gridy-table selectable="multi" selection-emit="ctrl+click" theme="antd" id="gridyTable">
<table>
<tbody class="ant-table-tbody">
<tr><td>row hand 1</td><td>0</td></tr>
<tr><td>row hand 2</td><td>100</td></tr>
<tr><td>row hand 3</td><td>100</td></tr>
<tr><td>row hand 4</td><td>100</td></tr>
<tr><td>row hand 5</td><td>100</td></tr>
</tbody>
</table>
</gridy-table>
<gridy-pager id="gridyPager2"></gridy-pager>
</gridy-grid>
thead contents also can be parsed, but only for ds field props that are not defined
Allow rows selection
Attribute selectable will enable selection behaviour when added to gridy-grid or gridy-table element. When value is set as "multi" there will be possible to select multiple rows, when any other or no value - only one row.
selection-emit attribute choses how selection will be emited, e.g. on long click/tap or click, possible values: "tap" - select by long click/tap, "click", "ctrl+click", "shift+click", "alt+click" (default: no -> click)
time-to-select - time to select for tap mode in miliseconds, (default: 700x)
GridyTable properties selectedRow, selectedRows and prevSelectedRow store links to selected rows.
Access row by dataItem
you can get link to rendered (visually present on table) table row by dataItem (data row mapped by field title) provided with rowByDataItem(dataIitem) gridy-table method
let gridyGrid = document.getElementById('gridyGrid');
let dataItem = {'title': 'foobar9', 'price': 600, 'created': '13.04.2022'};
let rowEl = gridyGrid.table.rowByDataItem(dataItem);
console.log(`row for ${JSON.stringify(dataItem)}`, rowEl);
Define row click callback
You can either bind it with general event api using gridy-grid element or gridy-table and rely to path attribue values or use special property onrowclick or onhrowclick (for heading) for convenience:
gridyTable.onrowclick = function(event, row) {
console.log('rowclick', event, row, this);
};
onrowrclick is for right button mouse click handling
Pane opened on click
You can easily implement details pane with your own template.
<gridy-table id="gridyTable" row-pane>
</gridy-table>
<template id="tableRowPaneTpl">
<div>Price:</div><div>{{ price }}</div>
</template>
Row data mapped to field is exposed info template rendering context.
attributes
row-pane-tpl-path - template loading url, can be specified to anything if template predefined dom
row-state - preserve row pane state between paging and sorting operations (default: not set -> false)
Define attributes for cell td element
You can specify any own td's attribute as value or lambda with attr options subset, e.g. you want to implement cell hover hint
dataSource.fields = [
{ title: 'Title', path: '$.title', attr: { title: (field, value) => `${field.title}` }},
{ title: 'Price', path: '$.price' }
];
rowattr can be also specified in the same way for tr element, beware multiple same attrs will overwrite each other
Custom formatting functions
tplPath - path for cell template to render, html option is also recommended to be enabled with this parameter, row data (dataItem) and executed value are flatmapped to rendering context
calc - calculation map with fields to functions, that evaluted on page is rendered, the result is passed to fmt function by key. Here is the example you can add hashcode calculation by executing method from dataSource. The first (0) argument of evaluted function is dataItem and context is binded to dataSource. If you use sort in LocalDataSource and calc is provided as hashmap then first calculated key is taken to compare.
calculation cache is controlled by table-calc-cache attr for gridy-table or config element, define it to enable, rows
must have unique contents (e.g. has id
field) to have caching work properly
<gridy-data-source fields='[{ "title": "Hashcode", "name": "hashcode", "fmt": "{{ hash }}", "calc": { "hash": "return this.hashDataItem(arguments[0])" }}]'></gridy-data-source>
or
<gridy-data-source fields='[{ "title": "Hashcode", "name": "hashcode", "fmt": "return arguments[0].calc;", "calc": { "return this.hashDataItem(arguments[0])" }]'></gridy-data-source>
fmt - field attribute allows to have string templating with value and row field values are available in rendering context or evaluted callback (when you use javascript to configure fields). The args of this callback are: field, value, dataItem, rendered. To evalute html from fmt you should also add html: true attribute.
if fmt string starts with words 'return', it will be evaluted as function with the following map as argument { value: value, ...dataItem, ...calcs, rendered: rendered } with context binded to table component
<gridy-data-source fields='[{ "title": "Hashcode", "name": "hashcode", "fmt": "return arguments[0].hash;", "calc": { "hash": "return this.hashDataItem(arguments[0])" }}]'></gridy-data-source>
formatting result cache is controlled by table-fmt-cache attr for gridy-table or config element, define it to enable, rows
must have unique contents (e.g. has id
field) to have caching work properly
in case tplPath and fmt both specified they can do accumulative rendering, template rendering result is passed as rendered var to fmt method or 4th arg to function.
<gridy-grid id="itemsGrid">
<gridy-data-source fields='[{ "title": "", "path": "$.*", "fmt": "<sk-checkbox class=\"item-chk no-rowclick\" value=\"{{ value }}\"></sk-checkbox>", "html": "true"}, { "title": "Содержание", "path": "$.contents" },{ "title": "Исполнитель", "path": "$.executor" },{ "title": "Ответственный", "path": "$.responsible" },{ "title": "Время выполнения", "path": "$.unitsToComplete" }]'
datasource-type="DataSourceLocal" datapath="$.data"></gridy-data-source>
<gridy-table id="itemsTable"></gridy-table>
<gridy-pager id="itemsPager"></gridy-pager>
</gridy-grid>
Configurations with stored-cfg
hide column
<gridy-table id="gridyTable" stored-cfg='{"columns": {"Name": { "display": "none"}}}'></gridy-table>
set custom styles (in camel-case)
<gridy-table id="gridyTable" stored-cfg='{"columns": {"Name": {"style": {"color": "red"}}}}'></gridy-table>
set column attribute by it's name
<gridy-table id="gridyTable" stored-cfg='{"columns": {"Name": { "attr": {"attr": {"style": "color:red"}}}}'></gridy-table>
reorder columns
<gridy-table id="gridyTable" stored-cfg='{"columnsOrder": ["Color", "Value", "Name"]}'></gridy-table>
you can preset any instance property when specify in stored-props attr json
<gridy-grid id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.name" stored-cfg='{"curPageNum": "2"}' stored-props='["curPageNum"]'></gridy-grid>
Table header context menu
To add context menu with settings add gridy-header-menu element to gridy-grid container
<gridy-grid id="gridyGrid">
<gridy-header-menu></gridy-header-menu>
...
</gridy-grid>
Toolbars
To add context menu with settings add gridy-header-menu element to gridy-grid container
<gridy-toolbar id="gridyToolbar">
<gridy-toolbar-item>add</gridy-toolbar-item>
<gridy-toolbar-item>edit</gridy-toolbar-item>
<gridy-toolbar-item>delete</gridy-toolbar-item>
</gridy-toolbar>
events
toolbaritemclick - triggered when toolbar item is clicked, detail: item - dom obj of item is clicked, origEvent - mouse click event
Subscribe to lifecycle events
Gridy grid components are emitting events that can help you to hook the process, e.g. you want to do some stuff after table header is rendered. All you have to do is to add event listener to particular event.
gridyTable.addEventListener('headrender', function(event) {
console.log('headrender', event, this);
});
List of events by elements:
gridy-table: headerRowRendered (deprecated) headrowrender headersRendered (deprecated) headrender bodyRowRendered (deprecated) bodyrowrender bodyRendered (deprecated) bodyrender rowclick (bubbles: true, composed: true) itemselect (bubbles: true, composed: true) itemdeselect (bubbles: true, composed: true)
gridy-view: itemselect (bubbles: true, composed: true) itemdeselect (bubbles: true, composed: true)
gridy-table-info: tableInfoRendered
gridy-pager: pageChanged (deprecated) pagechange pagerButtonRendered pagerRendered
gridy-filter: filterChanged (deprecated) filterchange gridy-per-page: perPageChanged (deprecated) perpagechange
Override and Cache Templates
You can place contents inside template tag with matching ids to make renderer overriding templates with by your versions. The highest priority is for lookup inside current html element, then document is searched and after that template path attribute is loaded.
So you can copy markup from theme inside tag, modify and component will use it. Here is the map of template attributes to loaded element ids below.
gridy-table: head-tpl-path -> tableHeadTpl body-tpl-path -> tableBodyTpl row-pane-tpl-path -> tableRowPaneTpl
gridy-table-info: tpl-path -> tableInfoTpl noent-tpl-path -> noEntriesInfoTpl (optional)
gridy-view: tpl-path -> GridyViewTpl : view.tpl.html
gridy-pager: body-tpl-path -> pagerBodyTpl item-active-tpl-path -> pagerActiveItemTpl item-tpl-path -> pagerItemTpl
gridy-filter: tpl-path -> pagerItemTpl
gridy-spinner: tpl-path -> spinnerTpl
gridy-per-page: tpl-path -> perPageTpl
Configuration Attributes
all:
theme - theme name (default: 'default')
base-path - base path to files
config-sl - config tag selector
lang - lang code
tpl-vars - json with data for templates prerendering
gridy-grid:
impl-path - path to impl class
sort-field - sort field jsonpath
sort-direction - sort fied initial direction (default: 'asc')
config-tn - confiruation element tag name (default: 'gridy-config')
table-tn - table element tag name (default: 'gridy-table')
ds-tn - datasource element tag name (default: 'gridy-data-source')
df-tn - datafield element tag name (default: 'gridy-data-field')
filter-tn - filter element tag name (default: 'gridy-filter')
pager-tn - pager element tag name (default: 'gridy-pager')
info-tn - info element tag name (default: 'gridy-table-info')
spinner-tn - spinner element tag name (default: 'gridy-spinner')
per-page-tn - per-page element tag name (default: 'gridy-per-page')
per-page - number of elements per page (default: not set)
cur-page-num - current page number (default: not set)
selectable - user can select rows by click, options: 'multi', (default: not set -> no)
gridy-table:
impl-path - path to impl class
sort-direction
headers | show-headers - show field titles in heading (default: not set -> true)
show-footers - render table footer (default: not set -> false)
head-in-foot - render column names in footer just as in header
sort-field - sort field jsonpath
tpl-path - path to base template
head-tpl-path - path to head template
body-tpl-path - path to body template
foot-tpl-path - path to body template
row-pane-tpl-path - path to row pane template
ds-ref - datasource instance reference
pg-ref - pager instance reference
per-page - number of elements per page (default: not set)
cur-page-num - current page number (default: not set)
selectable - user can select rows by click, options: 'multi', (default: not set -> no)
table-tn - table tag name (default: 'table')
head-tn - table header tag name (default: 'thead')
foot-tn - table footer tag name (default: 'tfoot')
hcell-tn - table footer tag name (default: 'th')
row-tn - table footer tag name (default: 'tr')
cell-tn - table footer tag name (default: 'td')
gridy-view: impl-path - path to impl class tpl-path - path to base template ds-ref - datasource instance reference pg-ref - pager instance reference dri - driver code selectable - user can select rows by click, options: 'multi', (default: not set -> no)
gridy-table-info:
impl-path - path to impl class
tpl-path - path to base template
noent-tpl-path - no entries message template path
lang-path - language relative path
lang-full-path - language full path
per-page - number of elements per page (default: not set)
gridy-pager:
impl-path - path to impl class
body-tpl-path - body template path
item-tpl-path - item template path
item-active-tpl-path - active item template path
per-page - number of results per page
cur-page-num - current page number
total-results - total results number
total-pages - total pages number
ds-ref - datasource instance reference
gridy-filter:
impl-path - path to impl class
field-title - filter by particular field (selected by Title)
field-label - overrides label for field
filter-mode - filter mode, "gt" - keep greater values, "ge" - keep greater or equal values,
"lt" keep smaler values, "le" - keep smaler or equal values,
"cntn" - keep by substring containity, "eq" - keep equals, "ne" - keep not equal values
default: unset -> keep by substring containity
gridy-spinner:
impl-path - path to impl class
gridy-per-page:
impl-path - path to impl class
per-page - number or rows for one page (default: 10)
per-page-options - number or rows for one page (default: 10, 25, 50, 100)
per-page-default - override default per page number (default: not set -> 10)
Loading templates from bundles
http/2+ allows to do request multiplexing that means a lot of smaller requests to the same host are better than big blocking requests to multiple hosts. But if you think you still need to load all in one bundle, you can use aggregated template bundles loaded with js or included to page by server.
<script>
const loadTemplates = async () => {
await fetch('node_modules/gridy-grid/dist/gridy-antd.tpls.native.html')
.then(response => response.text())
.then(text => document.body.insertAdjacentHTML('beforeend', text));
};
loadTemplates();
</script>
Tweak renderer behaviour
You can control template loading/caching behaviour either individually or with gridy-grid element attributes:
rd-cache - enables/disables templates caching and inline overriding (default: enabled)
rd-cache-global - if cached template wasn't found allows to lookup in document (default: enabled)
You can even enable rendering with Handlebars templating library for your overriden templates. To have this you must provide Handlebars static import.
<script src="./node_modules/handlebars/dist/handlebars.js"></script>
or any other way to set global Handlebars instance to window or inject renderer property (not attribute).
Also add option rd-var-render enabled for renderer instance passed to gridy-grid or gridy-table element as attribute. It can be 'handlebars' to load handlebars or loader window function name to call. If attribute is not defined, simplified built-in renderer that support only variables will be used.
There is an option to use handlebars templates format instead of Native Templates to support legacy browsers like Internet Explorer. It can be enabled with rd-tpl-fmt="handlebars" attribute to gridy-grid element. You will also need a kinda transpiler with modules, classes etc. support enabled to be able to see rendering results. In addition handlebars tpl format requires jquery library present in runtime.
Ajax Data Source
Data is loaded from REST service, ant.design framework theming enabled. Paging and filtering components added;
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.19.8/antd.css">
<gridy-filter id="gridyFilter"></gridy-filter>
<gridy-table id="gridyTable"></gridy-table>
<gridy-pager id="gridyPager"></gridy-pager>
<style>
th.sort.sort-desc::after {
content: '▼';
}
th.sort.sort-asc::after {
content: '▲';
}
</style>
<script type="module">
import { GridyTable } from '/node_modules/gridy-grid/src/table/gridy-table.js';
import { GridyPager } from '/node_modules/gridy-grid/src/pager/gridy-pager.js';
import { GridyFilter } from '/node_modules/gridy-grid/src/filter/gridy-filter.js';
import { DataSourceLocal } from '/node_modules/gridy-grid/src/datasource/data-source-local.js';
import { DataSourceAjax } from '/node_modules/gridy-grid/src/datasource/data-source-ajax.js';
//let dataSource = new DataSourceLocal();
let dataSource = new DataSourceAjax();
dataSource.fields = [
{ title: 'Title', path: '$.title' },
{ title: 'Price', path: '$.price' }
];
dataSource.url = 'http://127.0.0.1:8080/paged';
let filter = document.getElementById('gridyFilter');
filter.tplPath = '/node_modules/gridy-grid/src/theme/antd/filter.tpl.html';
filter.dataSource = dataSource;
let pager = document.getElementById('gridyPager');
pager.dataSource = dataSource;
pager.theme = 'antd';
pager.bodyTplPath = '/node_modules/gridy-grid/src/theme/antd/pager-body.tpl.html';
pager.itemTplPath = '/node_modules/gridy-grid/src/theme/antd/pager-item.tpl.html';
pager.itemActiveTplPath = '/node_modules/gridy-grid/src/theme/antd/pager-item-active.tpl.html';
let table = document.getElementById('gridyTable');
table.dataSource = dataSource;
table.pager = pager;
table.filter = filter;
table.headers = true;
table.theme = 'antd';
table.headTplPath = '/node_modules/gridy-grid/src/theme/antd/table-head.tpl.html';
table.bodyTplPath = '/node_modules/gridy-grid/src/theme/antd/table-body.tpl.html';
customElements.define('gridy-filter', GridyFilter);
customElements.define('gridy-table', GridyTable);
customElements.define('gridy-pager', GridyPager);
</script>
Charts
Gridy has chart component that uses datasources to initialize chart libraries.
e.g. for Chart.js install library
npm i chart.js gridy-chart-chartjs
and plug it to your page
<script src="/node_modules/chart.js/dist/chart.js"></script>
then add element to your container or setup it programmatically
<gridy-grid id="gridyGrid" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
<gridy-data-source fields='[{ "title": "Price1", "path": "$.price1", "borderColor": "red", "backgroundColor": "green", "borderWidth": 1 },{ "title": "Price2", "path": "$.price2", "borderColor": "green", "backgroundColor": "yellow", "borderWidth": 1 }]'
datasource-type="DataSourceLocal" datapath="$.data"></gridy-data-source>
<gridy-chart dri="chart-js" type="line" width="400" height="300" options='{"scales":{"y":{ "beginAtZero": true }}'></gridy-chart>
</gridy-grid>
<script>
let data = [];
for (let i = 0; i < 10; i++) {
data.push({ price1: 200 * i, price2: 100 * i })
}
let grid = document.querySelector('#gridyGrid');
grid.addEventListener('bootstrap', () => {
//grid2.charts[0].dri = function() { return dri }; // you can override driver at runtime
grid.charts[0].addEventListener('skrender', (event) => {
grid.dataSource.loadData(data);
});
});
</script>
When each field is data provider for own chart set, chart dataset parameter can be passed as field props or attribues
<gridy-data-source
datasource-type="DataSourceLocal" datapath="$.data">
<gridy-data-field title="Price1" path="$.price1" border-color="red" background-color="green" border-width=1></gridy-data-field>
<gridy-data-field title="Price2" path="$.price2" border-color="blue" background-color="white" border-width=1></gridy-data-field>
</gridy-data-source>
To use without gryd-grid container you must specify at leas ds-ref attribue pointing to gridy-data-source element id or setup dataSource programmatically.
<gridy-data-source id="gridyDs" fields='[{ "title": "x", "path": "$.x" },{ "title": "y", "path": "$.y"},{ "title": "Group", "path": "$.group"},{ "title": "Color", "path": "$.color"}]'
datasource-type="DataSourceLocal" datapath="$.data"></gridy-data-source>
<gridy-chart id="gridyChart" ds-ref="gridyDs" type="line" tooltip dri="d3" field-color="Color" field-x="x" field-y="y" width="400" height="400"></cbr-chart>
<script type="module">
let data2 = [];
data2.push({ x: 100, y: 100, group: 'a', color: '#98abc5' });
data2.push({ x: 200, y: 150, group: 'a', color: '#98abc5' });
data2.push({ x: 300, y: 270, group: 'a', color: '#98abc5' });
data2.push({ x: 400, y: 350, group: 'b', color: '#98abc5' });
data2.push({ x: 500, y: 460, group: 'b', color: '#98abc5' });
data2.push({ x: 600, y: 770, group: 'b', color: '#98abc5' });
data2.push({ x: 700, y: 840, group: 'c', color: '#98abc5' });
data2.push({ x: 800, y: 990, group: 'c', color: '#98abc5' });
let chart = document.querySelector('#gridyChart');
let ds = document.querySelector('#gridyDs');
customElements.define('gridy-data-source', GridyDataSource);
customElements.define('gridy-chart', GridyChart);
chart.whenRendered(() => {
chart.dataSource.loadData(data2);
});
</script>
attributes
ds-ref - to specify DataSource element id/global name
pg-ref - to link table to pager
Configuration
attributes
share-chart-dri - share chart driver instance from cache than instancinate, ok when all types of chart on page have the same type
Supported Drivers:
Views
Gridy has gridy-view that allows extensible and templatable components to display from datasource.
E.g. you can install gridy-view-icongrid package and use it transparently.
<gridy-grid id="gridyGrid2" base-path="/node_modules/gridy-grid/src" sort-field="$.title">
<gridy-data-source fields='[{ "title": "Name", "path": "$.name" },{ "title": "Value", "path": "$.value"},{ "title": "Group", "path": "$.group"}]'
datasource-type="DataSourceLocal" datapath="$.data"></gridy-data-source>
<gridy-view
dri="icongrid" id="gridyChart2" field-name="Name" field-value="Value" width="400" height="400"></gridy-view>
<gridy-pager id="gridyPager2"></gridy-pager>
</gridy-grid>
attributes
dri - driver code
field-{name}={title} - field mappings from data sources, valus will be mapped to rendering context by {name}
tpl-path - path for template to load
item-tpl-path - path for item template to load
item-tpl-str - template item with string value
tpl-vars - variables for prerendering templates
impl-path - path to theme based impl override
ds-ref - to specify DataSource element id/global name
pg-ref - to link table to pager
selectable - enables ability to select items, selection can be taken from props selectedItems, selectedItem, prevSelectedItem, possible values: multi or any/empty means single, default (empty -> single selection)
selection-emit - choses how selection will be emited, e.g. on long click/tap or click, possible values: "tap" - select by long click/tap, "click", "ctrl+click", "shift+click", "alt+click" (default: no -> click)
time-to-select - time to select for tap mode in miliseconds, (default: 700x)
share-dri - share chart driver instance from cache than instancinate, ok when all types of chart on page have the same type
Supported Drivers:
Internationalization
gridy-grid element supports lang attribued that allows developer to provide elements translated by locale resources. Resources for translation are shipped with themes.
<gridy-grid theme="antd" id="gridyGrid" lang="ru_RU">
<gridy-filter id="gridyFilter"></gridy-filter>
<gridy-table id="gridyTable"></gridy-table>
<gridy-pager id="gridyPager"></gridy-pager>
</gridy-grid>
Plugins
You can use plugins as derived from SkElement, with possibility to load from element attributes configuration as follows
<gridy-chart id="gridyChart2" title="Foobar" legend='{"data": ["sales", "markt"], "orient": "vertical", "right": 10, "top": "center"}'
options='{"xAxis": { "data": "names" }, "yAxis": {}}'
type="multi:bar:sales,line:markt" dri="echarts" echarts-renderer="svg"
attr-plugins="true" plg-foo-cn="MyPlugin" plg-foo-path="/my-plugin.js"
value-field field-name="Name" field-value="Value" field-group="Group" inner-radius="50" width="400" height="400"></gridy-chart>
Use with compatibility bundle (old browser support)
To have browsers up to IE11 supported you definitely need to load WebComponents polyfills.
Rendering engine must be changed to handlebars.
Also jquery needs to be loaded before bundle as it is used to generate table markup.
Initializing code must be wrapped into WebComponentsReady hook event.
In the example below polyfills from skinny-widgets library are used. You should install it or plug your own.
npm i skinny-widgets --save
<script src="/node_modules/skinny-widgets/compat/dialog-polyfill.js"></script>
<script src="/node_modules/skinny-widgets/compat/fetch.umd.js"></script>
<script src="/node_modules/skinny-widgets/compat/polyfill.min.js"></script>
<script src="/node_modules/skinny-widgets/compat/webcomponents-lite.js"></script>
<script src="/node_modules/skinny-widgets/compat/custom-elements-es5-adapter.js"></script>
<script src="/node_modules/skinny-widgets/compat/event-target.js"></script>
<link rel="stylesheet" type="text/css" href="/node_modules/gridy-grid/antd.min.css">
<sk-config
theme="antd"
base-path="/node_modules/gridy-grid/src"
lang="ru"
id="gridConfig"
></sk-config>
<gridy-table config-sl="#gridConfig" id="grid" pgref="pager" rd-tpl-fmt="handlebars"></gridy-table>
<gridy-pager config-sl="#gridConfig" id="pager" dsref="dataSource"></gridy-pager>
<script src="/node_modules/skinny-widgets/dist/skinny-widgets-bundle.js"></script>
<script src="/node_modules/jquery/dist/jquery.js"></script>
<script src="/node_modules/gridy-grid/dist/gridy-grid-bundle.js"></script>
<script>
window.addEventListener('WebComponentsReady', function(e) {
var dataSource = new DataSourceLocal();
dataSource.fields = [
{ title: 'Title', path: '$.title' },
{ title: 'Price', path: '$.price' }
];
var data = [];
for (let i = 0; i < 25; i++) {
data.push({ title: 'row' + i, price: 100 * i })
}
grid.dataSource = dataSource;
customElements.define('sk-config', SkConfig);
customElements.define('gridy-pager', GridyPager);
customElements.define('gridy-table', GridyTableAntd4);
dataSource.loadData(data);
});
</script>
Development
You can run sample data backend and index.html serving from this project's root.
npm run server
Use with SystemJS module loader
Gridy Grid currently has only one external runtime dependency that is actually source bundled to avoid problems in untranspilled environments. So in case you want to use that library (complets) from it's own npm package or somewhere else e.g. shared bundle you may use SystemJS loader map to resolve Gridy's internal dependencies.
First add and setup module loader for your project.
npm i systemjs systemjs-transform-babel --save
And do configuration that will transpile and resolve the code.
<script src="/node_modules/systemjs/dist/system.js"></script>
<script src="/node_modules/systemjs/dist/extras/transform.js"></script>
<script src="/node_modules/systemjs-transform-babel/dist/babel-transform.js"></script>
<script type="systemjs-importmap">
{
"imports": {
"gridy-grid": "./node_modules/gridy-grid/src/gridy-grid.js",
"data-source-local": "./node_modules/gridy-grid/src/datasource/data-source-local.js",
"/node_modules/gridy-grid/complets/": "/node_modules/complets/"
}
}
</script>
<script>
Promise.all([System.import('gridy-grid'), System.import('data-source-local')]).then((m) => {
let dataSource = new m[1].DataSourceLocal();
//let dataSource = new DataSourceAjax();
dataSource.fields = [
{ title: 'Title', path: '$.title' },
{ title: 'Price', path: '$.price' }
];
let grid = document.querySelector('#gridyGrid');
grid.dataSource = dataSource;
customElements.define('gridy-grid', m[0].GridyGrid);
// generate sample data for DataSourceLocal
let data = [];
for (let i = 0; i < 25; i++) {
data.push({ title: 'row' + i, price: 100 * i })
}
dataSource.loadData(data);
});
</script>
Update complets library version
git submodule update --remote --merge
Running tests
open test/tests.html from browser with statically served project root
or install webcomponents test runner
sudo npm install --global web-component-tester --unsafe-perm
and run with
wct --npm