Angular
@dzgrid/angular enveloppe la grille DOM dans un composant autonome <dz-grid> aux entrées en signaux, pour
Angular 19 à 22, sans zone d’abord (zone.js marche aussi). Quand la même chose se fait sans Angular, les onglets la
montrent en TypeScript et avec le composant web.
Installer
Section intitulée « Installer »npm install @dzgrid/core @dzgrid/dom @dzgrid/theme @dzgrid/angularDans angular.json, chargez le thème avant vos propres styles globaux :
"styles": ["node_modules/@dzgrid/theme/dzgrid.css", "src/styles.css"]L’hôte <dz-grid> est en display: contents (il n’a pas de boîte à lui) : dimensionnez son parent, en CSS
global — les styles émulés d’un composant n’atteignent pas le DOM de la grille.
.grid-box { block-size: 480px; display: flex; flex-direction: column; }.grid-box > dz-grid > .dzg { flex: 1; }Une première grille
Section intitulée « Une première grille »import { createGrid } from '@dzgrid/dom';import type { ColumnOrGroup } from '@dzgrid/core';import { FR } from '@dzgrid/dom/locales/fr';
type Comet = { id: string; name: string; seen: string; period: number };
const columns: ColumnOrGroup<Comet>[] = [ { id: 'name', header: 'Comète', flex: 2 }, { id: 'seen', header: 'Dernier passage', type: 'day', width: 140 }, { id: 'period', header: 'Période (ans)', type: 'number', numberFormat: { maximumFractionDigits: 1 } },];
createGrid<Comet>(document.querySelector<HTMLElement>('.grid-box')!, { rows: [ { id: 'c1', name: 'Vantor-Orme', seen: '2024-08-19', period: 71.4 }, { id: 'c2', name: 'Ossian Bleue', seen: '2025-01-03', period: 6.2 }, ], columns, getRowId: (c) => c.id, messages: FR, locale: 'fr-FR', label: 'Comètes',});import { Component } from '@angular/core';import { DzGrid, type DzgColumnOrGroup } from '@dzgrid/angular';import { FR } from '@dzgrid/dom/locales/fr';
type Comet = { id: string; name: string; seen: string; period: number };
@Component({ selector: 'app-comets', imports: [DzGrid], template: ` <div class="grid-box"> <dz-grid [rows]="rows" [columns]="columns" [getRowId]="rowId" [messages]="messages" locale="fr-FR" label="Comètes" /> </div>`,})export class Comets { readonly rows: Comet[] = [ { id: 'c1', name: 'Vantor-Orme', seen: '2024-08-19', period: 71.4 }, { id: 'c2', name: 'Ossian Bleue', seen: '2025-01-03', period: 6.2 }, { id: 'c3', name: 'Petite Herse', seen: '2023-11-27', period: 13.8 }, ]; readonly columns: DzgColumnOrGroup<Comet>[] = [ { id: 'name', header: 'Comète', flex: 2 }, { id: 'seen', header: 'Dernier passage', type: 'day', width: 140 }, { id: 'period', header: 'Période (ans)', type: 'number', numberFormat: { maximumFractionDigits: 1 } }, ]; readonly rowId = (c: Comet) => c.id; readonly messages = FR;}import '@dzgrid/element/define';import type { DzGridElement } from '@dzgrid/element';import { FR } from '@dzgrid/dom/locales/fr';
type Comet = { id: string; name: string; seen: string; period: number };
const grid = document.querySelector<DzGridElement<Comet>>('dz-grid')!;grid.columns = [ { id: 'name', header: 'Comète', flex: 2 }, { id: 'seen', header: 'Dernier passage', type: 'day', width: 140 }, { id: 'period', header: 'Période (ans)', type: 'number', numberFormat: { maximumFractionDigits: 1 } },];grid.getRowId = (c) => c.id;grid.messages = FR;grid.rows = [ { id: 'c1', name: 'Vantor-Orme', seen: '2024-08-19', period: 71.4 }, { id: 'c2', name: 'Ossian Bleue', seen: '2025-01-03', period: 6.2 },];Dessinée sur cette page par @dzgrid/dom, la grille qu’enveloppe <dz-grid>.
columns, getRowId et messages sont obligatoires (messages peut venir de provideDzGrid, plus bas). Typez le
tableau de colonnes DzgColumnOrGroup<T>[] : un littéral non typé cache les clés inconnues.
Les entrées sont comparées par identité
Section intitulée « Les entrées sont comparées par identité »Les entrées sont des signaux. Donnez un nouveau tableau ou objet pour changer quelque chose
(rows.update((rs) => [...rs, x])), jamais un objet modifié. Gardez les fonctions (getRowId, rowClass) et le
tableau features en champs de la classe : features est lu une fois, à la création de la grille.
import { createGrid } from '@dzgrid/dom';import { filters } from '@dzgrid/dom/filters-base';import { FR } from '@dzgrid/dom/locales/fr';import { FR_FILTERS } from '@dzgrid/dom/locales/fr-filters';
type Lamp = { id: number; model: string; watts: number };let rows: Lamp[] = [{ id: 1, model: 'Phalène 3', watts: 40 }];
const grid = createGrid<Lamp>(document.getElementById('lamps')!, { rows, columns: [{ id: 'model', header: 'Modèle' }, { id: 'watts', header: 'Watts', type: 'number' }], getRowId: (l) => l.id, messages: { ...FR, text: { ...FR.text, ...FR_FILTERS.text } }, features: [filters()], selection: { mode: 'multi', checkbox: true }, onEvent: (e) => { if (e.type === 'selectionChanged') console.log(e.count ?? 'quelques', 'sélectionnées'); },});
// Un nouveau tableau, jamais l’ancien modifié.rows = [...rows, { id: rows.length + 1, model: 'Héron 1', watts: 25 }];grid.update({ rows });import { Component, signal } from '@angular/core';import { DzGrid, type DzgColumnOrGroup } from '@dzgrid/angular';import type { Problem } from '@dzgrid/core';import { filters } from '@dzgrid/dom/filters-base';import { FR } from '@dzgrid/dom/locales/fr';import { FR_FILTERS } from '@dzgrid/dom/locales/fr-filters';
type Lamp = { id: number; model: string; watts: number };
@Component({ selector: 'app-lamps', imports: [DzGrid], template: ` <button type="button" (click)="add()">Ajouter une lampe</button> <div class="grid-box"> <dz-grid [rows]="rows()" [columns]="columns" [getRowId]="rowId" [messages]="messages" [features]="features" [selection]="{ mode: 'multi', checkbox: true }" [(quick)]="quick" (selectionChanged)="count = $event.count" (problems)="onProblems($event)" /> </div> <p>{{ count ?? 'quelques' }} sélectionnées</p>`,})export class Lamps { readonly rows = signal<Lamp[]>([{ id: 1, model: 'Phalène 3', watts: 40 }]); readonly columns: DzgColumnOrGroup<Lamp>[] = [{ id: 'model', header: 'Modèle' }, { id: 'watts', header: 'Watts', type: 'number' }]; readonly rowId = (l: Lamp) => l.id; readonly messages = { ...FR, text: { ...FR.text, ...FR_FILTERS.text } }; readonly features = [filters()]; quick = ''; count: number | null = 0; add() { this.rows.update((rs) => [...rs, { id: rs.length + 1, model: 'Héron 1', watts: 25 }]); } 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 { filters } from '@dzgrid/dom/filters-base';import { FR } from '@dzgrid/dom/locales/fr';import { FR_FILTERS } from '@dzgrid/dom/locales/fr-filters';
type Lamp = { id: number; model: string; watts: number };
const grid = document.querySelector<DzGridElement<Lamp>>('dz-grid')!;grid.columns = [{ id: 'model', header: 'Modèle' }, { id: 'watts', header: 'Watts', type: 'number' }];grid.getRowId = (l) => l.id;grid.messages = { ...FR, text: { ...FR.text, ...FR_FILTERS.text } };grid.features = [filters()];grid.settings = { selection: { mode: 'multi', checkbox: true } };grid.rows = [{ id: 1, model: 'Phalène 3', watts: 40 }];
// Les propriétés aussi sont comparées par identité : un nouveau tableau, jamais l’ancien modifié.grid.rows = [...(grid.rows ?? []), { id: 2, model: 'Héron 1', watts: 25 }];Modèles à double sens : [(quick)], [(filter)], [(selected)], [(expanded)], [(groupBy)]. Non liés, c’est
la grille qui tient cet état.
Valeurs par défaut de l’application
Section intitulée « Valeurs par défaut de l’application »provideDzGrid donne à chaque <dz-grid> de l’application ses textes et ses réglages par défaut. Une liaison sur
la balise l’emporte sur une valeur par défaut.
import { ApplicationConfig } from '@angular/core';import { provideDzGrid } from '@dzgrid/angular';import { FR } from '@dzgrid/dom/locales/fr';
export const appConfig: ApplicationConfig = { providers: [provideDzGrid({ messages: FR, defaults: { locale: 'fr-FR', timeZone: 'Europe/Paris', density: 'compact' } })],};Le contrôleur
Section intitulée « Le contrôleur »(gridReady) remet un DzgController : commandes, focus, transactions, et l’api propre à chaque fonction par use.
Sans Angular, la grille elle-même offre la même chose : grid.kernel et grid.api.
import { createGrid } from '@dzgrid/dom';import { dataKey } from '@dzgrid/core';import { FR } from '@dzgrid/dom/locales/fr';
type Owl = { id: string; name: string; age: number };
const grid = createGrid<Owl>(document.getElementById('owls')!, { rows: [{ id: 'o1', name: 'Pépin', age: 3 }, { id: 'o2', name: 'Ronce', age: 9 }], columns: [{ id: 'name', header: 'Chouette' }, { id: 'age', header: 'Âge', type: 'number' }], getRowId: (o) => o.id, messages: FR, selection: { mode: 'multi' },});
grid.kernel.dispatch({ type: 'sort.set', sort: [{ column: 'age', dir: 'desc' }] }, 'api');const key = dataKey('o2');if (key) { grid.api.ensureVisible(key, 'name'); grid.kernel.dispatch({ type: 'selection.only', row: key }, 'api');}grid.kernel.rows.applyTransaction({ patch: [{ id: 'o1', set: { age: 4 } }] });import { Component } from '@angular/core';import { DzGrid, DzgController, selectedIds, type DzgColumnOrGroup } from '@dzgrid/angular';import { dataKey } from '@dzgrid/core';import { FR } from '@dzgrid/dom/locales/fr';
type Owl = { id: string; name: string; age: number };
@Component({ selector: 'app-owls', imports: [DzGrid], template: `<dz-grid [rows]="rows" [columns]="columns" [getRowId]="rowId" [messages]="messages" [selection]="{ mode: 'multi' }" (gridReady)="ctrl = $event" /> <button type="button" (click)="oldestFirst()">Les plus âgées d’abord</button>`,})export class Owls { readonly rows: Owl[] = [{ id: 'o1', name: 'Pépin', age: 3 }, { id: 'o2', name: 'Ronce', age: 9 }]; readonly columns: DzgColumnOrGroup<Owl>[] = [{ id: 'name', header: 'Chouette' }, { id: 'age', header: 'Âge', type: 'number' }]; readonly rowId = (o: Owl) => o.id; readonly messages = FR; ctrl: DzgController<Owl> | null = null;
oldestFirst() { this.ctrl?.dispatch({ type: 'sort.set', sort: [{ column: 'age', dir: 'desc' }] }); const key = dataKey('o2'); if (key) { this.ctrl?.ensureVisible(key, 'name'); this.ctrl?.dispatch({ type: 'selection.only', row: key }); } this.ctrl?.applyTransaction({ patch: [{ id: 'o1', set: { age: 4 } }] }); const chosen = this.ctrl && selectedIds(this.ctrl); // { mode, ids, query } — des identifiants, prêts pour un serveur console.log(chosen); }}import '@dzgrid/element/define';import type { DzGridElement } from '@dzgrid/element';import { dataKey } from '@dzgrid/core';import { FR } from '@dzgrid/dom/locales/fr';
type Owl = { id: string; name: string; age: number };
const owls = document.querySelector<DzGridElement<Owl>>('dz-grid')!;owls.columns = [{ id: 'name', header: 'Chouette' }, { id: 'age', header: 'Âge', type: 'number' }];owls.getRowId = (o) => o.id;owls.messages = FR;owls.settings = { selection: { mode: 'multi' } };owls.rows = [{ id: 'o1', name: 'Pépin', age: 3 }, { id: 'o2', name: 'Ronce', age: 9 }];
await owls.ready;// `grid` est la GridInstance tant que l’élément est dessiné.const grid = owls.grid;const key = dataKey('o2');if (grid && key) { grid.kernel.dispatch({ type: 'sort.set', sort: [{ column: 'age', dir: 'desc' }] }, 'api'); grid.api.ensureVisible(key, 'name'); grid.kernel.dispatch({ type: 'selection.only', row: key }, 'api');}Des parties en composants
Section intitulée « Des parties en composants »Une cellule (ou un en-tête, une infobulle, un éditeur, un panneau de détail…) peut être un composant Angular avec
une entrée signal nommée cell. Les instances sont réutilisées d’une ligne à l’autre : tirez tout de cell().
import { ChangeDetectionStrategy, Component, input } from '@angular/core';import { DzGrid, type DzgColumnOrGroup, type DzgPartContext } from '@dzgrid/angular';import { FR } from '@dzgrid/dom/locales/fr';
type Robot = { id: string; name: string; state: 'awake' | 'asleep' };
@Component({ selector: 'app-robot-state', changeDetection: ChangeDetectionStrategy.OnPush, template: `<span>{{ cell().text }}</span> <button type="button" (click)="cell().emit('wake', robot().id)">Réveiller</button>`,})export class RobotState { readonly cell = input.required<DzgPartContext<'cell'>>(); robot(): Robot { return this.cell().row as Robot; }}
@Component({ selector: 'app-robots', imports: [DzGrid], template: `<dz-grid [rows]="rows" [columns]="columns" [getRowId]="rowId" [messages]="messages" (cellAction)="last = $event.name + ' ' + $event.detail" />`,})export class Robots { readonly rows: Robot[] = [{ id: 'r1', name: 'Tic', state: 'asleep' }]; readonly columns: DzgColumnOrGroup<Robot>[] = [{ id: 'name', header: 'Robot' }, { id: 'state', header: 'État', ui: { cell: RobotState } }]; readonly rowId = (r: Robot) => r.id; readonly messages = FR; last = '';}Autres façons de donner une partie : componentPart(slot, Component) dans [parts] ou provideDzGrid({ parts }) ;
un <ng-template dzgPart="name" [slot]="'header'" let-c> dans <dz-grid> (importez DzgPart) ; ou une partie DOM
simple faite avec definePart de @dzgrid/dom.
- La page TypeScript, pour ce que fait la grille sous le composant.
- Les guides, tâche par tâche, en commençant par les colonnes.