@tanstack/angular-table 9.0.0-beta.2 → 9.0.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fesm2022/tanstack-angular-table.mjs +22 -66
- package/dist/fesm2022/tanstack-angular-table.mjs.map +1 -1
- package/dist/types/tanstack-angular-table.d.ts +8 -14
- package/package.json +8 -7
- package/skills/angular/angular-rendering-directives/SKILL.md +4 -4
- package/skills/angular/angular-rendering-directives/references/create-table-hook-registries.md +4 -4
- package/skills/angular/client-to-server/SKILL.md +22 -21
- package/skills/angular/compose-with-tanstack-query/SKILL.md +17 -13
- package/skills/angular/compose-with-tanstack-store/SKILL.md +16 -8
- package/skills/angular/compose-with-tanstack-virtual/SKILL.md +1 -2
- package/skills/angular/getting-started/SKILL.md +34 -43
- package/skills/angular/getting-started/references/feature-row-model-mapping.md +9 -6
- package/skills/angular/migrate-v8-to-v9/SKILL.md +43 -40
- package/skills/angular/migrate-v8-to-v9/references/v8-to-v9-mapping.md +21 -14
- package/skills/angular/production-readiness/SKILL.md +40 -37
- package/skills/angular/table-state/SKILL.md +19 -26
- package/skills/angular/table-state/references/external-atoms-and-app-hook.md +8 -7
- package/src/helpers/cell.ts +6 -2
- package/src/helpers/createTableHook.ts +11 -12
- package/src/helpers/header.ts +4 -2
- package/src/helpers/table.ts +4 -2
- package/src/injectTable.ts +8 -71
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Type, ComponentMirror, OutputEmitterRef, InputSignal, Injector, createComponent, Binding, TemplateRef, InjectionToken, Signal } from '@angular/core';
|
|
3
|
-
import { TableFeatures, RowData, CellData, Cell, Header, CellContext, HeaderContext, Table,
|
|
3
|
+
import { TableFeatures, RowData, CellData, Cell, Header, CellContext, HeaderContext, Table, TableOptions, Column, Row, IdentifiedColumnDef, AccessorFn, DeepKeys, DeepValue, AccessorFnColumnDef, AccessorKeyColumnDef, ColumnDef, DisplayColumnDef, GroupColumnDef } from '@tanstack/table-core';
|
|
4
4
|
export * from '@tanstack/table-core';
|
|
5
5
|
import { Atom, ReadonlyAtom, Store, ReadonlyStore } from '@tanstack/angular-store';
|
|
6
6
|
export { shallow } from '@tanstack/angular-store';
|
|
@@ -365,16 +365,11 @@ type SubscribeSource<TValue> = Atom<TValue> | ReadonlyAtom<TValue> | Store<TValu
|
|
|
365
365
|
type AngularTable<TFeatures extends TableFeatures, TData extends RowData> = Table<TFeatures, TData> & {
|
|
366
366
|
/**
|
|
367
367
|
* @deprecated Prefer `table.atoms.<slice>.get()` for template/render reads
|
|
368
|
-
* of a specific state slice,
|
|
369
|
-
*
|
|
370
|
-
*
|
|
368
|
+
* of a specific state slice, or Angular computed values around explicit
|
|
369
|
+
* selectors. `table.store.state` is a current-value snapshot and is easy to
|
|
370
|
+
* misuse in render code.
|
|
371
371
|
*/
|
|
372
372
|
readonly store: Table<TFeatures, TData>['store'];
|
|
373
|
-
/**
|
|
374
|
-
* The current table state exposed as a flat proxy. Prefer
|
|
375
|
-
* `table.atoms.<slice>.get()` when reading a specific slice.
|
|
376
|
-
*/
|
|
377
|
-
readonly state: Readonly<TableState<TFeatures>>;
|
|
378
373
|
};
|
|
379
374
|
/**
|
|
380
375
|
* Creates and returns an Angular-reactive table instance.
|
|
@@ -449,7 +444,7 @@ interface TanStackTableCellContext<TFeatures extends TableFeatures, TData extend
|
|
|
449
444
|
*
|
|
450
445
|
* This token is provided by the {@link TanStackTableCell} directive.
|
|
451
446
|
*/
|
|
452
|
-
declare const TanStackTableCellToken: InjectionToken<Signal<
|
|
447
|
+
declare const TanStackTableCellToken: InjectionToken<Signal<Cell<TableFeatures, RowData, unknown>>>;
|
|
453
448
|
/**
|
|
454
449
|
* Provides a TanStack Table `Cell` instance in Angular DI.
|
|
455
450
|
*
|
|
@@ -521,7 +516,7 @@ interface TanStackTableHeaderContext<TFeatures extends TableFeatures, TData exte
|
|
|
521
516
|
*
|
|
522
517
|
* This token is provided by the {@link TanStackTableHeader} directive.
|
|
523
518
|
*/
|
|
524
|
-
declare const TanStackTableHeaderToken: InjectionToken<Signal<
|
|
519
|
+
declare const TanStackTableHeaderToken: InjectionToken<Signal<Header<TableFeatures, RowData, unknown>>>;
|
|
525
520
|
/**
|
|
526
521
|
* Provides a TanStack Table `Header` instance in Angular DI.
|
|
527
522
|
*
|
|
@@ -578,7 +573,7 @@ declare function injectTableHeaderContext<TFeatures extends TableFeatures, TData
|
|
|
578
573
|
*
|
|
579
574
|
* This token is provided by the {@link TanStackTable} directive.
|
|
580
575
|
*/
|
|
581
|
-
declare const TanStackTableToken: InjectionToken<Signal<
|
|
576
|
+
declare const TanStackTableToken: InjectionToken<Signal<AngularTable<TableFeatures, RowData>>>;
|
|
582
577
|
/**
|
|
583
578
|
* Provides a TanStack Table instance (`AngularTable`) in Angular DI.
|
|
584
579
|
*
|
|
@@ -756,7 +751,7 @@ type CreateTableHookResult<TFeatures extends TableFeatures, TTableComponents ext
|
|
|
756
751
|
injectTableCellContext: <TValue extends CellData = CellData, TRowData extends RowData = RowData>() => Signal<Cell<TFeatures, TRowData, TValue>>;
|
|
757
752
|
injectFlexRenderHeaderContext: <TData extends RowData, TValue extends CellData>() => HeaderContext<TFeatures, TData, TValue>;
|
|
758
753
|
injectFlexRenderCellContext: <TData extends RowData, TValue extends CellData>() => CellContext<TFeatures, TData, TValue>;
|
|
759
|
-
injectAppTable: <TData extends RowData>(tableOptions: () => Omit<TableOptions<TFeatures, TData>, 'features'
|
|
754
|
+
injectAppTable: <TData extends RowData>(tableOptions: () => Omit<TableOptions<TFeatures, TData>, 'features'>) => AppAngularTable<TFeatures, TData, TTableComponents, TCellComponents, THeaderComponents>;
|
|
760
755
|
};
|
|
761
756
|
/**
|
|
762
757
|
* Creates app-scoped Angular table helpers with features, row models, and
|
|
@@ -770,7 +765,6 @@ type CreateTableHookResult<TFeatures extends TableFeatures, TTableComponents ext
|
|
|
770
765
|
* ```ts
|
|
771
766
|
* const { injectAppTable, createAppColumnHelper } = createTableHook({
|
|
772
767
|
* features,
|
|
773
|
-
* rowModels: {},
|
|
774
768
|
* tableComponents: {},
|
|
775
769
|
* cellComponents: {},
|
|
776
770
|
* headerComponents: {},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/angular-table",
|
|
3
|
-
"version": "9.0.0-beta.
|
|
3
|
+
"version": "9.0.0-beta.20",
|
|
4
4
|
"description": "Headless UI for building powerful tables & datagrids for Angular.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,14 +52,15 @@
|
|
|
52
52
|
],
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@tanstack/angular-store": "^0.11.0",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
55
|
+
"tslib": "^2.8.1",
|
|
56
|
+
"@tanstack/table-core": "9.0.0-beta.20"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@analogjs/vite-plugin-angular": "^2.6.
|
|
60
|
-
"@analogjs/vitest-angular": "^2.6.
|
|
61
|
-
"@angular/
|
|
62
|
-
"@angular/
|
|
59
|
+
"@analogjs/vite-plugin-angular": "^2.6.1",
|
|
60
|
+
"@analogjs/vitest-angular": "^2.6.1",
|
|
61
|
+
"@angular/compiler": "^22.0.2",
|
|
62
|
+
"@angular/core": "^22.0.2",
|
|
63
|
+
"@angular/platform-browser": "^22.0.2",
|
|
63
64
|
"ng-packagr": "^22.0.0",
|
|
64
65
|
"typescript": "6.0.3"
|
|
65
66
|
},
|
|
@@ -297,10 +297,10 @@ column defs short and typed.
|
|
|
297
297
|
|
|
298
298
|
```ts
|
|
299
299
|
export const { injectAppTable, createAppColumnHelper } = createTableHook({
|
|
300
|
-
features: tableFeatures({
|
|
301
|
-
|
|
302
|
-
/*
|
|
303
|
-
},
|
|
300
|
+
features: tableFeatures({
|
|
301
|
+
rowSortingFeature,
|
|
302
|
+
/* row-model factories and fn registries also go here */
|
|
303
|
+
}),
|
|
304
304
|
tableComponents: { PaginationControls, RowCount },
|
|
305
305
|
cellComponents: { TextCell, NumberCell, StatusCell },
|
|
306
306
|
headerComponents: { SortIndicator, ColumnFilter },
|
package/skills/angular/angular-rendering-directives/references/create-table-hook-registries.md
CHANGED
|
@@ -26,10 +26,10 @@ export const {
|
|
|
26
26
|
injectTableCellContext,
|
|
27
27
|
injectTableHeaderContext,
|
|
28
28
|
} = createTableHook({
|
|
29
|
-
features: tableFeatures({
|
|
30
|
-
|
|
31
|
-
/*
|
|
32
|
-
},
|
|
29
|
+
features: tableFeatures({
|
|
30
|
+
rowSortingFeature,
|
|
31
|
+
/* row-model factories and fn registries also go here */
|
|
32
|
+
}),
|
|
33
33
|
tableComponents: { PaginationControls, RowCount },
|
|
34
34
|
cellComponents: { TextCell, NumberCell, StatusCell },
|
|
35
35
|
headerComponents: { SortIndicator, ColumnFilter },
|
|
@@ -3,12 +3,13 @@ name: angular/client-to-server
|
|
|
3
3
|
description: >
|
|
4
4
|
Convert an Angular Table v9 from client-side to server-side processing. Flip
|
|
5
5
|
`manualPagination` / `manualSorting` / `manualFiltering` / `manualGrouping` / `manualExpanding`
|
|
6
|
-
for the slices the server now owns; drop the corresponding
|
|
7
|
-
server replaces; supply `rowCount` (server total) so
|
|
8
|
-
`pagination` / `sorting` / `columnFilters` / `globalFilter`
|
|
9
|
-
`on[State]Change`; fetch via `rxResource` / `httpResource` /
|
|
10
|
-
previous data on refetch with `linkedSignal` (or
|
|
11
|
-
set `getRowId` for stable selection across
|
|
6
|
+
for the slices the server now owns; drop the corresponding row-model factory slots from the
|
|
7
|
+
`features` object for the factories the server replaces; supply `rowCount` (server total) so
|
|
8
|
+
pagination computes correctly; hoist `pagination` / `sorting` / `columnFilters` / `globalFilter`
|
|
9
|
+
to Angular signals with `state` + `on[State]Change`; fetch via `rxResource` / `httpResource` /
|
|
10
|
+
`@tanstack/angular-query`; preserve previous data on refetch with `linkedSignal` (or
|
|
11
|
+
`placeholderData: keepPreviousData` for Query); set `getRowId` for stable selection across
|
|
12
|
+
refetches.
|
|
12
13
|
type: lifecycle
|
|
13
14
|
library: tanstack-table
|
|
14
15
|
framework: angular
|
|
@@ -44,7 +45,7 @@ For each slice the server now owns:
|
|
|
44
45
|
|
|
45
46
|
1. **Flip `manualX: true`** in table options. This tells the table "don't
|
|
46
47
|
process this on the client — trust the data you receive."
|
|
47
|
-
2. **Drop the matching client-side row-model factory** from `
|
|
48
|
+
2. **Drop the matching client-side row-model factory slot** from `features`
|
|
48
49
|
(or keep it if you still want the feature's _state_ but no client
|
|
49
50
|
recomputation — see §3).
|
|
50
51
|
3. **Hoist the slice to an Angular signal**, control it via `state.x` +
|
|
@@ -97,6 +98,7 @@ import {
|
|
|
97
98
|
tableFeatures,
|
|
98
99
|
rowPaginationFeature,
|
|
99
100
|
rowSortingFeature,
|
|
101
|
+
columnFilteringFeature,
|
|
100
102
|
globalFilteringFeature,
|
|
101
103
|
createColumnHelper,
|
|
102
104
|
type ColumnDef,
|
|
@@ -107,6 +109,7 @@ import {
|
|
|
107
109
|
const features = tableFeatures({
|
|
108
110
|
rowPaginationFeature,
|
|
109
111
|
rowSortingFeature,
|
|
112
|
+
columnFilteringFeature,
|
|
110
113
|
globalFilteringFeature,
|
|
111
114
|
})
|
|
112
115
|
|
|
@@ -198,12 +201,11 @@ export class App {
|
|
|
198
201
|
}),
|
|
199
202
|
]
|
|
200
203
|
|
|
201
|
-
// 4. Wire the table — manualX flags,
|
|
204
|
+
// 4. Wire the table — manualX flags, features without row-model factories, supply rowCount
|
|
202
205
|
readonly table = injectTable(() => {
|
|
203
206
|
const data = this.dataWithLatest()
|
|
204
207
|
return {
|
|
205
|
-
features,
|
|
206
|
-
rowModels: {}, // ← dropped paginatedRowModel, sortedRowModel, filteredRowModel
|
|
208
|
+
features, // ← features has no paginatedRowModel/sortedRowModel/filteredRowModel slots
|
|
207
209
|
columns: this.columns,
|
|
208
210
|
data: data.items,
|
|
209
211
|
getRowId: (row) => String(row.id),
|
|
@@ -245,8 +247,8 @@ export class App {
|
|
|
245
247
|
|
|
246
248
|
### What changed from the client-side version
|
|
247
249
|
|
|
248
|
-
- `
|
|
249
|
-
|
|
250
|
+
- `features` has no `paginatedRowModel`, `sortedRowModel`, or `filteredRowModel`
|
|
251
|
+
slots — the server is the source of truth for those.
|
|
250
252
|
- `manualPagination` / `manualSorting` / `manualFiltering: true`.
|
|
251
253
|
- `rowCount: data.totalCount` — required for correct `getPageCount()` and the
|
|
252
254
|
next/prev buttons.
|
|
@@ -359,13 +361,12 @@ edits (toggle the flag around the update).
|
|
|
359
361
|
|
|
360
362
|
## Failure modes
|
|
361
363
|
|
|
362
|
-
### 1. (CRITICAL) Flipping `manualPagination: true` but keeping
|
|
363
|
-
|
|
364
|
-
`paginatedRowModel` in `rowModels`
|
|
364
|
+
### 1. (CRITICAL) Flipping `manualPagination: true` but keeping `paginatedRowModel` on `features`
|
|
365
365
|
|
|
366
366
|
The client row-model factory will re-paginate the (already-paginated) data,
|
|
367
367
|
chopping the visible rows down to the first `pageSize` of the page slice.
|
|
368
|
-
**
|
|
368
|
+
**Remove the `paginatedRowModel` slot from `features`** when going manual — or
|
|
369
|
+
accept double-pagination.
|
|
369
370
|
|
|
370
371
|
### 2. (CRITICAL) Forgetting `rowCount` under `manualPagination`
|
|
371
372
|
|
|
@@ -409,12 +410,12 @@ the fetch itself).
|
|
|
409
410
|
|
|
410
411
|
```ts
|
|
411
412
|
manualPagination: true,
|
|
412
|
-
// columnFilteringFeature still registered, filteredRowModel still
|
|
413
|
+
// columnFilteringFeature still registered, filteredRowModel slot still on features
|
|
413
414
|
```
|
|
414
415
|
|
|
415
416
|
The filtered row model now filters only the _current page_ — useless. If the
|
|
416
417
|
server paginates, the server must also filter; flip `manualFiltering: true`
|
|
417
|
-
and
|
|
418
|
+
and remove the `filteredRowModel` slot from `features`.
|
|
418
419
|
|
|
419
420
|
### 8. (HIGH) Forgetting to depend on the controlled signals in your fetch
|
|
420
421
|
|
|
@@ -448,11 +449,11 @@ the new filtered result set only has 2 pages. They have to manually click back
|
|
|
448
449
|
to page 1. Always reset `pageIndex` to 0 in `onGlobalFilterChange` /
|
|
449
450
|
`onColumnFiltersChange`.
|
|
450
451
|
|
|
451
|
-
### 11. (MEDIUM)
|
|
452
|
+
### 11. (MEDIUM) Thinking that omitting all row-model slots means "no row models work"
|
|
452
453
|
|
|
453
454
|
Core row model is always automatic. `table.getRowModel().rows` returns the
|
|
454
|
-
data array as `Row<...>` objects no matter what —
|
|
455
|
-
no client-side processing on top.
|
|
455
|
+
data array as `Row<...>` objects no matter what — a `features` object with no
|
|
456
|
+
factory slots just means no client-side processing on top.
|
|
456
457
|
|
|
457
458
|
---
|
|
458
459
|
|
|
@@ -5,10 +5,10 @@ description: >
|
|
|
5
5
|
Key the query on the controlled table state that drives the request (pagination, sorting,
|
|
6
6
|
filters); use `placeholderData: keepPreviousData` to avoid a "0 rows flash" between pages;
|
|
7
7
|
set `manualPagination` / `manualSorting` / `manualFiltering` for the slices the server owns;
|
|
8
|
-
drop the matching client `
|
|
9
|
-
set `getRowId` for stable selection across refetches; hoist controlled slices
|
|
10
|
-
signals + `state` + `on[State]Change`. Alternative
|
|
11
|
-
don't want to add the Query dependency (see `client-to-server`).
|
|
8
|
+
drop the matching client row-model factory slots from `features`; pass `rowCount` from the
|
|
9
|
+
server response; set `getRowId` for stable selection across refetches; hoist controlled slices
|
|
10
|
+
to Angular signals + `state` + `on[State]Change`. Alternative: `rxResource` / `httpResource`
|
|
11
|
+
if you don't want to add the Query dependency (see `client-to-server`).
|
|
12
12
|
type: composition
|
|
13
13
|
library: tanstack-table
|
|
14
14
|
framework: angular
|
|
@@ -82,6 +82,7 @@ import {
|
|
|
82
82
|
tableFeatures,
|
|
83
83
|
rowPaginationFeature,
|
|
84
84
|
rowSortingFeature,
|
|
85
|
+
columnFilteringFeature,
|
|
85
86
|
globalFilteringFeature,
|
|
86
87
|
createColumnHelper,
|
|
87
88
|
type ColumnDef,
|
|
@@ -92,6 +93,7 @@ import {
|
|
|
92
93
|
const features = tableFeatures({
|
|
93
94
|
rowPaginationFeature,
|
|
94
95
|
rowSortingFeature,
|
|
96
|
+
columnFilteringFeature,
|
|
95
97
|
globalFilteringFeature,
|
|
96
98
|
})
|
|
97
99
|
|
|
@@ -165,12 +167,11 @@ export class App {
|
|
|
165
167
|
}),
|
|
166
168
|
]
|
|
167
169
|
|
|
168
|
-
// 4. Wire the table — manual flags,
|
|
170
|
+
// 4. Wire the table — manual flags, features without row-model factories, rowCount, getRowId
|
|
169
171
|
readonly table = injectTable(() => {
|
|
170
172
|
const data = this.todosQuery.data() ?? { items: [], totalCount: 0 }
|
|
171
173
|
return {
|
|
172
|
-
features,
|
|
173
|
-
rowModels: {}, // ← dropped paginatedRowModel / sortedRowModel / filteredRowModel
|
|
174
|
+
features, // ← features has no paginatedRowModel/sortedRowModel/filteredRowModel slots
|
|
174
175
|
columns: this.columns,
|
|
175
176
|
data: data.items,
|
|
176
177
|
getRowId: (row) => String(row.id),
|
|
@@ -219,8 +220,8 @@ For server-driven Table + Query to work correctly:
|
|
|
219
220
|
during refetches. Without it, `todosQuery.data()` becomes `undefined`
|
|
220
221
|
mid-fetch, your table shows 0 rows for a frame, the user notices.
|
|
221
222
|
3. **`manualPagination` / `manualSorting` / `manualFiltering: true`** for
|
|
222
|
-
slices the server owns + **
|
|
223
|
-
the table doesn't re-process the data the server already filtered/sorted/paged.
|
|
223
|
+
slices the server owns + **remove the matching factory slots from `features`**
|
|
224
|
+
so the table doesn't re-process the data the server already filtered/sorted/paged.
|
|
224
225
|
4. **`rowCount: data.totalCount`** (or `pageCount`) so `getPageCount()`
|
|
225
226
|
computes correctly under `manualPagination`.
|
|
226
227
|
|
|
@@ -407,15 +408,18 @@ visible until the new one resolves.
|
|
|
407
408
|
`getPageCount()` returns `-1`, "next" never disables. The server tells you
|
|
408
409
|
how many rows exist — pass it.
|
|
409
410
|
|
|
410
|
-
### 4. (CRITICAL) Keeping client row
|
|
411
|
+
### 4. (CRITICAL) Keeping client row-model factory slots for slices the server owns
|
|
411
412
|
|
|
412
413
|
```ts
|
|
413
414
|
// ❌ Double-processes the data
|
|
414
415
|
manualPagination: true,
|
|
415
|
-
|
|
416
|
+
// features still has paginatedRowModel slot → re-paginates server page
|
|
416
417
|
|
|
417
|
-
// ✅
|
|
418
|
-
|
|
418
|
+
// ✅ remove the factory slot from features for server-owned slices
|
|
419
|
+
const features = tableFeatures({
|
|
420
|
+
rowPaginationFeature,
|
|
421
|
+
// paginatedRowModel intentionally omitted — server handles it
|
|
422
|
+
})
|
|
419
423
|
```
|
|
420
424
|
|
|
421
425
|
Same applies to `sortedRowModel` under `manualSorting` and `filteredRowModel`
|
|
@@ -99,7 +99,6 @@ export const sortingStore = new Store<SortingState>([])
|
|
|
99
99
|
export class App {
|
|
100
100
|
readonly table = injectTable(() => ({
|
|
101
101
|
features,
|
|
102
|
-
rowModels: { /* … */ },
|
|
103
102
|
columns,
|
|
104
103
|
data: this.data(),
|
|
105
104
|
atoms: {
|
|
@@ -188,9 +187,6 @@ export class PageRoute {
|
|
|
188
187
|
|
|
189
188
|
readonly table = injectTable(() => ({
|
|
190
189
|
features,
|
|
191
|
-
rowModels: {
|
|
192
|
-
/* … */
|
|
193
|
-
},
|
|
194
190
|
columns,
|
|
195
191
|
data: this.data(),
|
|
196
192
|
atoms: {
|
|
@@ -236,10 +232,23 @@ import { Store } from '@tanstack/store'
|
|
|
236
232
|
|
|
237
233
|
export const sharedFilter = new Store<string | null>(null)
|
|
238
234
|
|
|
235
|
+
const featuresA = tableFeatures({
|
|
236
|
+
columnFilteringFeature,
|
|
237
|
+
globalFilteringFeature,
|
|
238
|
+
filteredRowModel: createFilteredRowModel(),
|
|
239
|
+
filterFns,
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
const featuresB = tableFeatures({
|
|
243
|
+
columnFilteringFeature,
|
|
244
|
+
globalFilteringFeature,
|
|
245
|
+
filteredRowModel: createFilteredRowModel(),
|
|
246
|
+
filterFns,
|
|
247
|
+
})
|
|
248
|
+
|
|
239
249
|
// Table A
|
|
240
250
|
readonly tableA = injectTable(() => ({
|
|
241
|
-
features:
|
|
242
|
-
rowModels: { filteredRowModel: createFilteredRowModel(filterFns) },
|
|
251
|
+
features: featuresA,
|
|
243
252
|
columns: columnsA,
|
|
244
253
|
data: this.dataA(),
|
|
245
254
|
atoms: { globalFilter: sharedFilter },
|
|
@@ -247,8 +256,7 @@ readonly tableA = injectTable(() => ({
|
|
|
247
256
|
|
|
248
257
|
// Table B (separate component, same module)
|
|
249
258
|
readonly tableB = injectTable(() => ({
|
|
250
|
-
features:
|
|
251
|
-
rowModels: { filteredRowModel: createFilteredRowModel(filterFns) },
|
|
259
|
+
features: featuresB,
|
|
252
260
|
columns: columnsB,
|
|
253
261
|
data: this.dataB(),
|
|
254
262
|
atoms: { globalFilter: sharedFilter },
|
|
@@ -82,7 +82,6 @@ export class VirtualTable {
|
|
|
82
82
|
|
|
83
83
|
readonly table = injectTable(() => ({
|
|
84
84
|
features,
|
|
85
|
-
rowModels: {},
|
|
86
85
|
columns,
|
|
87
86
|
data: this.data(),
|
|
88
87
|
getRowId: (row) => row.id,
|
|
@@ -217,7 +216,7 @@ caches that, scrollbar adjusts.)
|
|
|
217
216
|
Combine with `rowExpandingFeature` for "click to expand details":
|
|
218
217
|
|
|
219
218
|
- Register `rowExpandingFeature` in `features` and
|
|
220
|
-
`expandedRowModel: createExpandedRowModel()`
|
|
219
|
+
`expandedRowModel: createExpandedRowModel()` as a slot on `features`.
|
|
221
220
|
- Use `table.getExpandedRowModel().rows` (or `getRowModel().rows`, which
|
|
222
221
|
already includes expansion under `paginateExpandedRows: true` semantics —
|
|
223
222
|
see `tanstack-table/core/row-expanding`).
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
name: angular/getting-started
|
|
3
3
|
description: >
|
|
4
4
|
End-to-end first-table journey for TanStack Table v9 in Angular: install
|
|
5
|
-
`@tanstack/angular-table`, declare `features` with `tableFeatures()
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
pagination.
|
|
5
|
+
`@tanstack/angular-table`, declare `features` with `tableFeatures()` (row-model factories and
|
|
6
|
+
fn registries live on the features object), build columns with the `TFeatures, TData` generic
|
|
7
|
+
order, call `injectTable(() => ({...}))` from an injection context, and render with `FlexRender` /
|
|
8
|
+
`*flexRenderHeader` / `*flexRenderCell` / `*flexRenderFooter`. Covers the minimum-viable
|
|
9
|
+
signal-backed table plus the upgrade path to sorting + filtering + pagination.
|
|
11
10
|
type: lifecycle
|
|
12
11
|
library: tanstack-table
|
|
13
12
|
framework: angular
|
|
@@ -31,9 +30,9 @@ sources:
|
|
|
31
30
|
> Goal: from zero to a working signal-backed, sorted + paginated, type-safe
|
|
32
31
|
> table in Angular ≥19.
|
|
33
32
|
>
|
|
34
|
-
> v9 is **explicit**: tell the table which features you want with `features
|
|
35
|
-
>
|
|
36
|
-
> makes the v9 bundle tree-shakeable.
|
|
33
|
+
> v9 is **explicit**: tell the table which features you want with `features`.
|
|
34
|
+
> Row-model factories and fn registries live on the features object alongside
|
|
35
|
+
> the feature flags. That explicitness is what makes the v9 bundle tree-shakeable.
|
|
37
36
|
|
|
38
37
|
---
|
|
39
38
|
|
|
@@ -104,8 +103,7 @@ export class App {
|
|
|
104
103
|
|
|
105
104
|
// 3. injectTable in an injection context (a class field qualifies)
|
|
106
105
|
readonly table = injectTable(() => ({
|
|
107
|
-
features, // required in v9
|
|
108
|
-
rowModels: {}, // {} = core only; that's fine
|
|
106
|
+
features, // required in v9; core row model is automatic
|
|
109
107
|
columns, // stable ref
|
|
110
108
|
data: this.data(), // signal read → re-syncs the table on change
|
|
111
109
|
}))
|
|
@@ -156,11 +154,9 @@ driven by the row model.
|
|
|
156
154
|
(`getRowModel()`) is always available. With `features: tableFeatures({})`,
|
|
157
155
|
`table.atoms.*` only contains the slices core ships with — no `pagination`,
|
|
158
156
|
no `sorting`, no `rowSelection` until you add the matching features.
|
|
159
|
-
- `rowModels: {}` does not register any feature-specific row models. Core
|
|
160
|
-
row model is included automatically.
|
|
161
157
|
- `injectTable(() => ({...}))` runs the initializer, builds the table, and
|
|
162
158
|
re-runs the initializer whenever any signal read inside changes. Stable
|
|
163
|
-
references outside the initializer keep `columns` / `features`
|
|
159
|
+
references outside the initializer keep `columns` / `features`
|
|
164
160
|
from getting recreated on every data update.
|
|
165
161
|
|
|
166
162
|
---
|
|
@@ -171,7 +167,7 @@ Each opt-in feature has two pieces in v9:
|
|
|
171
167
|
|
|
172
168
|
1. The **feature** itself (`rowSortingFeature`) in `features` — adds APIs
|
|
173
169
|
like `column.toggleSorting()` and the `sorting` state slice.
|
|
174
|
-
2. The **row-model factory** (`createSortedRowModel(
|
|
170
|
+
2. The **row-model factory** (`createSortedRowModel()`) on the features object
|
|
175
171
|
— produces the sorted output. Without it, `table.getRowModel().rows` is
|
|
176
172
|
unsorted regardless of sort state.
|
|
177
173
|
|
|
@@ -187,13 +183,12 @@ import {
|
|
|
187
183
|
|
|
188
184
|
const features = tableFeatures({
|
|
189
185
|
rowSortingFeature,
|
|
186
|
+
sortedRowModel: createSortedRowModel(), // <-- enables sorting output
|
|
187
|
+
sortFns,
|
|
190
188
|
})
|
|
191
189
|
|
|
192
190
|
readonly table = injectTable(() => ({
|
|
193
191
|
features,
|
|
194
|
-
rowModels: {
|
|
195
|
-
sortedRowModel: createSortedRowModel(sortFns), // <-- enables sorting output
|
|
196
|
-
},
|
|
197
192
|
columns,
|
|
198
193
|
data: this.data(),
|
|
199
194
|
}))
|
|
@@ -220,7 +215,7 @@ In the template, drive sorting from the header:
|
|
|
220
215
|
|
|
221
216
|
`sortFns` is the registry of built-in sort functions
|
|
222
217
|
(`alphanumeric`, `basic`, `datetime`, etc.). Pass only the ones you use to
|
|
223
|
-
tree-shake (`
|
|
218
|
+
tree-shake (`sortFns: { basic: sortFns.basic }`), or pass `sortFns`
|
|
224
219
|
in its entirety for all of them.
|
|
225
220
|
|
|
226
221
|
---
|
|
@@ -246,15 +241,15 @@ const features = tableFeatures({
|
|
|
246
241
|
rowSortingFeature,
|
|
247
242
|
columnFilteringFeature,
|
|
248
243
|
rowPaginationFeature,
|
|
244
|
+
sortedRowModel: createSortedRowModel(),
|
|
245
|
+
filteredRowModel: createFilteredRowModel(),
|
|
246
|
+
paginatedRowModel: createPaginatedRowModel(),
|
|
247
|
+
sortFns,
|
|
248
|
+
filterFns,
|
|
249
249
|
})
|
|
250
250
|
|
|
251
251
|
readonly table = injectTable(() => ({
|
|
252
252
|
features,
|
|
253
|
-
rowModels: {
|
|
254
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
255
|
-
filteredRowModel: createFilteredRowModel(filterFns),
|
|
256
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
257
|
-
},
|
|
258
253
|
columns,
|
|
259
254
|
data: this.data(),
|
|
260
255
|
initialState: {
|
|
@@ -323,8 +318,8 @@ const columns = columnHelper.columns([
|
|
|
323
318
|
> v9 changed the generic order: `createColumnHelper<typeof features, Person>()`,
|
|
324
319
|
> **not** `createColumnHelper<Person>()`. Same for `ColumnDef<typeof features, Person>`.
|
|
325
320
|
|
|
326
|
-
If multiple components share the same `features`
|
|
327
|
-
|
|
321
|
+
If multiple components share the same `features` object, factor them into a
|
|
322
|
+
`createTableHook(...)` call — see
|
|
328
323
|
`tanstack-table/angular/angular-rendering-directives` §10 and the
|
|
329
324
|
`composable-tables` example.
|
|
330
325
|
|
|
@@ -338,7 +333,6 @@ row pinning, and refetch-based updates correct.
|
|
|
338
333
|
```ts
|
|
339
334
|
readonly table = injectTable(() => ({
|
|
340
335
|
features,
|
|
341
|
-
rowModels: { /* … */ },
|
|
342
336
|
columns,
|
|
343
337
|
data: this.data(),
|
|
344
338
|
getRowId: (row) => row.id, // ← stable ID across re-fetches
|
|
@@ -360,7 +354,6 @@ and `table.setSorting(...)` to drive updates.
|
|
|
360
354
|
```ts
|
|
361
355
|
readonly table = injectTable(() => ({
|
|
362
356
|
features,
|
|
363
|
-
rowModels: { /* … */ },
|
|
364
357
|
columns,
|
|
365
358
|
data: this.data(),
|
|
366
359
|
initialState: {
|
|
@@ -404,8 +397,8 @@ import { injectTable, tableFeatures } from '@tanstack/angular-table'
|
|
|
404
397
|
|
|
405
398
|
There is no `getCoreRowModel()` / `getSortedRowModel()` / `getFilteredRowModel()`
|
|
406
399
|
in v9. Core row model is automatic; the rest are
|
|
407
|
-
`createSortedRowModel(
|
|
408
|
-
registered
|
|
400
|
+
`createSortedRowModel()` / `createFilteredRowModel()` / etc.
|
|
401
|
+
registered as slots on the `features` object alongside `sortFns` / `filterFns`.
|
|
409
402
|
|
|
410
403
|
### 3. (CRITICAL) Reimplementing what the table API already does
|
|
411
404
|
|
|
@@ -424,37 +417,35 @@ The table already does all of this. Use it.
|
|
|
424
417
|
### 4. (HIGH) Feature without its row model (or vice versa)
|
|
425
418
|
|
|
426
419
|
```ts
|
|
427
|
-
// ❌ rowSortingFeature without
|
|
428
|
-
features
|
|
429
|
-
rowModels: {
|
|
430
|
-
}
|
|
420
|
+
// ❌ rowSortingFeature without sortedRowModel → sort state changes, rows don't reorder
|
|
421
|
+
const features = tableFeatures({ rowSortingFeature })
|
|
431
422
|
|
|
432
|
-
// ✅
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
423
|
+
// ✅ factory and fn registry on the features object
|
|
424
|
+
const features = tableFeatures({
|
|
425
|
+
rowSortingFeature,
|
|
426
|
+
sortedRowModel: createSortedRowModel(),
|
|
427
|
+
sortFns,
|
|
428
|
+
})
|
|
436
429
|
```
|
|
437
430
|
|
|
438
431
|
Full mapping table → [`references/feature-row-model-mapping.md`](references/feature-row-model-mapping.md).
|
|
439
432
|
|
|
440
|
-
### 5. (HIGH) Declaring `columns` / `features`
|
|
433
|
+
### 5. (HIGH) Declaring `columns` / `features` inside the initializer
|
|
441
434
|
|
|
442
435
|
```ts
|
|
443
436
|
// ❌ Recreated on every signal change
|
|
444
437
|
readonly table = injectTable(() => ({
|
|
445
|
-
features: tableFeatures({ rowSortingFeature }),
|
|
446
|
-
rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
|
|
438
|
+
features: tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns }),
|
|
447
439
|
columns: [/* … */],
|
|
448
440
|
data: this.data(),
|
|
449
441
|
}))
|
|
450
442
|
|
|
451
443
|
// ✅ Stable references outside, signal reads inside
|
|
452
|
-
const features = tableFeatures({ rowSortingFeature })
|
|
444
|
+
const features = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns })
|
|
453
445
|
const columns: Array<ColumnDef<typeof features, Person>> = [/* … */]
|
|
454
446
|
|
|
455
447
|
readonly table = injectTable(() => ({
|
|
456
448
|
features,
|
|
457
|
-
rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
|
|
458
449
|
columns,
|
|
459
450
|
data: this.data(),
|
|
460
451
|
}))
|
|
@@ -10,16 +10,19 @@ Every opt-in v9 feature has two pieces:
|
|
|
10
10
|
|
|
11
11
|
1. The **feature** itself in `features` — adds APIs (e.g.
|
|
12
12
|
`column.toggleSorting()`) and the matching state slice.
|
|
13
|
-
2. A **row-model factory**
|
|
14
|
-
Without it, sort/filter/paginate UI updates but rows don't reorder.
|
|
13
|
+
2. A **row-model factory slot** on the `features` object — produces the derived
|
|
14
|
+
row output. Without it, sort/filter/paginate UI updates but rows don't reorder.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Fn registries (`sortFns`, `filterFns`, `aggregationFns`) are also slots on the
|
|
17
|
+
`features` object, not parameters to the factory calls.
|
|
18
|
+
|
|
19
|
+
| Feature | Slot needed on `features` |
|
|
17
20
|
| ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
18
|
-
| `rowSortingFeature` | `sortedRowModel: createSortedRowModel(
|
|
19
|
-
| `columnFilteringFeature` / `globalFilteringFeature` | `filteredRowModel: createFilteredRowModel(
|
|
21
|
+
| `rowSortingFeature` | `sortedRowModel: createSortedRowModel()` + `sortFns` |
|
|
22
|
+
| `columnFilteringFeature` / `globalFilteringFeature` | `filteredRowModel: createFilteredRowModel()` + `filterFns` |
|
|
20
23
|
| `rowPaginationFeature` | `paginatedRowModel: createPaginatedRowModel()` |
|
|
21
24
|
| `rowExpandingFeature` | `expandedRowModel: createExpandedRowModel()` |
|
|
22
|
-
| `columnGroupingFeature` | `groupedRowModel: createGroupedRowModel(
|
|
25
|
+
| `columnGroupingFeature` | `groupedRowModel: createGroupedRowModel()` + `aggregationFns` |
|
|
23
26
|
| `columnFacetingFeature` | `facetedRowModel: createFacetedRowModel()` (+ `facetedMinMaxValues` / `facetedUniqueValues`) |
|
|
24
27
|
| `rowSelectionFeature` | (no row model needed) |
|
|
25
28
|
| `columnVisibilityFeature` / `columnOrderingFeature` / `columnPinningFeature` / `columnSizingFeature` / `columnResizingFeature` / `rowPinningFeature` | (no row model needed) |
|