useDataTable
Composable for schema-driven data tables with pagination, sorting, filtering, and selection.
Basic Usage
vue
<script setup lang="ts">
import { useDataTable } from '@anhanga/vue'
import { Scope } from '@anhanga/core'
const table = useDataTable({
schema: PersonSchema.provide(),
scope: Scope.index,
handlers: personHandlers,
hooks: personHooks,
component: componentContract,
translate: t,
})
</script>
<template>
<div v-if="table.loading">Loading...</div>
<div v-else-if="table.empty">No records found</div>
<div v-else>
<table>
<thead>
<tr>
<th
v-for="col in table.columns"
:key="col.name"
@click="table.setSort(col.name)"
>
{{ t(`person.fields.${col.name}`) }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in table.rows" :key="table.getIdentity(row)">
<td v-for="col in table.columns" :key="col.name">
{{ table.formatValue(col.name, row[col.name], row) }}
</td>
</tr>
</tbody>
</table>
<div>
<button :disabled="table.page <= 1" @click="table.setPage(table.page - 1)">Previous</button>
<span>Page {{ table.page }} of {{ table.totalPages }}</span>
<button :disabled="table.page >= table.totalPages" @click="table.setPage(table.page + 1)">Next</button>
</div>
</div>
</template>Options
typescript
interface UseDataTableOptions {
schema: SchemaProvide
scope: ScopeValue
handlers?: Record<string, HandlerFn>
hooks?: SchemaHooks
context?: Record<string, unknown>
component: ComponentContract
pageSize?: number
translate?: TranslateContract
}| Option | Required | Description |
|---|---|---|
schema | Yes | Output of SchemaDefinition.provide() |
scope | Yes | Current scope (typically Scope.index) |
handlers | No | Action handlers |
hooks | No | Lifecycle hooks (fetch hooks provide data) |
component | Yes | ComponentContract implementation |
pageSize | No | Items per page (default varies) |
translate | No | Translation function |
Return Value
Data
| Property | Type | Description |
|---|---|---|
rows | Record<string, unknown>[] | Current page data |
loading | boolean | True while fetching data |
empty | boolean | True if no rows |
Columns
| Property | Type | Description |
|---|---|---|
columns | ResolvedColumn[] | Visible columns |
availableColumns | ResolvedColumn[] | All available columns |
visibleColumns | string[] | Names of visible columns |
toggleColumn(name) | function | Toggle column visibility |
Pagination
| Property | Type | Description |
|---|---|---|
page | number | Current page |
limit | number | Items per page |
total | number | Total record count |
totalPages | number | Total page count |
setPage(page) | function | Navigate to page |
setLimit(limit) | function | Change page size |
Sorting
| Property | Type | Description |
|---|---|---|
sortField | string | undefined | Current sort field |
sortOrder | 'asc' | 'desc' | undefined | Sort direction |
setSort(field) | function | Toggle sort on field |
Filtering
| Property | Type | Description |
|---|---|---|
filters | Record<string, unknown> | Active filters |
setFilter(field, value) | function | Set a filter value |
clearFilters() | function | Clear all filters |
Selection
| Property | Type | Description |
|---|---|---|
selected | Record<string, unknown>[] | Selected rows |
isSelected(record) | function | Check if row is selected |
toggleSelect(record) | function | Toggle row selection |
selectAll() | function | Select all rows |
clearSelection() | function | Clear selection |
Actions
| Property | Type | Description |
|---|---|---|
actions | ResolvedAction[] | Top-level actions (e.g., "Add") |
getRowActions(record) | function | Get actions for a specific row |
Utilities
| Method | Description |
|---|---|
reload() | Re-fetch current page data |
formatValue(name, value, record) | Format a cell value using column config |
getIdentity(record) | Get the identity key for a record |
Column Configuration
Fields become table columns when marked with .column():
typescript
text().column() // basic column
text().column().sortable() // sortable
text().column().filterable() // filterableFetch Hooks
Table data is loaded through fetch hooks:
typescript
const personHooks = PersonSchema.hooks({
fetch: {
[Scope.index]: async ({ params }) => {
return personService.paginate(params)
},
},
})The params object contains pagination, sorting, and filter state:
typescript
interface PaginateParams {
page: number
limit: number
sort?: string
order?: 'asc' | 'desc'
filters?: Record<string, unknown>
}The hook expects a PaginatedResult:
typescript
interface PaginatedResult<T> {
data: T[]
total: number
page: number
limit: number
}ResolvedColumn
typescript
interface ResolvedColumn {
name: string
config: FieldConfig
table: TableConfig
}