TypeScript
@dzgrid/dom dessine la grille dans n’importe quelle page, sans framework. @dzgrid/core fait les calculs (sans
DOM) et @dzgrid/theme apporte le CSS. Cette page utilise createGrid directement ; les onglets montrent la même
grille avec Angular et avec le composant web.
Installer
Section intitulée « Installer »npm install @dzgrid/core @dzgrid/dom @dzgrid/theme@dzgrid/dom/filters et @dzgrid/dom/editors chargent un calendrier de dates venu de @tzslot/core et
@tzslot/dom : installez-les aussi si vous utilisez ces deux entrées. Sans eux, importez depuis
@dzgrid/dom/filters-base et @dzgrid/dom/editors-base, qui n’ont pas de calendrier : avec filters-base, une
colonne de jours ou d’instants n’a aucun filtre (pas de bouton) et un avertissement missing-feature ; avec
editors-base, elle se modifie en tapant. Les filtres de jours viennent de @dzgrid/dom/filters (ou de
filters-base muni de loadDates).
Une première grille
Section intitulée « Une première grille »La grille remplit son élément hôte, quel que soit son nombre de lignes : donnez une hauteur à l’hôte.
<div id="books" style="block-size: 320px; display: flex; flex-direction: column"></div>Avec 3 lignes ou 10 000, la grille a la hauteur de l’hôte, défile à l’intérieur et ne dessine que les lignes
visibles. Une hauteur fixe, une part de flex ou de grid conviennent toutes ; la colonne flex laisse une barre se
placer au-dessus de la grille. Un hôte sans hauteur (dimensionné par son contenu) fait grandir la grille avec ses
lignes ; elle limite alors sa vue à la hauteur de la fenêtre et signale une fois un événement problems de code
unbounded-viewport (chemin host).
import { createGrid } from '@dzgrid/dom';import type { ColumnOrGroup } from '@dzgrid/core';import { FR } from '@dzgrid/dom/locales/fr';import '@dzgrid/theme/dzgrid.css';
type Book = { id: string; title: string; author: string; published: string; price: number };
const rows: Book[] = [ { id: 'b1', title: 'Le Verger de verre', author: 'Mireille Vautrin', published: '2019-03-14', price: 18.5 }, { id: 'b2', title: 'Sel et lanternes', author: 'Isidore Fenouil', published: '2021-11-02', price: 22 }, { id: 'b3', title: 'Carte des rivières tranquilles', author: 'Léna Okafor', published: '2017-06-30', price: 15.9 }, { id: 'b4', title: 'Le Jardin de l’horloger', author: 'Thomas Ruel', published: '2023-01-19', price: 24 }, { id: 'b5', title: 'Neuf Bateaux de papier', author: 'Adèle Lindqvist', published: '2020-09-08', price: 12.75 }, { id: 'b6', title: 'L’Hiver au quai Creux', author: 'Mireille Vautrin', published: '2024-02-27', price: 19.9 },];
const columns: ColumnOrGroup<Book>[] = [ { id: 'title', header: 'Titre', flex: 2, pinned: 'start' }, { id: 'author', header: 'Auteur', flex: 1 }, { id: 'published', header: 'Parution', type: 'day', width: 130 }, { id: 'price', header: 'Prix', type: 'number', numberFormat: { style: 'currency', currency: 'EUR' } },];
createGrid<Book>(document.getElementById('books')!, { rows, columns, getRowId: (b) => b.id, messages: FR, locale: 'fr-FR', label: 'Livres',});import { Component } from '@angular/core';import { DzGrid, type DzgColumnOrGroup } from '@dzgrid/angular';import { FR } from '@dzgrid/dom/locales/fr';
type Book = { id: string; title: string; author: string; published: string; price: number };
@Component({ selector: 'app-books', imports: [DzGrid], template: ` <div class="grid-box"> <dz-grid [rows]="rows" [columns]="columns" [getRowId]="rowId" [messages]="messages" locale="fr-FR" label="Livres" /> </div>`,})export class Books { readonly rows: Book[] = [ { id: 'b1', title: 'Le Verger de verre', author: 'Mireille Vautrin', published: '2019-03-14', price: 18.5 }, { id: 'b2', title: 'Sel et lanternes', author: 'Isidore Fenouil', published: '2021-11-02', price: 22 }, { id: 'b3', title: 'Carte des rivières tranquilles', author: 'Léna Okafor', published: '2017-06-30', price: 15.9 }, ]; readonly columns: DzgColumnOrGroup<Book>[] = [ { id: 'title', header: 'Titre', flex: 2, pinned: 'start' }, { id: 'author', header: 'Auteur', flex: 1 }, { id: 'published', header: 'Parution', type: 'day', width: 130 }, { id: 'price', header: 'Prix', type: 'number', numberFormat: { style: 'currency', currency: 'EUR' } }, ]; readonly rowId = (b: Book) => b.id; readonly messages = FR;}<dz-grid locale="fr-FR" label="Livres"></dz-grid>import '@dzgrid/element/define';import type { DzGridElement } from '@dzgrid/element';import { FR } from '@dzgrid/dom/locales/fr';
type Book = { id: string; title: string; author: string; published: string; price: number };
const grid = document.querySelector<DzGridElement<Book>>('dz-grid')!;grid.columns = [ { id: 'title', header: 'Titre', flex: 2, pinned: 'start' }, { id: 'author', header: 'Auteur', flex: 1 }, { id: 'published', header: 'Parution', type: 'day', width: 130 }, { id: 'price', header: 'Prix', type: 'number', numberFormat: { style: 'currency', currency: 'EUR' } },];grid.getRowId = (b) => b.id;grid.messages = FR;grid.rows = [ { id: 'b1', title: 'Le Verger de verre', author: 'Mireille Vautrin', published: '2019-03-14', price: 18.5 }, { id: 'b2', title: 'Sel et lanternes', author: 'Isidore Fenouil', published: '2021-11-02', price: 22 },];Trois réglages sont obligatoires : columns, getRowId (un identifiant stable et unique par ligne) et messages
(la grille n’a pas de langue par défaut ; EN, FR et AR sont dans @dzgrid/dom/locales/*). La grille injecte
son propre CSS de mise en page ; @dzgrid/theme/dzgrid.css lui donne son aspect fini (bordures, couleurs, anneau de
focus). Cliquez sur un titre pour trier ; Tab entre dans la grille, les flèches vont de cellule en cellule.
Les fonctions et leurs textes
Section intitulée « Les fonctions et leurs textes »Chaque fonction vient de sa propre entrée et se donne une fois dans features. Une fonction qui affiche du texte a
besoin de son lot de textes fusionné dans messages ; un lot manquant lève une erreur à la création de la grille,
avec la liste des clés manquantes.
import { createGrid, type GridMessages } from '@dzgrid/dom';import { filters } from '@dzgrid/dom/filters-base';import { pagination } from '@dzgrid/dom/pagination';import { FR } from '@dzgrid/dom/locales/fr';import { FR_FILTERS } from '@dzgrid/dom/locales/fr-filters';import { FR_PAGINATION } from '@dzgrid/dom/locales/fr-pagination';
type Plant = { id: number; name: string; height: number };
const messages: GridMessages = { ...FR, text: { ...FR.text, ...FR_FILTERS.text, ...FR_PAGINATION.text } };
createGrid<Plant>(document.getElementById('plants')!, { rows: [{ id: 1, name: 'Fougère bleue', height: 40 }], columns: [{ id: 'name', header: 'Nom' }, { id: 'height', header: 'Hauteur (cm)', type: 'number' }], getRowId: (p) => p.id, messages, pagination: { pageSize: 50 }, features: [filters(), pagination()],});import { Component } from '@angular/core';import { DzGrid, type DzgColumnOrGroup } from '@dzgrid/angular';import { filters } from '@dzgrid/dom/filters-base';import { pagination } from '@dzgrid/dom/pagination';import { FR } from '@dzgrid/dom/locales/fr';import { FR_FILTERS } from '@dzgrid/dom/locales/fr-filters';import { FR_PAGINATION } from '@dzgrid/dom/locales/fr-pagination';
type Plant = { id: number; name: string; height: number };
@Component({ selector: 'app-plants', imports: [DzGrid], template: `<dz-grid [rows]="rows" [columns]="columns" [getRowId]="rowId" [messages]="messages" [features]="features" [pagination]="{ pageSize: 50 }" />`,})export class Plants { readonly rows: Plant[] = [{ id: 1, name: 'Fougère bleue', height: 40 }]; readonly columns: DzgColumnOrGroup<Plant>[] = [{ id: 'name', header: 'Nom' }, { id: 'height', header: 'Hauteur (cm)', type: 'number' }]; readonly rowId = (p: Plant) => p.id; readonly messages = { ...FR, text: { ...FR.text, ...FR_FILTERS.text, ...FR_PAGINATION.text } }; // Un champ, pas écrit dans le gabarit : les fonctions sont lues une fois, à la création de la grille. readonly features = [filters(), pagination()];}import '@dzgrid/element/define';import type { DzGridElement } from '@dzgrid/element';import { filters } from '@dzgrid/dom/filters-base';import { pagination } from '@dzgrid/dom/pagination';import { FR } from '@dzgrid/dom/locales/fr';import { FR_FILTERS } from '@dzgrid/dom/locales/fr-filters';import { FR_PAGINATION } from '@dzgrid/dom/locales/fr-pagination';
type Plant = { id: number; name: string; height: number };
const grid = document.querySelector<DzGridElement<Plant>>('dz-grid')!;grid.columns = [{ id: 'name', header: 'Nom' }, { id: 'height', header: 'Hauteur (cm)', type: 'number' }];grid.getRowId = (p) => p.id;grid.messages = { ...FR, text: { ...FR.text, ...FR_FILTERS.text, ...FR_PAGINATION.text } };grid.features = [filters(), pagination()];grid.settings = { pagination: { pageSize: 50 } };grid.rows = [{ id: 1, name: 'Fougère bleue', height: 40 }];features est lu une fois, à la création de la grille. La plupart des autres réglages sont vivants (voir plus bas).
Événements
Section intitulée « Événements »onEvent reçoit chaque événement de la grille ; aiguillez sur type. Les problèmes (une colonne invalide, un filtre
invalide, une réponse du serveur illisible) arrivent dans un événement problems : affichez-les pendant le
développement.
import { createGrid } from '@dzgrid/dom';import { FR } from '@dzgrid/dom/locales/fr';
type Tea = { id: string; name: string };
createGrid<Tea>(document.getElementById('teas')!, { rows: [{ id: 't1', name: 'Oolong fumé' }], columns: [{ id: 'name', header: 'Thé' }], getRowId: (t) => t.id, messages: FR, selection: { mode: 'multi', checkbox: true }, onEvent: (e) => { if (e.type === 'rowClicked' && e.row.kind === 'data') console.log('cliqué', e.row.data.name); if (e.type === 'selectionChanged') console.log('sélectionnées', e.count); if (e.type === 'problems') for (const p of e.problems) console.warn('dzgrid', p.code, p.path, p.detail); },});import { Component } from '@angular/core';import { DzGrid, type DzgColumnOrGroup } from '@dzgrid/angular';import type { Problem } from '@dzgrid/core';import { FR } from '@dzgrid/dom/locales/fr';
type Tea = { id: string; name: string };
@Component({ selector: 'app-teas', imports: [DzGrid], template: `<dz-grid [rows]="rows" [columns]="columns" [getRowId]="rowId" [messages]="messages" [selection]="{ mode: 'multi', checkbox: true }" (selectionChanged)="count = $event.count" (problems)="onProblems($event)" /> <p>{{ count ?? 'quelques' }} sélectionnées</p>`,})export class Teas { readonly rows: Tea[] = [{ id: 't1', name: 'Oolong fumé' }]; readonly columns: DzgColumnOrGroup<Tea>[] = [{ id: 'name', header: 'Thé' }]; readonly rowId = (t: Tea) => t.id; readonly messages = FR; count: number | null = 0; onProblems(ps: readonly Problem[]) { for (const p of ps) console.warn('dzgrid', p.code, p.path, p.detail); }}import '@dzgrid/element/define';import type { DzGridElement } from '@dzgrid/element';import type { GridEvent } from '@dzgrid/core';import { FR } from '@dzgrid/dom/locales/fr';
type Tea = { id: string; name: string };
const grid = document.querySelector<DzGridElement<Tea>>('dz-grid')!;grid.columns = [{ id: 'name', header: 'Thé' }];grid.getRowId = (t) => t.id;grid.messages = FR;grid.settings = { selection: { mode: 'multi', checkbox: true } };grid.rows = [{ id: 't1', name: 'Oolong fumé' }];
// Chaque événement de la grille, en `dzg-` suivi de son type en kebab-case ; l’événement lui-même est le detail.grid.addEventListener('dzg-selection-changed', (e) => { const event = (e as CustomEvent<GridEvent<Tea>>).detail; if (event.type === 'selectionChanged') console.log('sélectionnées', event.count);});grid.addEventListener('dzg-problems', (e) => { const event = (e as CustomEvent<GridEvent<Tea>>).detail; if (event.type === 'problems') for (const p of event.problems) console.warn('dzgrid', p.code, p.path, p.detail);});Les mêmes événements s’écoutent aussi plus tard avec grid.kernel.subscribe(fn), qui rend une fonction pour arrêter
l’écoute.
Mettre à jour et piloter la grille
Section intitulée « Mettre à jour et piloter la grille »grid.update(patch) change des réglages. Donnez de nouveaux tableaux et objets, jamais des objets modifiés : les
lignes sont reconnues par leur identifiant, si bien qu’un nouveau tableau garde le focus, la sélection et les lignes
ouvertes de celles qui restent.
import { createGrid } from '@dzgrid/dom';import { dataKey } from '@dzgrid/core';import { FR } from '@dzgrid/dom/locales/fr';
type Kite = { id: string; name: string; span: number };let kites: Kite[] = [{ id: 'k1', name: 'Faucon rouge', span: 120 }];
const grid = createGrid<Kite>(document.getElementById('kites')!, { rows: kites, columns: [{ id: 'name', header: 'Cerf-volant' }, { id: 'span', header: 'Envergure (cm)', type: 'number' }], getRowId: (k) => k.id, messages: FR,});
kites = [...kites, { id: 'k2', name: 'Phalène de papier', 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');
// Quand la page s’en va :grid.destroy();import { Component, signal } from '@angular/core';import { DzGrid, DzgController, type DzgColumnOrGroup } from '@dzgrid/angular';import { dataKey } from '@dzgrid/core';import { FR } from '@dzgrid/dom/locales/fr';
type Kite = { id: string; name: string; span: number };
@Component({ selector: 'app-kites', imports: [DzGrid], template: `<dz-grid [rows]="rows()" [columns]="columns" [getRowId]="rowId" [messages]="messages" density="compact" (gridReady)="ctrl = $event" /> <button type="button" (click)="add()">Ajouter un cerf-volant</button>`,})export class Kites { readonly rows = signal<Kite[]>([{ id: 'k1', name: 'Faucon rouge', span: 120 }]); readonly columns: DzgColumnOrGroup<Kite>[] = [{ id: 'name', header: 'Cerf-volant' }, { id: 'span', header: 'Envergure (cm)', type: 'number' }]; readonly rowId = (k: Kite) => k.id; readonly messages = FR; ctrl: DzgController<Kite> | null = null;
add() { this.rows.update((rs) => [...rs, { id: 'k2', name: 'Phalène de papier', span: 80 }]); this.ctrl?.dispatch({ type: 'sort.set', sort: [{ column: 'span', dir: 'desc' }] }); const key = dataKey('k2'); if (key) this.ctrl?.ensureVisible(key, 'name'); }}import '@dzgrid/element/define';import type { DzGridElement } from '@dzgrid/element';import { FR } from '@dzgrid/dom/locales/fr';
type Kite = { id: string; name: string; span: number };
const grid = document.querySelector<DzGridElement<Kite>>('dz-grid')!;grid.columns = [{ id: 'name', header: 'Cerf-volant' }, { id: 'span', header: 'Envergure (cm)', type: 'number' }];grid.getRowId = (k) => k.id;grid.messages = FR;grid.rows = [{ id: 'k1', name: 'Faucon rouge', span: 120 }];
await grid.ready;grid.rows = [...(grid.rows ?? []), { id: 'k2', name: 'Phalène de papier', span: 80 }];grid.update({ density: 'compact' });grid.grid?.kernel.dispatch({ type: 'sort.set', sort: [{ column: 'span', dir: 'desc' }] }, 'api');Les commandes (grid.kernel.dispatch) prennent une RowKey (de dataKey(id)), jamais une position : les positions
changent à chaque tri et à chaque filtre.
- Les pages Angular et composant web pour les deux enveloppes.
- Les guides, tâche par tâche, en commençant par les colonnes.