Active Foundation · Example
Server mode against a real route
Server mode against a real routeServerCalls a real route of this site, which does the work on the server.
Requests (POST /api/examples/grid/orders)
Full source examples/grid/server-orders.tsx
// apps/docs/examples/grid/server-orders.tsx'use client';
import '@workspace/active-grid/styles.css';
import {useCallback, useState} from 'react';import type {Order} from '@/lib/examples/orders';
import { composePresentationContext, toIanaTimeZone,} from '@workspace/active-format';import { ActiveGrid, createColumnHelper, englishGridTranslator, globalField, scalarField, type GridColumnDef, type PagedResponse, type ServerFetchFunction,} from '@workspace/active-grid';
const presentation = composePresentationContext( 'en-GB', null, toIanaTimeZone('Europe/Amsterdam'),);
const ch = createColumnHelper<Order>();// In server mode the column contract (`server`) declares what the server can// sort and filter; the grid offers exactly that, and nothing the server would// refuse.const columns: GridColumnDef<Order>[] = [ ch.accessor('id', { header: 'Order', meta: {filterType: 'text'}, server: {...scalarField(), global: globalField()}, }), ch.accessor('customer', { header: 'Customer', meta: {filterType: 'text'}, server: {...scalarField(), global: globalField()}, }), ch.accessor('category', { header: 'Category', meta: {filterType: 'select'}, server: {...scalarField(), global: globalField()}, }), ch.accessor('total', { header: 'Total', type: 'currency', meta: {currencyCode: 'EUR', filterType: 'number'}, server: scalarField(), }), ch.accessor('orderDate', { header: 'Ordered', type: 'date', meta: {filterType: 'date'}, server: scalarField(), }), ch.accessor('status', { header: 'Status', meta: {filterType: 'select'}, server: scalarField(), }),];
interface Exchange { n: number; request: string; answer: string;}
/** * Server mode against a real route of this site. The browser sends the * grid's request (page, block size, sorting, filters, search); the server * filters, sorts and pages 5,000 orders and answers with one block. */export default function ServerOrders() { const [log, setLog] = useState<Exchange[]>([]); const fetchOrders = useCallback<ServerFetchFunction<Order>>( async ({signal, ...params}) => { const body = JSON.stringify(params); const response = await fetch('/api/examples/grid/orders', { method: 'POST', headers: {'Content-Type': 'application/json'}, body, signal, }); const text = await response.text(); if (!response.ok) throw new Error(`The server refused: ${text}`); const page = JSON.parse(text) as PagedResponse<Order>; setLog((list) => [ { n: (list[0]?.n ?? 0) + 1, request: body, answer: `${page.data.length} rows of ${page.totalCount}, ${(text.length / 1024).toFixed(1)} kB`, }, ...list, ].slice(0, 5), ); return page; }, [], ); return ( <div className="flex flex-col gap-4"> <ActiveGrid<Order> gridId="docs-server-orders" mode="server" fetchFn={fetchOrders} columns={columns} height={380} locale={presentation} t={englishGridTranslator} pagination={{enabled: true, pageSizeOptions: [10, 25, 50]}} toolbar={{search: {enabled: true}}} serverOptions={{blockSize: 50, retryCount: 0}} /> <section aria-label="Requests to the server" data-request-log> <h3 className="mb-2 text-sm font-semibold"> Requests (POST /api/examples/grid/orders) </h3> <ol className="flex flex-col gap-2"> {log.map((exchange) => ( <li key={exchange.n} className="bg-muted rounded-md p-2 font-mono text-xs break-all" data-request={exchange.n} > <div> #{exchange.n} → {exchange.request} </div> <div className="text-muted-foreground">← {exchange.answer}</div> </li> ))} </ol> </section> </div> );}