@tanstack/angular-table 9.0.0-beta.1 → 9.0.0-beta.11
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 +4 -47
- package/dist/fesm2022/tanstack-angular-table.mjs.map +1 -1
- package/dist/types/tanstack-angular-table.d.ts +8 -14
- package/package.json +3 -3
- 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
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
name: angular/migrate-v8-to-v9
|
|
3
3
|
description: >
|
|
4
4
|
Mechanical v8 → v9 migration for `@tanstack/angular-table`: `createAngularTable` →
|
|
5
|
-
`injectTable`, `get*RowModel()` options →
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
`
|
|
10
|
-
split
|
|
11
|
-
|
|
5
|
+
`injectTable`, `get*RowModel()` options → row-model factories on the `features` object (no
|
|
6
|
+
separate `rowModels` option; fn registries like `filterFns`/`sortFns` are also slots on
|
|
7
|
+
`features`), required `features` via `tableFeatures()`, state access via
|
|
8
|
+
`table.atoms.<slice>.get()` or `table.state` instead of `table.getState()`,
|
|
9
|
+
`createColumnHelper<TFeatures, TData>()` generic-order flip, every type now requires `TFeatures`,
|
|
10
|
+
`enablePinning` split into `enableColumnPinning` / `enableRowPinning`, `sortingFn` → `sortFn`
|
|
11
|
+
rename pile, `ColumnSizingInfo` → `ColumnResizing` split, removal of `_`-prefixed internals,
|
|
12
|
+
signal-backed atoms replacing v8 memoized accessors, and structural-directive rendering replacing
|
|
13
|
+
v8 component-based rendering.
|
|
12
14
|
type: lifecycle
|
|
13
15
|
library: tanstack-table
|
|
14
16
|
framework: angular
|
|
@@ -27,9 +29,9 @@ sources:
|
|
|
27
29
|
# Migrate from TanStack Table v8 to v9 (Angular)
|
|
28
30
|
|
|
29
31
|
> **Angular does not ship a legacy v8 API in v9** (unlike React's
|
|
30
|
-
> `useLegacyTable`). You migrate directly to v9's `injectTable` + `features`
|
|
31
|
-
>
|
|
32
|
-
> entrypoint name itself changes.
|
|
32
|
+
> `useLegacyTable`). You migrate directly to v9's `injectTable` + `features`
|
|
33
|
+
> shape (row-model factories and fn registries now live on the `features` object).
|
|
34
|
+
> There is no incremental in-place adapter — the public entrypoint name itself changes.
|
|
33
35
|
|
|
34
36
|
This skill is a mechanical translation table. Work through it top-to-bottom.
|
|
35
37
|
|
|
@@ -58,7 +60,6 @@ const features = tableFeatures({}) // empty is valid; core row model is automati
|
|
|
58
60
|
|
|
59
61
|
const v9Table = injectTable(() => ({
|
|
60
62
|
features,
|
|
61
|
-
rowModels: {},
|
|
62
63
|
columns,
|
|
63
64
|
data: data(),
|
|
64
65
|
}))
|
|
@@ -66,14 +67,16 @@ const v9Table = injectTable(() => ({
|
|
|
66
67
|
|
|
67
68
|
Key behavioral change: **the `injectTable` initializer re-runs when signals
|
|
68
69
|
inside it change**, then the adapter calls `table.setOptions({ ...prev, ...new })`.
|
|
69
|
-
Move stable values (`columns`, `features
|
|
70
|
-
|
|
70
|
+
Move stable values (`columns`, `features`) **outside** the initializer so they
|
|
71
|
+
aren't recreated on every data update.
|
|
71
72
|
|
|
72
73
|
---
|
|
73
74
|
|
|
74
|
-
## 2. Required new
|
|
75
|
+
## 2. Required new option: `features`
|
|
75
76
|
|
|
76
|
-
v9 is opt-in for every feature.
|
|
77
|
+
v9 is opt-in for every feature. `features` is required. Row-model factories and
|
|
78
|
+
fn registries are now **slots on the features object** — there is no separate
|
|
79
|
+
`rowModels` option.
|
|
77
80
|
|
|
78
81
|
```ts
|
|
79
82
|
// v8 — features were bundled, row models added piecewise
|
|
@@ -106,15 +109,15 @@ const features = tableFeatures({
|
|
|
106
109
|
columnFilteringFeature,
|
|
107
110
|
rowSortingFeature,
|
|
108
111
|
rowPaginationFeature,
|
|
112
|
+
filteredRowModel: createFilteredRowModel(), // factory is now a features slot
|
|
113
|
+
sortedRowModel: createSortedRowModel(),
|
|
114
|
+
paginatedRowModel: createPaginatedRowModel(),
|
|
115
|
+
filterFns, // fn registry is also a features slot
|
|
116
|
+
sortFns, // note rename: sortingFns → sortFns
|
|
109
117
|
})
|
|
110
118
|
|
|
111
119
|
injectTable(() => ({
|
|
112
120
|
features,
|
|
113
|
-
rowModels: {
|
|
114
|
-
filteredRowModel: createFilteredRowModel(filterFns), // fns are PARAMETERS now
|
|
115
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
116
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
117
|
-
},
|
|
118
121
|
columns,
|
|
119
122
|
data: data(),
|
|
120
123
|
}))
|
|
@@ -167,7 +170,6 @@ readonly pagination = signal<PaginationState>({ pageIndex: 0, pageSize: 10 })
|
|
|
167
170
|
|
|
168
171
|
readonly table = injectTable(() => ({
|
|
169
172
|
features,
|
|
170
|
-
rowModels: { /* … */ },
|
|
171
173
|
columns,
|
|
172
174
|
data: this.data(),
|
|
173
175
|
state: {
|
|
@@ -302,10 +304,10 @@ v8 backed reactivity with manual memoized getters. v9's adapter
|
|
|
302
304
|
|
|
303
305
|
- [ ] Replace `createAngularTable` import + call with `injectTable`.
|
|
304
306
|
- [ ] Add `features: tableFeatures({...})` (or `stockFeatures`) — required.
|
|
305
|
-
- [ ] Convert every `get*RowModel()` option to a
|
|
306
|
-
the matching `create*RowModel(
|
|
307
|
-
- [ ]
|
|
308
|
-
|
|
307
|
+
- [ ] Convert every `get*RowModel()` option to a slot on the `features` object
|
|
308
|
+
using the matching `create*RowModel()` factory.
|
|
309
|
+
- [ ] Move `filterFns` / `sortFns` / `aggregationFns` fn registries to slots
|
|
310
|
+
on the `features` object (not as factory parameters).
|
|
309
311
|
- [ ] Update `createColumnHelper<Person>()` → `createColumnHelper<typeof features, Person>()`.
|
|
310
312
|
- [ ] Update every `ColumnDef<Person>` / `Cell<Person, X>` etc. to include
|
|
311
313
|
`TFeatures`.
|
|
@@ -314,8 +316,7 @@ v8 backed reactivity with manual memoized getters. v9's adapter
|
|
|
314
316
|
- [ ] Remove any usage of the v8 single `onStateChange` — split into per-slice
|
|
315
317
|
`on[State]Change`.
|
|
316
318
|
- [ ] In `on[State]Change` callbacks, handle both value and updater-fn shapes.
|
|
317
|
-
- [ ] Move `columns
|
|
318
|
-
initializer.
|
|
319
|
+
- [ ] Move `columns` and `features` **outside** the `injectTable` initializer.
|
|
319
320
|
- [ ] Switch any `flexRender` long-form to `*flexRenderCell` / `*flexRenderHeader` /
|
|
320
321
|
`*flexRenderFooter` shorthand where applicable.
|
|
321
322
|
- [ ] Where you need component rendering with explicit options, switch wrapper
|
|
@@ -326,8 +327,9 @@ v8 backed reactivity with manual memoized getters. v9's adapter
|
|
|
326
327
|
- [ ] Rename `sortingFn` → `sortFn`, `getSortingFn` → `getSortFn`,
|
|
327
328
|
`sortingFns` → `sortFns`, `SortingFn` → `SortFn`.
|
|
328
329
|
- [ ] Replace `columnSizingInfo` state / setters / change handler with the
|
|
329
|
-
`columnResizing` equivalents; add `
|
|
330
|
-
if you actually drag-resize
|
|
330
|
+
`columnResizing` equivalents; add `columnSizingFeature` and
|
|
331
|
+
`columnResizingFeature` to `features` if you actually drag-resize
|
|
332
|
+
(`columnResizingFeature` requires `columnSizingFeature`).
|
|
331
333
|
- [ ] Replace `enablePinning` with `enableColumnPinning` / `enableRowPinning`.
|
|
332
334
|
- [ ] Update `ColumnMeta` module augmentation to include the `TFeatures`
|
|
333
335
|
generic.
|
|
@@ -342,9 +344,9 @@ v8 backed reactivity with manual memoized getters. v9's adapter
|
|
|
342
344
|
|
|
343
345
|
### 1. (CRITICAL) Leaving `getCoreRowModel()` / `getSortedRowModel()` / etc. in v9 options
|
|
344
346
|
|
|
345
|
-
These options don't exist anymore.
|
|
346
|
-
|
|
347
|
-
it with `as any` — don't.
|
|
347
|
+
These options don't exist anymore. The equivalent factories become slots on the
|
|
348
|
+
`features` object (e.g. `sortedRowModel: createSortedRowModel()`). The TypeScript
|
|
349
|
+
error is loud but agents sometimes silence it with `as any` — don't.
|
|
348
350
|
|
|
349
351
|
### 2. (CRITICAL) Reaching for `createAngularTable` from v8 muscle memory
|
|
350
352
|
|
|
@@ -356,14 +358,14 @@ it must run from a class field, constructor, or
|
|
|
356
358
|
|
|
357
359
|
```ts
|
|
358
360
|
// ❌ filtering enabled, but no filtered row model — UI changes, rows don't filter
|
|
359
|
-
features
|
|
360
|
-
rowModels: {
|
|
361
|
-
} // missing filteredRowModel
|
|
361
|
+
const features = tableFeatures({ columnFilteringFeature }) // missing filteredRowModel
|
|
362
362
|
|
|
363
363
|
// ✅
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
364
|
+
const features = tableFeatures({
|
|
365
|
+
columnFilteringFeature,
|
|
366
|
+
filteredRowModel: createFilteredRowModel(),
|
|
367
|
+
filterFns,
|
|
368
|
+
})
|
|
367
369
|
```
|
|
368
370
|
|
|
369
371
|
Same for sorting, pagination, expanding, grouping, faceting. Selection,
|
|
@@ -390,11 +392,12 @@ to default sort.
|
|
|
390
392
|
const columnHelper = createColumnHelper<Person>()
|
|
391
393
|
```
|
|
392
394
|
|
|
393
|
-
### 7. (HIGH) Putting `features` / `columns`
|
|
395
|
+
### 7. (HIGH) Putting `features` / `columns` inside the `injectTable` initializer
|
|
394
396
|
|
|
395
397
|
The v8 mental model was "build columns inside the hook". v9's
|
|
396
398
|
`injectTable` initializer re-runs on every signal read change — keep heavy
|
|
397
|
-
literals outside.
|
|
399
|
+
literals outside. Because row-model factories and fn registries live on the
|
|
400
|
+
`features` object, keeping `features` stable at module scope covers all of them.
|
|
398
401
|
|
|
399
402
|
Lower-severity failure modes (MEDIUM/LOW: `stockFeatures` cleanup, `enablePinning`
|
|
400
403
|
removal, `columnSizingInfo` rename, single-handler porting, hand-rolled
|
|
@@ -8,17 +8,21 @@ exhaustive lookup.
|
|
|
8
8
|
|
|
9
9
|
## Row-model migration table
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
11
|
+
Row-model factories and fn registries are now **slots on the `features` object**,
|
|
12
|
+
not a separate `rowModels` option. Factory calls no longer take fn arguments;
|
|
13
|
+
pass the fn map as a sibling slot instead.
|
|
14
|
+
|
|
15
|
+
| v8 option | v9 `features` slot key | v9 factory call | v9 fn registry slot |
|
|
16
|
+
| -------------------------- | ---------------------- | ----------------------------- | ------------------- |
|
|
17
|
+
| `getCoreRowModel()` | (automatic) | — | — |
|
|
18
|
+
| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel()` | `filterFns` |
|
|
19
|
+
| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel()` | `sortFns` |
|
|
20
|
+
| `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` | — |
|
|
21
|
+
| `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` | — |
|
|
22
|
+
| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel()` | `aggregationFns` |
|
|
23
|
+
| `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` | — |
|
|
24
|
+
| `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` | — |
|
|
25
|
+
| `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` | — |
|
|
22
26
|
|
|
23
27
|
## Feature registration table
|
|
24
28
|
|
|
@@ -116,7 +120,7 @@ declare module '@tanstack/angular-table' {
|
|
|
116
120
|
| `SortingFns` interface | `SortFns` |
|
|
117
121
|
| `sortingFns` (built-in fn registry) | `sortFns` |
|
|
118
122
|
|
|
119
|
-
Find-and-replace, then
|
|
123
|
+
Find-and-replace, then verify `sortFns` is a slot on `features` alongside `sortedRowModel: createSortedRowModel()`.
|
|
120
124
|
|
|
121
125
|
---
|
|
122
126
|
|
|
@@ -171,13 +175,16 @@ during migration if you have shared base config:
|
|
|
171
175
|
import { tableOptions, tableFeatures, rowSortingFeature } from '@tanstack/angular-table'
|
|
172
176
|
|
|
173
177
|
const baseOptions = tableOptions({
|
|
174
|
-
features: tableFeatures({
|
|
178
|
+
features: tableFeatures({
|
|
179
|
+
rowSortingFeature,
|
|
180
|
+
sortedRowModel: createSortedRowModel(),
|
|
181
|
+
sortFns,
|
|
182
|
+
}),
|
|
175
183
|
debugTable: isDevMode(),
|
|
176
184
|
})
|
|
177
185
|
|
|
178
186
|
readonly table = injectTable(() => ({
|
|
179
187
|
...baseOptions,
|
|
180
|
-
rowModels: { /* … */ },
|
|
181
188
|
columns: this.columns,
|
|
182
189
|
data: this.data(),
|
|
183
190
|
}))
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
name: angular/production-readiness
|
|
3
3
|
description: >
|
|
4
4
|
Ship-ready optimizations for Angular Table v9: register only the `features` you actually use
|
|
5
|
-
(tree-shake the bundle); keep `columns` / `features`
|
|
6
|
-
stable references OUTSIDE the `injectTable` initializer; pass only the `*Fns`
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
(tree-shake the bundle); keep `columns` / `features` (which carries row-model factories and
|
|
6
|
+
fn registries) as stable references OUTSIDE the `injectTable` initializer; pass only the `*Fns`
|
|
7
|
+
your data needs as slots on the features object; use `ChangeDetectionStrategy.OnPush`; lean on
|
|
8
|
+
signal-backed atoms (`table.atoms.<slice>.get()`) instead of broad `table.state` reads where
|
|
9
|
+
granularity matters; use `{ equal: shallow }` on object/array `computed` selectors; set
|
|
10
|
+
`getRowId` for stable identity; track by `id` in every `@for`; defer cell components with
|
|
11
|
+
`flexRenderComponent` only when you need its options; scope DI tokens via `[tanStackTable*]`
|
|
12
|
+
directives to kill prop drilling.
|
|
13
13
|
type: lifecycle
|
|
14
14
|
library: tanstack-table
|
|
15
15
|
framework: angular
|
|
@@ -59,10 +59,11 @@ Use `stockFeatures` to bootstrap during a v8 → v9 migration, then **come back
|
|
|
59
59
|
and curate**. The bundle wins only land once you do.
|
|
60
60
|
|
|
61
61
|
The same applies to feature-fn registries — pass only the `*Fns` your data
|
|
62
|
-
needs:
|
|
62
|
+
needs as slots on the features object:
|
|
63
63
|
|
|
64
64
|
```ts
|
|
65
65
|
import {
|
|
66
|
+
tableFeatures,
|
|
66
67
|
createSortedRowModel,
|
|
67
68
|
createFilteredRowModel,
|
|
68
69
|
sortFns,
|
|
@@ -70,16 +71,20 @@ import {
|
|
|
70
71
|
} from '@tanstack/angular-table'
|
|
71
72
|
|
|
72
73
|
// ❌ pulls in every built-in sort + filter fn
|
|
73
|
-
|
|
74
|
-
sortedRowModel:
|
|
75
|
-
filteredRowModel: createFilteredRowModel(
|
|
76
|
-
|
|
74
|
+
const features = tableFeatures({
|
|
75
|
+
sortedRowModel: createSortedRowModel(),
|
|
76
|
+
filteredRowModel: createFilteredRowModel(),
|
|
77
|
+
sortFns,
|
|
78
|
+
filterFns,
|
|
79
|
+
})
|
|
77
80
|
|
|
78
81
|
// ✅ only what you use
|
|
79
|
-
|
|
80
|
-
sortedRowModel:
|
|
81
|
-
filteredRowModel: createFilteredRowModel(
|
|
82
|
-
}
|
|
82
|
+
const features = tableFeatures({
|
|
83
|
+
sortedRowModel: createSortedRowModel(),
|
|
84
|
+
filteredRowModel: createFilteredRowModel(),
|
|
85
|
+
sortFns: { basic: sortFns.basic, datetime: sortFns.datetime },
|
|
86
|
+
filterFns: { includesString: filterFns.includesString },
|
|
87
|
+
})
|
|
83
88
|
```
|
|
84
89
|
|
|
85
90
|
Same logic for `aggregationFns` if you use grouping.
|
|
@@ -91,30 +96,28 @@ Same logic for `aggregationFns` if you use grouping.
|
|
|
91
96
|
`injectTable(() => ({...}))` **re-runs the initializer every time a signal read
|
|
92
97
|
inside it changes** and then calls `table.setOptions({ ...prev, ...new })`.
|
|
93
98
|
Anything you create inside the initializer is recreated on every signal
|
|
94
|
-
change.
|
|
99
|
+
change. Because row-model factories and fn registries live on the `features`
|
|
100
|
+
object, keeping `features` stable at module scope covers all of them.
|
|
95
101
|
|
|
96
102
|
```ts
|
|
97
|
-
// ❌ columns / features
|
|
103
|
+
// ❌ columns / features recreated on every data() change
|
|
98
104
|
@Component({...})
|
|
99
105
|
export class App {
|
|
100
106
|
readonly table = injectTable(() => ({
|
|
101
|
-
features: tableFeatures({ rowSortingFeature
|
|
102
|
-
|
|
103
|
-
columns: [/* … */], // ← new ref each run
|
|
107
|
+
features: tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns }),
|
|
108
|
+
columns: [/* … */],
|
|
104
109
|
data: this.data(),
|
|
105
110
|
}))
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
// ✅ stable references outside; only reactive reads inside
|
|
109
|
-
const features = tableFeatures({ rowSortingFeature })
|
|
110
|
-
const rowModels = { sortedRowModel: createSortedRowModel(sortFns) }
|
|
114
|
+
const features = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns })
|
|
111
115
|
const columns: Array<ColumnDef<typeof features, Person>> = [/* … */]
|
|
112
116
|
|
|
113
117
|
@Component({...})
|
|
114
118
|
export class App {
|
|
115
119
|
readonly table = injectTable(() => ({
|
|
116
120
|
features,
|
|
117
|
-
rowModels,
|
|
118
121
|
columns,
|
|
119
122
|
data: this.data(),
|
|
120
123
|
}))
|
|
@@ -125,7 +128,7 @@ Same rule for the controlled-state pattern — keep `state: { pagination: this.p
|
|
|
125
128
|
inside the initializer, but keep the signal definitions on the class.
|
|
126
129
|
|
|
127
130
|
For shared infrastructure across multiple tables, `createTableHook(...)` lets
|
|
128
|
-
you define `features` /
|
|
131
|
+
you define `features` / default options once at module scope.
|
|
129
132
|
|
|
130
133
|
---
|
|
131
134
|
|
|
@@ -337,11 +340,11 @@ the wiring cost. Pick exactly one source of truth per slice (see
|
|
|
337
340
|
## 13. Quick wins checklist
|
|
338
341
|
|
|
339
342
|
- [ ] `features` listed explicitly (no `stockFeatures` in production).
|
|
340
|
-
- [ ] `*Fns`
|
|
341
|
-
`
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
343
|
+
- [ ] `*Fns` registry slots on `features` pass only what you use (not the full
|
|
344
|
+
`sortFns` / `filterFns` / `aggregationFns` spread when you can narrow it).
|
|
345
|
+
- [ ] `columns` and `features` are at module scope or stable class fields —
|
|
346
|
+
never inside the `injectTable` initializer (factories and fn registries
|
|
347
|
+
live on `features`, so this covers them too).
|
|
345
348
|
- [ ] Component is `ChangeDetectionStrategy.OnPush`.
|
|
346
349
|
- [ ] `getRowId` set when rows have a stable primary key.
|
|
347
350
|
- [ ] All `@for` blocks track by `id`.
|
|
@@ -366,13 +369,12 @@ the wiring cost. Pick exactly one source of truth per slice (see
|
|
|
366
369
|
calls this out — `stockFeatures` is a v8 → v9 bootstrap, not a production
|
|
367
370
|
end-state.
|
|
368
371
|
|
|
369
|
-
### 2. (CRITICAL) Recreating `columns` / `features`
|
|
370
|
-
|
|
371
|
-
`injectTable` initializer
|
|
372
|
+
### 2. (CRITICAL) Recreating `columns` / `features` inside the `injectTable` initializer
|
|
372
373
|
|
|
373
|
-
The initializer re-runs on every signal read change.
|
|
374
|
+
The initializer re-runs on every signal read change. A new `columns` reference
|
|
374
375
|
triggers full column-model rebuilds — for big tables this is visibly slow.
|
|
375
|
-
Module-scope
|
|
376
|
+
Module-scope both `columns` and `features` (which carries the factories and fn
|
|
377
|
+
registries).
|
|
376
378
|
|
|
377
379
|
### 3. (CRITICAL) Reimplementing what the table already does
|
|
378
380
|
|
|
@@ -386,7 +388,8 @@ Symptoms:
|
|
|
386
388
|
|
|
387
389
|
All of these are far slower than the built-in row models (which memoize and
|
|
388
390
|
short-circuit) and ship more code. Use `table.setSorting(...)`,
|
|
389
|
-
`table.setColumnFilters(...)`, the registered
|
|
391
|
+
`table.setColumnFilters(...)`, and the row-model factories registered on the
|
|
392
|
+
`features` object.
|
|
390
393
|
|
|
391
394
|
### 4. (HIGH) `OnPush` not set
|
|
392
395
|
|
|
@@ -36,7 +36,7 @@ sources:
|
|
|
36
36
|
|
|
37
37
|
---
|
|
38
38
|
|
|
39
|
-
## 1. Prerequisites — `features`
|
|
39
|
+
## 1. Prerequisites — `features` decides what state exists
|
|
40
40
|
|
|
41
41
|
In v9, **a state slice only exists if its feature is registered in `features`**.
|
|
42
42
|
This is the #1 v9-specific gotcha and the root cause of many "missing API"
|
|
@@ -57,14 +57,13 @@ import {
|
|
|
57
57
|
const features = tableFeatures({
|
|
58
58
|
rowPaginationFeature,
|
|
59
59
|
rowSortingFeature,
|
|
60
|
+
paginatedRowModel: createPaginatedRowModel(),
|
|
61
|
+
sortedRowModel: createSortedRowModel(),
|
|
62
|
+
sortFns,
|
|
60
63
|
})
|
|
61
64
|
|
|
62
65
|
readonly table = injectTable(() => ({
|
|
63
66
|
features,
|
|
64
|
-
rowModels: {
|
|
65
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
66
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
67
|
-
},
|
|
68
67
|
columns,
|
|
69
68
|
data: this.data(),
|
|
70
69
|
}))
|
|
@@ -91,7 +90,6 @@ run inside an Angular injection context (a component constructor / class field).
|
|
|
91
90
|
```ts
|
|
92
91
|
readonly table = injectTable(() => ({
|
|
93
92
|
features,
|
|
94
|
-
rowModels: {},
|
|
95
93
|
columns,
|
|
96
94
|
data: this.data(),
|
|
97
95
|
}))
|
|
@@ -105,25 +103,23 @@ That means:
|
|
|
105
103
|
|
|
106
104
|
- **Reactive values that should re-sync the table** (`this.data()`, controlled
|
|
107
105
|
state signals) go _inside_ the initializer.
|
|
108
|
-
- **Stable references** (`columns`, `features
|
|
109
|
-
|
|
106
|
+
- **Stable references** (`columns`, `features`) go _outside_ — or you'll
|
|
107
|
+
recreate the column model on every data update.
|
|
110
108
|
|
|
111
109
|
```ts
|
|
112
110
|
// ❌ WRONG — columns + features recreated on every data change
|
|
113
111
|
readonly table = injectTable(() => ({
|
|
114
|
-
features: tableFeatures({ rowSortingFeature
|
|
115
|
-
|
|
116
|
-
columns: [/* … */], // ditto
|
|
112
|
+
features: tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns }),
|
|
113
|
+
columns: [/* … */],
|
|
117
114
|
data: this.data(),
|
|
118
115
|
}))
|
|
119
116
|
|
|
120
117
|
// ✅ Stable references outside, signal reads inside
|
|
121
|
-
const features = tableFeatures({ rowSortingFeature })
|
|
118
|
+
const features = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns })
|
|
122
119
|
const columns: Array<ColumnDef<typeof features, Person>> = [/* … */]
|
|
123
120
|
|
|
124
121
|
readonly table = injectTable(() => ({
|
|
125
122
|
features,
|
|
126
|
-
rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
|
|
127
123
|
columns,
|
|
128
124
|
data: this.data(), // ← only the signal read should be inside
|
|
129
125
|
}))
|
|
@@ -230,7 +226,6 @@ the value that reset APIs reset to.
|
|
|
230
226
|
```ts
|
|
231
227
|
readonly table = injectTable(() => ({
|
|
232
228
|
features,
|
|
233
|
-
rowModels: { /* … */ },
|
|
234
229
|
columns,
|
|
235
230
|
data: this.data(),
|
|
236
231
|
initialState: {
|
|
@@ -276,9 +271,6 @@ export class Component {
|
|
|
276
271
|
|
|
277
272
|
readonly table = injectTable(() => ({
|
|
278
273
|
features,
|
|
279
|
-
rowModels: {
|
|
280
|
-
/* … */
|
|
281
|
-
},
|
|
282
274
|
columns,
|
|
283
275
|
data: this.data(),
|
|
284
276
|
state: {
|
|
@@ -335,9 +327,9 @@ right ownership model.** When you need more, see
|
|
|
335
327
|
- **State type imports** — `PaginationState`, `SortingState`,
|
|
336
328
|
`RowSelectionState`, `TableState<typeof features>`, etc.
|
|
337
329
|
- **`createTableHook(...)`** — app-wide `injectAppTable` /
|
|
338
|
-
`createAppColumnHelper` that pre-bind `features`
|
|
339
|
-
exposes `tableComponents` / `cellComponents` /
|
|
340
|
-
(covered in `angular-rendering-directives`).
|
|
330
|
+
`createAppColumnHelper` that pre-bind `features` (which now carries row-model
|
|
331
|
+
factories and fn registries). Also exposes `tableComponents` / `cellComponents` /
|
|
332
|
+
`headerComponents` registries (covered in `angular-rendering-directives`).
|
|
341
333
|
|
|
342
334
|
---
|
|
343
335
|
|
|
@@ -359,15 +351,15 @@ import { injectTable, tableFeatures } from '@tanstack/angular-table'
|
|
|
359
351
|
const features = tableFeatures({})
|
|
360
352
|
const table = injectTable(() => ({
|
|
361
353
|
features,
|
|
362
|
-
rowModels: {},
|
|
363
354
|
columns,
|
|
364
355
|
data: data(),
|
|
365
356
|
}))
|
|
366
357
|
```
|
|
367
358
|
|
|
368
359
|
Also retired: `getFilteredRowModel`, `getSortedRowModel`, `getPaginationRowModel`
|
|
369
|
-
as top-level options → migrated to
|
|
370
|
-
|
|
360
|
+
as top-level options → migrated to slots on the `features` object
|
|
361
|
+
(`filteredRowModel: createFilteredRowModel()`, `sortedRowModel: createSortedRowModel()`,
|
|
362
|
+
`paginatedRowModel: createPaginatedRowModel()`) with fn registries (`filterFns`, `sortFns`) also on `features`.
|
|
371
363
|
|
|
372
364
|
### 2. (CRITICAL) Missing API because feature not in `features`
|
|
373
365
|
|
|
@@ -394,9 +386,10 @@ Same for `setPageIndex`, `setPageSize`, `setSorting`, `toggleSorting`,
|
|
|
394
386
|
### 4. (HIGH) Expensive values declared **inside** the `injectTable` initializer
|
|
395
387
|
|
|
396
388
|
Because the initializer re-runs when any reactive read inside it changes,
|
|
397
|
-
declaring `columns
|
|
398
|
-
|
|
399
|
-
|
|
389
|
+
declaring `columns` or `features` inside the function causes them to be
|
|
390
|
+
recreated and re-applied on every data update (row-model factories and fn
|
|
391
|
+
registries are part of `features`, so moving `features` outside covers all of
|
|
392
|
+
them). Move them outside the class or to stable class fields.
|
|
400
393
|
|
|
401
394
|
### 5. (HIGH) Forgetting that the initializer re-runs
|
|
402
395
|
|
|
@@ -24,7 +24,6 @@ const paginationAtom = new Store<PaginationState>({ pageIndex: 0, pageSize: 10 }
|
|
|
24
24
|
|
|
25
25
|
readonly table = injectTable(() => ({
|
|
26
26
|
features,
|
|
27
|
-
rowModels: { /* … */ },
|
|
28
27
|
columns,
|
|
29
28
|
data: this.data(),
|
|
30
29
|
atoms: {
|
|
@@ -68,7 +67,7 @@ type MyTableState = TableState<typeof features>
|
|
|
68
67
|
|
|
69
68
|
## `createTableHook` — app-wide table infrastructure
|
|
70
69
|
|
|
71
|
-
When multiple tables in an app share the same `features
|
|
70
|
+
When multiple tables in an app share the same `features` object and
|
|
72
71
|
component conventions, factor them into a `createTableHook(...)` call once and
|
|
73
72
|
import the resulting `injectAppTable` / `createAppColumnHelper`.
|
|
74
73
|
|
|
@@ -91,11 +90,13 @@ export const {
|
|
|
91
90
|
injectTableCellContext,
|
|
92
91
|
injectTableHeaderContext,
|
|
93
92
|
} = createTableHook({
|
|
94
|
-
features: tableFeatures({
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
features: tableFeatures({
|
|
94
|
+
rowSortingFeature,
|
|
95
|
+
rowPaginationFeature,
|
|
96
|
+
sortedRowModel: createSortedRowModel(),
|
|
97
97
|
paginatedRowModel: createPaginatedRowModel(),
|
|
98
|
-
|
|
98
|
+
sortFns,
|
|
99
|
+
}),
|
|
99
100
|
getRowId: (row) => row.id,
|
|
100
101
|
})
|
|
101
102
|
```
|
|
@@ -110,7 +111,7 @@ const columnHelper = createAppColumnHelper<Person>() // only TData generic neede
|
|
|
110
111
|
readonly table = injectAppTable(() => ({
|
|
111
112
|
columns,
|
|
112
113
|
data: this.data(),
|
|
113
|
-
})) // features
|
|
114
|
+
})) // features (including row-model factories) inherited
|
|
114
115
|
```
|
|
115
116
|
|
|
116
117
|
`createTableHook` also lets you register `tableComponents` / `cellComponents` /
|
package/src/helpers/cell.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface TanStackTableCellContext<
|
|
|
23
23
|
* This token is provided by the {@link TanStackTableCell} directive.
|
|
24
24
|
*/
|
|
25
25
|
export const TanStackTableCellToken = new InjectionToken<
|
|
26
|
-
TanStackTableCellContext<
|
|
26
|
+
TanStackTableCellContext<TableFeatures, RowData, CellData>['cell']
|
|
27
27
|
>('[TanStack Table] CellContext')
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -100,5 +100,9 @@ export function injectTableCellContext<
|
|
|
100
100
|
TData extends RowData,
|
|
101
101
|
TValue extends CellData,
|
|
102
102
|
>(): TanStackTableCellContext<TFeatures, TData, TValue>['cell'] {
|
|
103
|
-
return inject(TanStackTableCellToken)
|
|
103
|
+
return inject(TanStackTableCellToken) as unknown as TanStackTableCellContext<
|
|
104
|
+
TFeatures,
|
|
105
|
+
TData,
|
|
106
|
+
TValue
|
|
107
|
+
>['cell']
|
|
104
108
|
}
|
|
@@ -333,10 +333,7 @@ export type CreateTableHookResult<
|
|
|
333
333
|
TValue extends CellData,
|
|
334
334
|
>() => CellContext<TFeatures, TData, TValue>
|
|
335
335
|
injectAppTable: <TData extends RowData>(
|
|
336
|
-
tableOptions: () => Omit<
|
|
337
|
-
TableOptions<TFeatures, TData>,
|
|
338
|
-
'features' | 'rowModels'
|
|
339
|
-
>,
|
|
336
|
+
tableOptions: () => Omit<TableOptions<TFeatures, TData>, 'features'>,
|
|
340
337
|
) => AppAngularTable<
|
|
341
338
|
TFeatures,
|
|
342
339
|
TData,
|
|
@@ -358,7 +355,6 @@ export type CreateTableHookResult<
|
|
|
358
355
|
* ```ts
|
|
359
356
|
* const { injectAppTable, createAppColumnHelper } = createTableHook({
|
|
360
357
|
* features,
|
|
361
|
-
* rowModels: {},
|
|
362
358
|
* tableComponents: {},
|
|
363
359
|
* cellComponents: {},
|
|
364
360
|
* headerComponents: {},
|
|
@@ -424,10 +420,7 @@ export function createTableHook<
|
|
|
424
420
|
TData extends RowData,
|
|
425
421
|
TSelected = TableState<TFeatures>,
|
|
426
422
|
>(
|
|
427
|
-
tableOptions: () => Omit<
|
|
428
|
-
TableOptions<TFeatures, TData>,
|
|
429
|
-
'features' | 'rowModels'
|
|
430
|
-
>,
|
|
423
|
+
tableOptions: () => Omit<TableOptions<TFeatures, TData>, 'features'>,
|
|
431
424
|
): AppAngularTable<
|
|
432
425
|
TFeatures,
|
|
433
426
|
TData,
|
|
@@ -447,7 +440,7 @@ export function createTableHook<
|
|
|
447
440
|
return footer as Header<TFeatures, TData, any> & THeaderComponents
|
|
448
441
|
}
|
|
449
442
|
|
|
450
|
-
const appTableFeatures: TableFeature
|
|
443
|
+
const appTableFeatures: TableFeature = {
|
|
451
444
|
constructTableAPIs: (table) => {
|
|
452
445
|
Object.assign(table, tableComponents, { appCell, appHeader, appFooter })
|
|
453
446
|
},
|
|
@@ -467,8 +460,14 @@ export function createTableHook<
|
|
|
467
460
|
...defaultTableOptions.features,
|
|
468
461
|
appTableFeatures,
|
|
469
462
|
},
|
|
470
|
-
} as TableOptions<TFeatures, TData>
|
|
471
|
-
}) as
|
|
463
|
+
} as unknown as TableOptions<TFeatures, TData>
|
|
464
|
+
}) as unknown as AppAngularTable<
|
|
465
|
+
TFeatures,
|
|
466
|
+
TData,
|
|
467
|
+
TTableComponents,
|
|
468
|
+
TCellComponents,
|
|
469
|
+
THeaderComponents
|
|
470
|
+
>
|
|
472
471
|
}
|
|
473
472
|
|
|
474
473
|
function createAppColumnHelper<TData extends RowData>(): AppColumnHelper<
|