DataView
SlickDataView sits between your data array and the grid. It filters, sorts, pages and groups the data on the client, then feeds the result to the grid one row at a time. This chapter explains what a DataView is, how items map to rows, and how to wire the core features. For the full member list, see the DataView API reference.
When you need one
The grid itself is deliberately small. It renders rows and reports events. It does not filter, sort, page or group.
Use a SlickDataView when all the data is on the client and you want any of these:
- Client-side sorting and multi-column sorting.
- A search or filter box.
- Paging.
- Grouping with totals (see Grouping & aggregators).
- Efficient, minimal re-rendering when the data changes.
If you only show a static array and need none of the above, pass the array straight to the grid. See Providing data to the grid.
The mental model
A DataView is a data provider
The grid can read its data from any object with three methods:
getLength()— the number of rows to render.getItem(index)— the row at an index.getItemMetadata(index)— optional per-row metadata.
SlickDataView implements this interface. You pass the DataView to the grid in place of the array:
import { SlickGrid, SlickDataView } from 'slickgrid';
const dataView = new SlickDataView();
const grid = new SlickGrid('#myGrid', dataView, columns, options);The grid now asks the DataView for rows. When the DataView changes the data, it tells the grid which rows to repaint through events (see Events).
Items vs rows
This is the most important idea in the chapter.
- Items are your input. They are the objects you put in with
setItems. Read them back withgetItems. - Rows are the grid's output. They are what the DataView shows after filtering, paging and grouping. The grid counts rows, not items.
A row index is not an item index. When a grid event gives you a row number, look the object up through the DataView:
grid.onClick.subscribe((_e, args) => {
const item = dataView.getItem(args.row); // correct
// const item = dataView.getItems()[args.row]; // WRONG - that is an item index
});Note the naming quirk, kept for history: getItem(row) returns a row, while getItems() returns items.
The id property
Every item must have a unique id. The DataView uses it to track items as they move, and to map between ids, items and rows.
- The default id property is
id. - Pass a different property name as the second argument to
setItems. - The id must be unique and convertible to a string. A duplicate or missing id throws an error.
getIdPropertyNamereturns the name in use.
dataView.setItems(products, 'sku'); // use the "sku" property as the idWalkthrough
1. Wire the DataView to the grid
Subscribe once to onRowsOrCountChanged. This one event replaces the older pair onRowCountChanged + onRowsChanged and tells you which of the two changed:
dataView.onRowsOrCountChanged.subscribe((_e, args) => {
if (args.rowCountChanged) {
grid.updateRowCount();
}
if (args.rowsChanged) {
grid.invalidateRows(args.rowsDiff);
}
grid.render();
});Use args.rowsDiff for the changed rows. Do not use args.rows; that field belongs to the older onRowsChanged event.
2. Load the data
setItems replaces the whole dataset and refreshes the grid:
const data = [
{ id: 1, name: 'Apple', qty: 12 },
{ id: 2, name: 'Pear', qty: 3 },
{ id: 3, name: 'Plum', qty: 7 },
];
dataView.setItems(data);setItems fires onSetItemsCalled and then refreshes.
3. Look items up by id
Since each item has an id, the DataView can map between ids, items and rows:
| Method | Returns |
|---|---|
getItemById(id) | the item with that id |
getItemByIdx(idx) | the item at that index in the items array |
getIdxById(id) | the item's index in the items array |
getRowById(id) | the item's grid row, or undefined if it is not visible |
getRowByItem(item) | the grid row for an item object |
mapIdsToRows(ids) / mapRowsToIds(rows) | map arrays between ids and rows |
getRowById returns undefined when the item is filtered out or is on another page. That is expected; the item still exists, it just has no visible row.
To add, change or remove items, use addItem, insertItem, updateItem and deleteItem (and their plural forms). Each one refreshes the grid.
4. Sort
Wire the grid's onSort event to sort. Pass a comparer and the direction:
grid.onSort.subscribe((_e, args) => {
const field = args.sortCol.field;
dataView.sort((a, b) => (a[field] > b[field] ? 1 : a[field] < b[field] ? -1 : 0), args.sortAsc);
});For multi-column sorting, set the grid option multiColumnSort. The event then gives args.sortCols, an array of { sortCol, sortAsc }. See Sorting for the full pattern.
sortreorders the underlying items array. Keep a copy first if you need the original order.reSortre-applies the last sort after the data changes.fastSortis deprecated. Usesort.
5. Filter
A filter is a function that returns true to keep an item. Set it with setFilter:
const search = document.querySelector<HTMLInputElement>('#search')!;
dataView.setFilter((item, args) => {
if (!args?.text) {
return true;
}
return item.name.toLowerCase().includes(args.text.toLowerCase());
});
search.addEventListener('input', () => {
dataView.setFilterArgs({ text: search.value });
dataView.refresh();
});- Pass extra data to the filter with
setFilterArgs. It arrives as the second parameter. setFilterArgsalone does not re-run the filter. Callrefreshafter it.
CSP-safe by default
In v6 all filtering is CSP-safe. The DataView calls your filter function directly and never builds code with new Function. It works unchanged under a strict Content-Security-Policy.
The old options inlineFilters and useCSPSafeFilter are deprecated and ignored. Do not set them.
6. Page
Set the page size and current page with setPagingOptions:
dataView.setPagingOptions({ pageSize: 25, pageNum: 0 });Read the current state with getPagingInfo. It returns { pageSize, pageNum, totalRows, totalPages, dataView }:
const info = dataView.getPagingInfo();
dataView.setPagingOptions({ pageNum: Math.min(info.pageNum + 1, info.totalPages - 1) });- A
pageSizeof0shows all rows on one page. - The DataView fires
onPagingInfoChangedwhenever the page, size or total changes. Update any pager UI from that event.
The built-in SlickGridPager control renders a ready-made pager and keeps itself in sync. See Controls and the Filtering & paging chapter.
7. Batch several changes
Each mutating call refreshes the grid. When you make many changes at once, wrap them so the grid refreshes only once:
dataView.beginUpdate();
dataView.addItem({ id: 4, name: 'Fig', qty: 5 });
dataView.deleteItem(2);
dataView.updateItem(1, { id: 1, name: 'Apple', qty: 20 });
dataView.endUpdate(); // one refresh hereFor very large insert or delete batches, use bulk mode: beginUpdate(true). It defers index rebuilding and deletions to the endUpdate call for speed. While bulk mode is active, some lookups may return stale results until endUpdate runs.
8. Sync the selection
Without help, the grid tracks selection by row. If items move, sort or filter, the same rows stay selected instead of the same items. syncGridSelection fixes this by tracking selection by item id.
import { SlickRowSelectionModel } from 'slickgrid';
grid.setSelectionModel(new SlickRowSelectionModel());
dataView.syncGridSelection(grid, true);- The second argument,
preserveHidden, keeps items selected even after a filter hides them. When the filter clears, they are still selected. - A third argument,
preserveHiddenOnSelectionChange, keeps hidden selections while the visible selection changes (multi-select grids). - The method returns the
onSelectedRowIdsChangedevent, so you can read the full id list, including hidden selections. - Read selections with
getAllSelectedIds/getAllSelectedItems, or the filtered-onlygetAllSelectedFilteredIds/getAllSelectedFilteredItems.
syncGridSelection works with the row selection model, not the cell selection model. There is a matching syncGridCellCssStyles(grid, key) for cell CSS styles. See Selection models.
Events
The DataView fires these events. Subscribe with .subscribe((e, args) => { ... }).
| Event | Fires when |
|---|---|
onSetItemsCalled | setItems runs |
onBeforePagingInfoChanged | before paging info changes (return false to cancel) |
onPagingInfoChanged | page, size or total row count changed |
onRowCountChanged | the number of rows changed |
onRowsChanged | the content of some rows changed |
onRowsOrCountChanged | either of the two above changed (use this one) |
onSelectedRowIdsChanged | the synced selection changed |
onGroupExpanded / onGroupCollapsed | a group toggled |
Firing order
A refresh fires the change events in a fixed order, and only those that apply:
onBeforePagingInfoChanged, thenonPagingInfoChanged— only if the total row count changed.onRowCountChanged— only if the visible row count changed.onRowsChanged— only if some rows differ.onRowsOrCountChanged— if either the count or the rows changed.
Prefer onRowsOrCountChanged. Handling the two separate events is a common source of bugs, because neither one knows whether the other will also fire. onRowsOrCountChanged reports both facts in one call, through args.rowCountChanged and args.rowsChanged, so its handler can do the right thing every time.
If you do use the separate events, each carries a flag naming the other: onRowCountChanged has callingOnRowsChanged, and onRowsChanged has calledOnRowCountChanged. Do not subscribe to both the pair and the combined event; pick one approach.
Grouping
The DataView also groups data with totals. Call setGrouping and use the built-in Aggregators (Avg, Min, Max, Sum, Count). Grouping has its own chapter: Grouping & aggregators.
Common pitfalls
- Row index is not item index. In grid event handlers use
dataView.getItem(row), nevergetItems()[row]. - Every item needs a unique id. A duplicate or
undefinedid throws. Set a custom id property with the second argument ofsetItems. - Sorting mutates your array.
sortreorders the items you passed tosetItems. Copy first if you need the original order. getRowByIdcan returnundefined. A filtered or off-page item has no visible row. This is normal, not an error.setFilterArgsneedsrefresh. Changing the filter args does not re-run the filter on its own.- Use
args.rowsDiff, notargs.rows, inonRowsOrCountChangedhandlers.rowsbelongs toonRowsChanged.
See also
- DataView — API reference — every method, option and event.
- Providing data to the grid — the data provider interface and plain arrays.
- Sorting — single- and multi-column sorting in depth.
- Filtering & paging — search boxes and the pager control.
- Grouping & aggregators —
setGroupingand totals. - Selection models — row selection and
syncGridSelection. - CSP & sanitization — why filtering is CSP-safe.
- Grid — API reference —
onSort,updateRowCount,invalidateRows,render.