Skip to content

TypeScript

@dzgrid/dom draws the grid in any page, with no framework. @dzgrid/core holds the calculations (no DOM) and @dzgrid/theme the CSS. This page uses createGrid directly; the tabs show the same grid with Angular and with the Web Component.

Terminal window
npm install @dzgrid/core @dzgrid/dom @dzgrid/theme

@dzgrid/dom/filters and @dzgrid/dom/editors load a date calendar from @tzslot/core and @tzslot/dom: install them too when you use those two entries. Without them, import from @dzgrid/dom/filters-base and @dzgrid/dom/editors-base, which have no calendar: with filters-base a day or instant column gets no filter (no button) and one missing-feature warning; with editors-base it is edited by typing. Day filters come from @dzgrid/dom/filters (or filters-base given loadDates).

The grid fills its host element, whatever its row count: give the host a height.

<div id="books" style="block-size: 320px; display: flex; flex-direction: column"></div>

With 3 rows or 10 000 the grid is as tall as the host, scrolls inside it and draws only the rows in view. A fixed height, a flex or grid share all work; the flex column lets a bar sit above the grid. A host with no height (sized by its content) makes the grid grow with its rows; it then holds its view to the window’s height and reports a problems event with the code unbounded-viewport (path host) once.

import { createGrid } from '@dzgrid/dom';
import type { ColumnOrGroup } from '@dzgrid/core';
import { EN } from '@dzgrid/dom/locales/en';
import '@dzgrid/theme/dzgrid.css';
type Book = { id: string; title: string; author: string; published: string; price: number };
const rows: Book[] = [
{ id: 'b1', title: 'The Glass Orchard', author: 'Mira Volkov', published: '2019-03-14', price: 18.5 },
{ id: 'b2', title: 'Salt and Lanterns', author: 'Idris Fenwick', published: '2021-11-02', price: 22 },
{ id: 'b3', title: 'A Map of Quiet Rivers', author: 'Lena Okafor', published: '2017-06-30', price: 15.9 },
{ id: 'b4', title: 'The Clockmaker’s Garden', author: 'Tomas Ruel', published: '2023-01-19', price: 24 },
{ id: 'b5', title: 'Nine Paper Boats', author: 'Ada Lindqvist', published: '2020-09-08', price: 12.75 },
{ id: 'b6', title: 'Winter at Hollow Pier', author: 'Mira Volkov', published: '2024-02-27', price: 19.9 },
];
const columns: ColumnOrGroup<Book>[] = [
{ id: 'title', header: 'Title', flex: 2, pinned: 'start' },
{ id: 'author', header: 'Author', flex: 1 },
{ id: 'published', header: 'Published', type: 'day', width: 130 },
{ id: 'price', header: 'Price', type: 'number', numberFormat: { style: 'currency', currency: 'EUR' } },
];
createGrid<Book>(document.getElementById('books')!, {
rows,
columns,
getRowId: (b) => b.id,
messages: EN,
locale: 'en-GB',
label: 'Books',
});

Three settings are required: columns, getRowId (a stable, unique id per row) and messages (the grid ships no language by default; EN, FR and AR are in @dzgrid/dom/locales/*). The grid injects its own layout CSS; @dzgrid/theme/dzgrid.css makes it look finished (borders, colours, focus ring). Click a title to sort; Tab reaches the grid, the arrow keys move from cell to cell.

Each feature is a function from its own entry, given once in features. A feature that shows text needs its text bundle merged into messages; a missing bundle throws when the grid is made, with the list of missing keys.

import { createGrid, type GridMessages } from '@dzgrid/dom';
import { filters } from '@dzgrid/dom/filters-base';
import { pagination } from '@dzgrid/dom/pagination';
import { EN } from '@dzgrid/dom/locales/en';
import { EN_FILTERS } from '@dzgrid/dom/locales/en-filters';
import { EN_PAGINATION } from '@dzgrid/dom/locales/en-pagination';
type Plant = { id: number; name: string; height: number };
const messages: GridMessages = { ...EN, text: { ...EN.text, ...EN_FILTERS.text, ...EN_PAGINATION.text } };
createGrid<Plant>(document.getElementById('plants')!, {
rows: [{ id: 1, name: 'Blue fern', height: 40 }],
columns: [{ id: 'name', header: 'Name' }, { id: 'height', header: 'Height (cm)', type: 'number' }],
getRowId: (p) => p.id,
messages,
pagination: { pageSize: 50 },
features: [filters(), pagination()],
});

features is read once, when the grid is made. Most other settings are live (see below).

onEvent receives every grid event; switch on type. Problems (a bad column, a bad filter, a server answer that cannot be read) arrive as a problems event: log them while you develop.

import { createGrid } from '@dzgrid/dom';
import { EN } from '@dzgrid/dom/locales/en';
type Tea = { id: string; name: string };
createGrid<Tea>(document.getElementById('teas')!, {
rows: [{ id: 't1', name: 'Smoked oolong' }],
columns: [{ id: 'name', header: 'Tea' }],
getRowId: (t) => t.id,
messages: EN,
selection: { mode: 'multi', checkbox: true },
onEvent: (e) => {
if (e.type === 'rowClicked' && e.row.kind === 'data') console.log('clicked', e.row.data.name);
if (e.type === 'selectionChanged') console.log('selected', e.count);
if (e.type === 'problems') for (const p of e.problems) console.warn('dzgrid', p.code, p.path, p.detail);
},
});

The same events can be heard later with grid.kernel.subscribe(fn), which returns a function that stops listening.

grid.update(patch) changes settings. Give new arrays and objects, never mutated ones: rows are matched by their id, so a new array keeps the focus, the selection and the open rows of the rows that are still there.

import { createGrid } from '@dzgrid/dom';
import { dataKey } from '@dzgrid/core';
import { EN } from '@dzgrid/dom/locales/en';
type Kite = { id: string; name: string; span: number };
let kites: Kite[] = [{ id: 'k1', name: 'Red falcon', span: 120 }];
const grid = createGrid<Kite>(document.getElementById('kites')!, {
rows: kites,
columns: [{ id: 'name', header: 'Kite' }, { id: 'span', header: 'Span (cm)', type: 'number' }],
getRowId: (k) => k.id,
messages: EN,
});
kites = [...kites, { id: 'k2', name: 'Paper moth', span: 80 }];
grid.update({ rows: kites, density: 'compact' });
grid.kernel.dispatch({ type: 'sort.set', sort: [{ column: 'span', dir: 'desc' }] }, 'api');
const key = dataKey('k2');
if (key) grid.api.focusCell(key, 'name');
// When the page goes away:
grid.destroy();

Commands (grid.kernel.dispatch) take a RowKey (from dataKey(id)), never a position: positions change with every sort and filter.