Pagination
Client paging with a footer that counts, plus the two hooks a server row model needs. Pagination is a windowing stage, so it runs last, over rows that filtering and sorting have already settled.
Basic Usage
The footer shows the range, the total and a page-size select. Filter the grid and watch both the total and the page reset.
<!-- pageSize is all pagination needs in the shorthand form -->
<DataGrid data={people} {columns} {getRowId} pageSize={8} toolbar />
<script lang="ts">
import { createDataGrid, pagination } from '@sv5ui/datagrid';
// Registered by hand, with a starting page
const grid = createDataGrid<Person>({
data: people,
columns,
getRowId,
features: [pagination({ pageSize: 8, page: 2 })]
});
</script>Choosing the Page Sizes
Compose the chrome yourself when the default footer is not what you want. The status bar and the footer are separate parts, so either can be dropped or moved.
<!-- The footer comes with DataGrid. Compose it yourself to choose the
page-size options or to put it somewhere else. -->
<Grid.Root {grid}>
<Grid.Viewport>
<Grid.Header />
<Grid.Body />
</Grid.Viewport>
<Grid.StatusBar />
<Grid.Pagination pageSizes={[5, 10, 25]} />
</Grid.Root>Resets and Clamping
Sorting or filtering returns the grid to page 1, because page 7 of a list the user has just narrowed is nowhere in particular. The page is also clamped when it is read rather than when it is set, so rows disappearing underneath it can never strand anyone on an empty page.
Press the buttons in order. Page 6 of 6 becomes page 1 of 2 the moment the filter lands, and the badge shows the page the grid reports rather than the one it was last told.
// Sorting or filtering sends the grid back to page 1, because page 7 of
// a list the user has just narrowed is nowhere in particular.
grid.events.on('sortChanged', () => {/* page is already 1 */});
grid.events.on('filterChanged', () => {/* page is already 1 */});
// The page also clamps on read rather than on write: rows can disappear
// without going through setPage, and a page past the end would strand the
// user on empty rows.Driving the Pages
Page size is a preference and is persisted; the page number is not, because restoring page 7 of a list the user has since filtered lands them nowhere. The grid below has no footer at all, so every move comes from a call.
import { getPagination } from '@sv5ui/datagrid';
const paging = getPagination(grid);
paging?.page; // clamped on read, so it never points past the end
paging?.pageSize; // number, or null for "everything on one page"
paging?.pageCount;
paging?.total; // rowCount when set, otherwise grid.totalRows
paging?.server; // true when rowModel is 'server'
paging?.setPage(3);
paging?.setPageSize(25);
paging?.setPageSize(null); // one page, no slicing
paging?.setRowCount(1042); // what the server says the total is
// The same three through grid.api
grid.api.setPage?.(3);
grid.api.setPageSize?.(25);
grid.api.setRowCount?.(1042);
grid.events.on('pageChanged', ({ page, pageSize }) => console.log(page, pageSize));Server Paging
Under rowModel: 'server' the windowing stage passes rows through untouched, because data already holds one page. The feature stays registered, because its state and its footer are what
the fetch listens to.
setRowCount is what makes the footer count against the whole set rather than the page in hand.
import { createDataGrid, filtering, pagination, sorting } from '@sv5ui/datagrid';
// data holds exactly one page; the pipeline passes it through untouched
const grid = createDataGrid<Person>({
columns,
data: [],
getRowId,
rowModel: 'server',
features: [sorting(), filtering(), pagination({ pageSize: 25 })]
});
for (const event of ['sortChanged', 'filterChanged', 'pageChanged'] as const) {
grid.events.on(event, fetchPage);
}
async function fetchPage() {
const { rows, total } = await api.load({ /* ... */ });
grid.data = rows;
grid.api.setRowCount(total); // what the footer counts against
}Pagination or Virtualization
One window-order feature per grid. The constructor throws rather than letting two stages fight over the same slice.
// Both features claim the windowing stage of the pipeline, so registering
// both throws when the grid is constructed:
//
// Only one window-order feature may be registered
//
createDataGrid<Person>({
columns, data, getRowId,
features: [pagination(), virtualization()] // throws
});
// Pick one per grid. Pagination suits a page of records; virtualization
// suits a scrollable list that keeps going.pagination() Options
| Option | Default |
|---|---|
pageSize | null |
page | 1 |
rowCount | null |
Pagination State
What getPagination(grid) exposes. The three setters are also merged into grid.api.
| Member | Description |
|---|---|
page | The current page, clamped on read so it never points past the end |
pageSize | Rows per page |
pageCount | How many pages the total divides into |
total | rowCount when a server set it, otherwise the filtered row count |
server | True when the grid's rowModel is 'server' |
setPage(n) | Clamps into range and emits pageChanged |
setPageSize(n) | Also returns to page 1 |
setRowCount(n) | What the footer counts against under a server row model |
Grid.Pagination Props
| Prop | Type | Default |
|---|---|---|
pageSizes | number[] | [10, 25, 50, 100] |
class | ClassNameValue | - |