@tanstack/angular-table
Version:
Headless UI for building powerful tables & datagrids for Angular.
143 lines (108 loc) • 3.89 kB
Markdown
name: with-tanstack-query
description: >
Compose Angular Query with signal-owned Table filtering, sorting, and pagination state using reactive query options, manual row-model boundaries, direct query data, server counts, and valid injection context.
metadata:
type: composition
library: '@tanstack/angular-table'
framework: angular
library_version: '9.1.2'
requires:
- '@tanstack/table-core#client-vs-server'
- getting-started
- table-state
sources:
- 'TanStack/table:examples/angular/with-tanstack-query'
- 'TanStack/table:docs/framework/angular/guide/table-state.md'
- 'TanStack/table:docs/framework/angular/guide/pagination.md'
This skill builds on `@tanstack/table-core#client-vs-server`, `getting-started`, and `table-state`. Decide the server-owned stages and dataset before wiring Query.
## Setup
```ts
import {
injectQuery,
keepPreviousData,
} from '@tanstack/angular-query-experimental'
import { signal } from '@angular/core'
import {
injectTable,
rowPaginationFeature,
tableFeatures,
} from '@tanstack/angular-table'
const features = tableFeatures({ rowPaginationFeature })
const EMPTY_ROWS: never[] = []
export class PeopleTable {
readonly pagination = signal({ pageIndex: 0, pageSize: 20 })
readonly query = injectQuery(() => ({
queryKey: [
'people',
this.pagination().pageIndex,
this.pagination().pageSize,
],
queryFn: () =>
fetch(
`/api/people?page=${this.pagination().pageIndex}&size=${this.pagination().pageSize}`,
).then((r) => r.json()),
placeholderData: keepPreviousData,
}))
readonly table = injectTable(() => ({
features,
columns,
data: this.query.data()?.rows ?? EMPTY_ROWS,
rowCount: this.query.data()?.rowCount ?? 0,
manualPagination: true,
state: { pagination: this.pagination() },
onPaginationChange: (next) =>
typeof next === 'function'
? this.pagination.update(next)
: this.pagination.set(next),
}))
}
```
## Core Patterns
### Track every server-owned input
Read pagination, sorting, and filtering signals inside `injectQuery(() => ...)` and include them in the query key. Return data already processed for each manual stage.
### Keep Query data authoritative
Read the Query signal directly in `injectTable`. Create another signal only for a deliberate editable draft with an explicit cache synchronization policy.
## Common Mistakes
### HIGH Capturing query inputs outside tracking
Wrong:
```ts
const page = this.pagination().pageIndex
readonly query = injectQuery(() => ({ queryKey: ['people', page], queryFn }))
```
Correct:
```ts
readonly query = injectQuery(() => ({ queryKey: ['people', this.pagination().pageIndex], queryFn }))
```
Signal reads inside the options function establish refetch dependencies.
Source: `examples/angular/with-tanstack-query/src/app/app.ts`
### HIGH Expecting manual mode to request
Wrong:
```ts
injectTable(() => ({ features, columns, data, manualPagination: true }))
```
Correct:
```ts
injectTable(() => ({
features,
columns,
data: this.query.data()?.rows ?? EMPTY_ROWS,
manualPagination: true,
}))
```
Manual flags only bypass client row processing; Query performs network work.
Source: `examples/angular/with-tanstack-query/src/app/app.ts`
### HIGH Omitting total counts
Wrong:
```ts
{ data: this.query.data()?.rows ?? EMPTY_ROWS, manualPagination: true }
```
Correct:
```ts
{ data: this.query.data()?.rows ?? EMPTY_ROWS, rowCount: this.query.data()?.rowCount ?? 0, manualPagination: true }
```
Table needs `rowCount` or `pageCount` to constrain navigation across server pages.
Source: `docs/framework/angular/guide/pagination.md`
## API Discovery
Inspect installed `@tanstack/angular-table/dist/types/`, the relevant core feature source, and installed Angular Query source for the exact `injectQuery` package/version contract.