Appearance
Pagination
By default all rows are rendered in one scrollable (virtualized) list. Enable pagination if your users prefer pages:
Source
vue
<template>
<div class="demo">
<Datagrid
:columns="columns"
:rows="rows"
:options="options"
v-model:pageIndex="page"
/>
<div class="demo-toolbar">Current page index: {{ page }}</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Datagrid, type ColumnConfig, type GridOptions } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";
const rows = createEmployees(1234);
const page = ref(0);
const columns: Array<ColumnConfig<Employee>> = [
{ field: "id", text: "ID", type: "number", width: 80 },
{ field: "firstName", text: "First name" },
{ field: "lastName", text: "Last name" },
{ field: "department", text: "Department" },
{ field: "salary", text: "Salary", type: "number" },
];
const options: GridOptions<Employee> = {
pagination: { enabled: true, pageSize: 20, pageSizes: [20, 50, 100] },
defaultColumn: { sortable: true, filterable: true, flex: 1 },
};
</script>tsx
import { useState } from "react";
import { Datagrid, type ColumnConfig, type GridOptions } from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";
const rows = createEmployees(1234);
const columns: Array<ColumnConfig<Employee>> = [
{ field: "id", text: "ID", type: "number", width: 80 },
{ field: "firstName", text: "First name" },
{ field: "lastName", text: "Last name" },
{ field: "department", text: "Department" },
{ field: "salary", text: "Salary", type: "number" },
];
const options: GridOptions<Employee> = {
pagination: { enabled: true, pageSize: 20, pageSizes: [20, 50, 100] },
defaultColumn: { sortable: true, filterable: true, flex: 1 },
};
export default function Pagination() {
const [page, setPage] = useState(0);
return (
<div className="demo">
<Datagrid columns={columns} rows={rows} options={options} pageIndex={page} onPageIndexChange={setPage} />
<div className="demo-toolbar">Current page index: {page}</div>
</div>
);
}Options
ts
pagination: {
enabled: true,
pageSize: 25,
pageSizes: [10, 25, 50, 100], // [] hides the page size selector
}Pagination is applied after filtering, sorting and grouping. With server side data the page is loaded from the server.
Switching pagination on and off
The pagination options can be changed at any time, e.g. to let the user choose between pages and one scrollable list. pageSizes: [] hides the page size selector of the pagination bar – here the page size is chosen in the toolbar instead.
Source
vue
<template>
<div class="demo">
<div class="demo-toolbar">
<label><input type="checkbox" v-model="paged" /> Pages</label>
<label>
Rows per page
<select v-model.number="pageSize" :disabled="!paged">
<option :value="10">10</option>
<option :value="15">15</option>
<option :value="30">30</option>
</select>
</label>
</div>
<Datagrid :columns="columns" :rows="rows" :options="options" />
</div>
</template>
<script setup lang="ts">
import { computed, ref } from "vue";
import { Datagrid, formatters, type ColumnConfig, type GridOptions } from "@datagrid/vue-ui";
import { createOrders, type Order } from "./data";
const rows = createOrders(300);
const paged = ref(true);
const pageSize = ref(10);
const columns: Array<ColumnConfig<Order>> = [
{ field: "id", text: "Order", type: "number", width: 90 },
{ field: "customer", text: "Customer", flex: 1 },
{ field: "date", text: "Date", type: "date" },
{ field: "status", text: "Status" },
{ field: "total", text: "Total", type: "number", valueFormatter: formatters.currency("EUR") },
];
const options = computed<GridOptions<Order>>(() => ({
// the page size is chosen outside of the grid, [] hides the selector in the pagination bar
pagination: { enabled: paged.value, pageSize: pageSize.value, pageSizes: [] },
rowId: (data) => data.id,
defaultColumn: { sortable: true, width: 130 },
}));
</script>tsx
import { useMemo, useState } from "react";
import { Datagrid, formatters, type ColumnConfig, type GridOptions } from "@datagrid/react-ui";
import { createOrders, type Order } from "../data";
const rows = createOrders(300);
const columns: Array<ColumnConfig<Order>> = [
{ field: "id", text: "Order", type: "number", width: 90 },
{ field: "customer", text: "Customer", flex: 1 },
{ field: "date", text: "Date", type: "date" },
{ field: "status", text: "Status" },
{ field: "total", text: "Total", type: "number", valueFormatter: formatters.currency("EUR") },
];
export default function PaginationToggle() {
const [paged, setPaged] = useState(true);
const [pageSize, setPageSize] = useState(10);
const options = useMemo<GridOptions<Order>>(
() => ({
// the page size is chosen outside of the grid, [] hides the selector in the pagination bar
pagination: { enabled: paged, pageSize, pageSizes: [] },
rowId: (data) => data.id,
defaultColumn: { sortable: true, width: 130 },
}),
[paged, pageSize]
);
return (
<div className="demo">
<div className="demo-toolbar">
<label>
<input type="checkbox" checked={paged} onChange={(e) => setPaged(e.target.checked)} /> Pages
</label>
<label>
Rows per page{" "}
<select value={pageSize} disabled={!paged} onChange={(e) => setPageSize(Number(e.target.value))}>
<option value={10}>10</option>
<option value={15}>15</option>
<option value={30}>30</option>
</select>
</label>
</div>
<Datagrid columns={columns} rows={rows} options={options} />
</div>
);
}Pagination with grouping
With row grouping the grid paginates the top level groups: a page contains pageSize groups with all their rows, so a group is never split across two pages. The pagination bar counts groups instead of rows.
Source
vue
<template>
<div class="demo">
<div class="demo-toolbar">
<button @click="api?.expandAll()">Expand all</button>
<button @click="api?.collapseAll()">Collapse all</button>
</div>
<Datagrid
:columns="columns"
:rows="rows"
:options="options"
:columnGroups="groups"
@ready="api = $event"
/>
</div>
</template>
<script setup lang="ts">
import { shallowRef } from "vue";
import { Datagrid, formatters, type ColumnConfig, type Core, type GridOptions } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";
const rows = createEmployees(300);
const api = shallowRef<Core>();
const groups = ["city"];
const columns: Array<ColumnConfig<Employee>> = [
{ field: "city", text: "City", groupable: true },
{ field: "lastName", text: "Name" },
{ field: "department", text: "Department" },
{
field: "salary",
text: "Salary (avg)",
type: "number",
aggFunc: "avg",
valueFormatter: formatters.currency("EUR", { maximumFractionDigits: 0 }),
},
];
const options: GridOptions<Employee> = {
// with row grouping a page contains 4 groups (with all their rows)
pagination: { enabled: true, pageSize: 4, pageSizes: [] },
groupShowCount: true,
defaultColumn: { sortable: true, flex: 1 },
};
</script>tsx
import { useRef } from "react";
import {
Datagrid,
formatters,
type ColumnConfig,
type DatagridHandle,
type GridOptions,
} from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";
const rows = createEmployees(300);
const groups = ["city"];
const columns: Array<ColumnConfig<Employee>> = [
{ field: "city", text: "City", groupable: true },
{ field: "lastName", text: "Name" },
{ field: "department", text: "Department" },
{
field: "salary",
text: "Salary (avg)",
type: "number",
aggFunc: "avg",
valueFormatter: formatters.currency("EUR", { maximumFractionDigits: 0 }),
},
];
const options: GridOptions<Employee> = {
// with row grouping a page contains 4 groups (with all their rows)
pagination: { enabled: true, pageSize: 4, pageSizes: [] },
groupShowCount: true,
defaultColumn: { sortable: true, flex: 1 },
};
export default function PaginationGrouping() {
const grid = useRef<DatagridHandle>(null);
return (
<div className="demo">
<div className="demo-toolbar">
<button onClick={() => grid.current?.expandAll()}>Expand all</button>
<button onClick={() => grid.current?.collapseAll()}>Collapse all</button>
</div>
<Datagrid ref={grid} columns={columns} rows={rows} options={options} columnGroups={groups} />
</div>
);
}Binding and API
Bind the page index to control the page from outside of the grid, e.g. with your own buttons or to show a specific row. The page of a row depends on the current sort and filter, so the demo looks up the position of the employee in api.getFilteredSortedItems() and divides it by the page size:
Source
vue
<template>
<div class="demo">
<div class="demo-toolbar">
<button :disabled="page === 0" @click="page--">Previous</button>
<span>Page {{ page + 1 }} of {{ pageCount }}</span>
<button :disabled="page >= pageCount - 1" @click="page++">Next</button>
<input type="text" v-model="search" placeholder="Employee ID, e.g. 345" @keydown.enter="show" />
<button @click="show">Show employee</button>
<span>{{ message }}</span>
</div>
<Datagrid
:columns="columns"
:rows="rows"
:options="options"
v-model:pageIndex="page"
v-model:selectedItems="selected"
@ready="api = $event"
@page-change="pageCount = $event.pageCount"
/>
</div>
</template>
<script setup lang="ts">
import { ref, shallowRef } from "vue";
import { Datagrid, type ColumnConfig, type Core, type GridOptions, type ItemUid, type Row } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";
const rows = createEmployees(500);
const api = shallowRef<Core>();
const page = ref(0);
const pageCount = ref(1);
const selected = ref<Array<ItemUid>>([]);
const search = ref("");
const message = ref("");
function show() {
const id = Number(search.value);
// position of the row after sorting and filtering (the page depends on the current sort)
const index = api.value!.getFilteredSortedItems().findIndex((row) => (row as Row<Employee>).data.id === id);
if (index === -1) {
message.value = `Employee ${search.value} not found`;
return;
}
page.value = Math.floor(index / api.value!.getPaginationState().pageSize);
selected.value = [id];
message.value = `Employee ${id} is on page ${page.value + 1}`;
}
const columns: Array<ColumnConfig<Employee>> = [
{ field: "id", text: "ID", type: "number", width: 80 },
{ field: "firstName", text: "First name" },
{ field: "lastName", text: "Last name", sort: "asc" },
{ field: "department", text: "Department" },
{ field: "city", text: "City" },
];
const options: GridOptions<Employee> = {
rowId: (data) => data.id,
selection: "Row",
pagination: { enabled: true, pageSize: 10, pageSizes: [10, 20, 50] },
defaultColumn: { sortable: true, flex: 1 },
};
</script>tsx
import { useRef, useState } from "react";
import {
Datagrid,
type ColumnConfig,
type DatagridHandle,
type GridOptions,
type ItemUid,
type Row,
} from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";
const rows = createEmployees(500);
const columns: Array<ColumnConfig<Employee>> = [
{ field: "id", text: "ID", type: "number", width: 80 },
{ field: "firstName", text: "First name" },
{ field: "lastName", text: "Last name", sort: "asc" },
{ field: "department", text: "Department" },
{ field: "city", text: "City" },
];
const options: GridOptions<Employee> = {
rowId: (data) => data.id,
selection: "Row",
pagination: { enabled: true, pageSize: 10, pageSizes: [10, 20, 50] },
defaultColumn: { sortable: true, flex: 1 },
};
export default function PaginationJump() {
const grid = useRef<DatagridHandle>(null);
const [page, setPage] = useState(0);
const [pageCount, setPageCount] = useState(1);
const [selected, setSelected] = useState<Array<ItemUid>>([]);
const [search, setSearch] = useState("");
const [message, setMessage] = useState("");
function show() {
const api = grid.current!.api;
const id = Number(search);
// position of the row after sorting and filtering (the page depends on the current sort)
const index = api.getFilteredSortedItems().findIndex((row) => (row as Row<Employee>).data.id === id);
if (index === -1) {
setMessage(`Employee ${search} not found`);
return;
}
const newPage = Math.floor(index / api.getPaginationState().pageSize);
setPage(newPage);
setSelected([id]);
setMessage(`Employee ${id} is on page ${newPage + 1}`);
}
return (
<div className="demo">
<div className="demo-toolbar">
<button disabled={page === 0} onClick={() => setPage(page - 1)}>
Previous
</button>
<span>
Page {page + 1} of {pageCount}
</span>
<button disabled={page >= pageCount - 1} onClick={() => setPage(page + 1)}>
Next
</button>
<input
type="text"
value={search}
placeholder="Employee ID, e.g. 345"
onChange={(e) => setSearch(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && show()}
/>
<button onClick={show}>Show employee</button>
<span>{message}</span>
</div>
<Datagrid
ref={grid}
columns={columns}
rows={rows}
options={options}
pageIndex={page}
onPageIndexChange={setPage}
onPageChange={(state) => setPageCount(state.pageCount)}
selectedItems={selected}
onSelectedItemsChange={setSelected}
/>
</div>
);
}vue
<Datagrid v-model:pageIndex="page" ... />tsx
<Datagrid pageIndex={page} onPageIndexChange={setPage} ... />ts
api.setPageIndex(3); // zero based
api.nextPage();
api.prevPage();
api.setPageSize(50);
api.getPaginationState();
// { active, pageIndex, pageCount, pageSize, pageSizes, hasPreviousPage, hasNextPage, rowCount }@page-change is emitted when the page or the page size changes.