Для конечных пользователей (операторы, инженеры в UI): User-Guide.
Прикладной гайд «как использовать API», а не протокол по проводам. Wire-формат (пути, коды ошибок, envelope) —
NOVA-Grid-API-Spec.md/NOVA-Grid-API-Spec-Backend.md. Соответствие TS↔HTTP вcreateHttpDataSource—NOVA-Grid-HTTP-Client.md. Семантика фильтров —NOVA-Grid-Filter-Data-Contract.md. Источник типов:packages/grid/src/data-source.ts.
1. Модель: DataSource<Row>
Grid никогда не обращается к сети напрямую — он работает с любым объектом,
реализующим контракт DataSource<Row> (packages/grid/src/data-source.ts).
Единственный обязательный метод — getPage; всё остальное опционально и
включает/выключает соответствующие возможности UI (панель фильтров, групповые
действия, сохранённые наборы и т.д. — К-10/К-11 в NOVA-Grid-TZ-v1.4.md §6).
В репозитории есть две готовые реализации (packages/grid/src):
| Реализация | Файл | Когда использовать | |
|---|---|---|---|
|
|
демки, тесты, Storybook — реализует весь контракт, включая |
|
|
|
реальный backend по протоколу |
2. Быстрый старт: mock-источник
import { createMockDataSource } from "@nova/grid";
import { Grid } from "@nova/kit";
// createMockDataSource(rows, options) — возвращает DataSource<Row>,
// расширенный рычагами insert/patch/remove/snapshot (не часть контракта,
// только для демо/тестов — MockDataSourceControls).
const dataSource = createMockDataSource<Item>(initialRows, {
getId: (row) => String(row.id),
});
const grid = new Grid<Item>(host, {
id: "my-grid",
columns,
dataSource,
getRowId: (row) => String(row.id),
pageSize: 50,
});
grid.start();
dataSource.insert(newRow); // источник «меняется сам по себе», Grid увидит это через getPage/subscribe
Мок сам решает фильтрацию/сортировку/поиск в памяти; при необходимости
кастомной семантики полей передавайте evaluateFieldCondition,
matchesSearch и т.п. (см. MockDataSourceOptions в mock-data-source.ts).
3. Подключение к реальному backend
import { createHttpDataSource } from "@nova/grid";
const dataSource = createHttpDataSource<Item>({
baseUrl: "http://127.0.0.1:8028",
entity: "items", // слаг сущности, /api/v1/{entity}/...
getApiKey: () => apiKey, // вызывается заново перед каждым запросом
lang: "ru", // опционально — прокидывается в getPage как query lang
});
const grid = new Grid<Item>(host, {
id: "items-grid",
columns,
dataSource,
getRowId: (row) => String(row.id),
pageSize: 50,
enableToolbar: true,
enableRowSelection: true,
});
grid.start();
Рабочий полный пример — apps/showcase/pages/grid-live.ts (маршрут /grid-live,
включая pre-flight-проверку соединения и обвязку rowEditing.onSave).
getApiKey — функция, а не строка: ключ можно ротировать между запросами
без пересоздания dataSource.
4. Загрузка страницы: getPage
Grid сам вызывает getPage при скролле/пагинации/смене фильтров — вручную
дёргать его не нужно, но полезно знать форму запроса/ответа для отладки или
pre-flight-проверок:
const response = await dataSource.getPage({
offset: 0, // 0-based, сколько строк пропустить
pageSize: 50,
sort: [{ field: "updatedAt", direction: "desc" }],
filters: [{ kind: "field", field: "status", operator: "eq", value: "normal" }],
combinator: "and", // "and" | "or", по умолчанию "and"
search: "alpha",
signal: abortController.signal, // опционально — отмена запроса
});
// response: { records, hasMore, total }
filters поддерживает два вида условий — FieldCondition (поле/оператор/
значение) и NamedCondition (именованное условие с параметрами); за их
семантику отвечает конкретный источник (NOVA-Grid-Filter-Data-Contract.md).
Как описать FilterSchema в TypeScript (поля, операторы, named, enum) —
NOVA-Grid-Filter-Schema-Frontend.md.
5. Метаданные для UI фильтров
Опциональные методы — Grid включает соответствующие части тулбара, только если источник их реализует:
const schema = await dataSource.getFilterSchema?.(); // доступные поля/операторы
const hints = await dataSource.getColumnHints?.(); // ширины/порядок/pinning по умолчанию
const options = await dataSource.getEnumValues?.({
field: "status",
filters: [], // остальные активные условия — для контекстных значений
search: "nor",
});
Подробный гайд по сборке объекта схемы — NOVA-Grid-Filter-Schema-Frontend.md.
== 6. Персональные настройки колонок: columnSettings
columnSettings (get/save) — часть контракта DataSource<Row>
(data-source.ts:171) и backend его реализует (GET/PUT
/api/v1/{entity}/column-settings). createHttpDataSource реализует
columnSettings через DTO-мост (ColumnSettings.columns ↔ wire-ключ
columnSettings, см. NOVA-Grid-HTTP-Client.md); мок
(createMockDataSource) реализует то же в памяти и годится как эталон
поведения.
7. Изменение записи: create / update / delete
Каждый метод — опционален (источник может быть read-only) и возвращает
OperationResult<T>, а не бросает исключение при отказе в праве:
type OperationResult<T> =
| { status: "ok"; data: T }
| { status: "forbidden"; message?: string };
const result = await dataSource.update!(rowId, { status: "critical" });
if (result.status === "forbidden") {
throw new Error(result.message ?? "Изменение отклонено сервером");
}
Реальные сетевые/авторизационные ошибки (4xx/5xx) — это исключение
HttpDataSourceError (см. §10), а не status: "forbidden": различие между
«транспорт отказал» и «бизнес-правило отказало» важно для UI-обработки.
8. Массовые операции: bulkUpdate / bulkDelete
Принимают не список id, а декларативную выборку SelectionDescriptor — это
позволяет применить операцию к «всем, кроме N» без пересылки списка:
await dataSource.bulkUpdate!(
{ mode: "all", exclude: ["5", "9"], filters: [], search: "" },
{ status: "critical" },
);
await dataSource.bulkDelete!({ mode: "manual", include: ["1", "2", "3"] });
Оба возвращают OperationResult<{ affected: number }>.
9. Сохранённые наборы: savedSets
const sets = await dataSource.savedSets?.list();
const result = await dataSource.savedSets?.save?.({
name: "Критичные EU",
scope: "personal",
filters: [...],
search: "",
sort: [...],
columns: [...],
});
if (result?.status === "ok") {
await dataSource.savedSets?.delete?.(result.data.id);
}
save/delete дополнительно опциональны внутри savedSets — источник
вправе отдавать наборы только на чтение.
10. Обработка ошибок HTTP-источника
createHttpDataSource бросает HttpDataSourceError на любой не-2xx ответ:
import { HttpDataSourceError } from "@nova/grid";
try {
await dataSource.getPage(request);
} catch (err) {
if (err instanceof HttpDataSourceError) {
// err.status, err.code (например "UNAUTHORIZED"), err.message, err.method, err.url
}
throw err;
}
AbortError (отмена через request.signal) долетает как есть — Grid
полагается на это в собственном load-controller и не должен гаситься здесь.
11. Realtime: subscribe
Часть контракта (точка расширения на будущее), но не реализована в
createHttpDataSource — на живом backend нет маршрута /events (SSE). Мок
реализует subscribe полностью — удобно для демо push-обновлений. На
реальном backend полагайтесь на обычный poll/reload после write.
12. Куда дальше
| Вопрос | Документ | |
|---|---|---|
Точные пути, коды ошибок, параметры запроса |
|
|
То же с точки зрения backend + черновик OpenAPI |
|
|
Соответствие |
|
|
Семантика операторов фильтрации, |
|
|
Как описать схему фильтров в TS (frontend) |
|
|
Общая архитектура Grid-компонента |
|
|
Что не реализовано и запланировано |
|