@tanstack/angular-table 9.0.0-alpha.47 → 9.0.0-alpha.49

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.
@@ -0,0 +1,415 @@
1
+ ---
2
+ name: angular/migrate-v8-to-v9
3
+ description: >
4
+ Mechanical v8 → v9 migration for `@tanstack/angular-table`: `createAngularTable` →
5
+ `injectTable`, `get*RowModel()` options → `_rowModels` factories with explicit `*Fns`,
6
+ required `_features` via `tableFeatures()`, `state` access via `table.store.state` instead
7
+ of `table.getState()`, `createColumnHelper<TFeatures, TData>()` generic-order flip, every
8
+ type now requires `TFeatures`, `enablePinning` split into `enableColumnPinning` /
9
+ `enableRowPinning`, `sortingFn` → `sortFn` rename pile, `ColumnSizingInfo` → `ColumnResizing`
10
+ split, removal of `_`-prefixed internals, signal-backed atoms replacing v8 memoized accessors,
11
+ and structural-directive rendering replacing v8 component-based rendering.
12
+ type: lifecycle
13
+ library: tanstack-table
14
+ framework: angular
15
+ library_version: '9.0.0-alpha.48'
16
+ requires:
17
+ - angular/table-state
18
+ - angular/getting-started
19
+ - angular/angular-rendering-directives
20
+ sources:
21
+ - TanStack/table:docs/framework/angular/guide/migrating.md
22
+ - TanStack/table:docs/framework/angular/angular-table.md
23
+ - TanStack/table:packages/angular-table/src/injectTable.ts
24
+ - TanStack/table:packages/angular-table/src/reactivity.ts
25
+ ---
26
+
27
+ # Migrate from TanStack Table v8 to v9 (Angular)
28
+
29
+ > **Angular does not ship a legacy v8 API in v9** (unlike React's
30
+ > `useLegacyTable`). You migrate directly to v9's `injectTable` + `_features` +
31
+ > `_rowModels` shape. There is no incremental in-place adapter — the public
32
+ > entrypoint name itself changes.
33
+
34
+ This skill is a mechanical translation table. Work through it top-to-bottom.
35
+
36
+ For exhaustive lookup tables (row-model mapping, feature registration, type
37
+ generics, sorting renames, sizing-vs-resizing split, etc.) →
38
+ [`references/v8-to-v9-mapping.md`](references/v8-to-v9-mapping.md).
39
+
40
+ ---
41
+
42
+ ## 1. Entrypoint rename
43
+
44
+ ```ts
45
+ // v8
46
+ import { createAngularTable, getCoreRowModel } from '@tanstack/angular-table'
47
+
48
+ const v8Table = createAngularTable(() => ({
49
+ columns,
50
+ data: data(),
51
+ getCoreRowModel: getCoreRowModel(),
52
+ }))
53
+
54
+ // v9
55
+ import { injectTable, tableFeatures } from '@tanstack/angular-table'
56
+
57
+ const _features = tableFeatures({}) // empty is valid; core row model is automatic
58
+
59
+ const v9Table = injectTable(() => ({
60
+ _features,
61
+ _rowModels: {},
62
+ columns,
63
+ data: data(),
64
+ }))
65
+ ```
66
+
67
+ Key behavioral change: **the `injectTable` initializer re-runs when signals
68
+ inside it change**, then the adapter calls `table.setOptions({ ...prev, ...new })`.
69
+ Move stable values (`columns`, `_features`, `_rowModels`) **outside** the
70
+ initializer so they aren't recreated on every data update.
71
+
72
+ ---
73
+
74
+ ## 2. Required new options: `_features` + `_rowModels`
75
+
76
+ v9 is opt-in for every feature. **Both options are required.**
77
+
78
+ ```ts
79
+ // v8 — features were bundled, row models added piecewise
80
+ createAngularTable(() => ({
81
+ columns,
82
+ data: data(),
83
+ getCoreRowModel: getCoreRowModel(),
84
+ getFilteredRowModel: getFilteredRowModel(),
85
+ getSortedRowModel: getSortedRowModel(),
86
+ getPaginationRowModel: getPaginationRowModel(),
87
+ filterFns, // root option
88
+ sortingFns, // root option
89
+ }))
90
+
91
+ // v9
92
+ import {
93
+ injectTable,
94
+ tableFeatures,
95
+ columnFilteringFeature,
96
+ rowSortingFeature,
97
+ rowPaginationFeature,
98
+ createFilteredRowModel,
99
+ createSortedRowModel,
100
+ createPaginatedRowModel,
101
+ filterFns,
102
+ sortFns, // note rename: sortingFns → sortFns
103
+ } from '@tanstack/angular-table'
104
+
105
+ const _features = tableFeatures({
106
+ columnFilteringFeature,
107
+ rowSortingFeature,
108
+ rowPaginationFeature,
109
+ })
110
+
111
+ injectTable(() => ({
112
+ _features,
113
+ _rowModels: {
114
+ filteredRowModel: createFilteredRowModel(filterFns), // fns are PARAMETERS now
115
+ sortedRowModel: createSortedRowModel(sortFns),
116
+ paginatedRowModel: createPaginatedRowModel(),
117
+ },
118
+ columns,
119
+ data: data(),
120
+ }))
121
+ ```
122
+
123
+ Row-model and feature lookup tables → [`references/v8-to-v9-mapping.md`](references/v8-to-v9-mapping.md#row-model-migration-table).
124
+
125
+ ---
126
+
127
+ ## 3. State access: `getState()` → `table.store.state` (and atoms)
128
+
129
+ ```ts
130
+ // v8
131
+ const { sorting, pagination } = table.getState()
132
+
133
+ // v9 — flat snapshot
134
+ const { sorting, pagination } = table.store.state
135
+
136
+ // v9 — per slice (signal-backed in Angular)
137
+ const sorting = table.atoms.sorting.get()
138
+ const pagination = table.atoms.pagination.get()
139
+ ```
140
+
141
+ In Angular, all three (`table.atoms.<slice>`, `table.store.state`,
142
+ `table.baseAtoms.<slice>`) are signal-backed — reading them inside a template,
143
+ `computed(...)`, or `effect(...)` registers an Angular dependency
144
+ automatically. No `toSignal(...)` wrappers needed.
145
+
146
+ See `tanstack-table/angular/table-state` for the full state surface mental
147
+ model.
148
+
149
+ ---
150
+
151
+ ## 4. Controlled state — `on[State]Change` shape
152
+
153
+ The shape is largely the same. **`onStateChange` (the single global v8 hook) is
154
+ gone in v9.** Slices are controlled individually via `state.<slice>` +
155
+ `on[State]Change` callbacks. Each callback receives either a new value or an
156
+ updater function:
157
+
158
+ ```ts
159
+ // v9 pattern
160
+ import { signal } from '@angular/core'
161
+ import type { SortingState, PaginationState } from '@tanstack/angular-table'
162
+
163
+ readonly sorting = signal<SortingState>([])
164
+ readonly pagination = signal<PaginationState>({ pageIndex: 0, pageSize: 10 })
165
+
166
+ readonly table = injectTable(() => ({
167
+ _features,
168
+ _rowModels: { /* … */ },
169
+ columns,
170
+ data: this.data(),
171
+ state: {
172
+ sorting: this.sorting(),
173
+ pagination: this.pagination(),
174
+ },
175
+ onSortingChange: (updater) => {
176
+ updater instanceof Function
177
+ ? this.sorting.update(updater)
178
+ : this.sorting.set(updater)
179
+ },
180
+ onPaginationChange: (updater) => {
181
+ updater instanceof Function
182
+ ? this.pagination.update(updater)
183
+ : this.pagination.set(updater)
184
+ },
185
+ }))
186
+ ```
187
+
188
+ > Always check `updater instanceof Function` (or `typeof updater === 'function'`).
189
+ > TanStack Table calls the callback with both shapes depending on the
190
+ > transition.
191
+
192
+ ---
193
+
194
+ ## 5. Column helper generic-order flip
195
+
196
+ ```ts
197
+ // v8
198
+ const columnHelper = createColumnHelper<Person>()
199
+
200
+ // v9 — TFeatures FIRST, then TData
201
+ const columnHelper = createColumnHelper<typeof _features, Person>()
202
+ ```
203
+
204
+ New in v9: `columnHelper.columns([...])` preserves each column's `TValue` —
205
+ prefer it over a bare array literal:
206
+
207
+ ```ts
208
+ const columns = columnHelper.columns([
209
+ columnHelper.accessor('firstName', {
210
+ header: 'First',
211
+ cell: (i) => i.getValue(),
212
+ }),
213
+ columnHelper.display({
214
+ id: 'actions',
215
+ header: 'Actions',
216
+ cell: () => 'Edit',
217
+ }),
218
+ ])
219
+ ```
220
+
221
+ If you don't want to repeat `TFeatures` everywhere, use `createTableHook(...)`
222
+ and the resulting `createAppColumnHelper<Person>()` which pre-binds features.
223
+
224
+ Every public type now requires `TFeatures` (`ColumnDef<TFeatures, TData, TValue>`,
225
+ `Cell<TFeatures, TData, TValue>`, etc.). Full mapping →
226
+ [`references/v8-to-v9-mapping.md`](references/v8-to-v9-mapping.md#type-generics--every-type-takes-tfeatures-now).
227
+
228
+ ---
229
+
230
+ ## 6. Rendering — directive-based, with shorthand directives
231
+
232
+ v8 Angular rendering was already directive-flavored, but v9 adds the
233
+ **shorthand directives** (`*flexRenderCell`, `*flexRenderHeader`,
234
+ `*flexRenderFooter`) that auto-resolve the column-def slot and context. Prefer
235
+ them over the long `*flexRender="… ; props: …"` form.
236
+
237
+ ```html
238
+ <!-- v8 / v9 long form (still works) -->
239
+ <td
240
+ *flexRender="cell.column.columnDef.cell; props: cell.getContext(); let rendered"
241
+ >
242
+ {{ rendered }}
243
+ </td>
244
+
245
+ <!-- v9 shorthand — recommended -->
246
+ <td *flexRenderCell="cell; let value">{{ value }}</td>
247
+ <th *flexRenderHeader="header; let value">{{ value }}</th>
248
+ <th *flexRenderFooter="footer; let value">{{ value }}</th>
249
+ ```
250
+
251
+ ```ts
252
+ import { FlexRender } from '@tanstack/angular-table' // ← imports both directives
253
+ @Component({ imports: [FlexRender], ... })
254
+ ```
255
+
256
+ v9-only:
257
+
258
+ - `flexRenderComponent(Component, { inputs, outputs, bindings, directives, injector })`
259
+ for explicit component rendering.
260
+ - DI tokens (`TanStackTable` / `TanStackTableHeader` / `TanStackTableCell`
261
+ directives + `injectTableContext()` / `injectTableHeaderContext()` /
262
+ `injectTableCellContext()` / `injectFlexRenderContext()`) — no more input
263
+ drilling.
264
+ - Column-def `cell` / `header` / `footer` functions run inside
265
+ `runInInjectionContext`, so `inject(...)` and signals work in them.
266
+
267
+ See `tanstack-table/angular/angular-rendering-directives` for the full surface.
268
+
269
+ ---
270
+
271
+ ## 7. Reactivity model — signals replace v8 memo accessors
272
+
273
+ v8 backed reactivity with manual memoized getters. v9's adapter
274
+ (`angularReactivity(injector)`) backs every readonly atom with an Angular
275
+ `computed` and every writable atom with an Angular `signal`. Consequences:
276
+
277
+ - **No `toSignal(...)` adapters around table state.** Read `table.atoms.x.get()`
278
+ / `table.store.state.x` directly inside templates, `computed`, `effect`.
279
+ - **`computed(...)` is for derivation / equality, not for "make it reactive".**
280
+ Use `{ equal: shallow }` from `@tanstack/angular-table` on object/array
281
+ slices to skip downstream work on no-op updates.
282
+ - **The `injectTable` initializer re-runs on signal changes.** Don't put
283
+ expensive object literals in there.
284
+
285
+ ---
286
+
287
+ ## 8. Renames at a glance
288
+
289
+ - `sortingFn` → `sortFn`, `sortingFns` → `sortFns`, `SortingFn` → `SortFn`
290
+ (full table in [`references/v8-to-v9-mapping.md`](references/v8-to-v9-mapping.md#naming-renames--sorting)).
291
+ - `enablePinning` → `enableColumnPinning` / `enableRowPinning` (split).
292
+ - `columnSizingInfo` state → `columnResizing` state (sizing/resizing split into
293
+ two features — see [`references/v8-to-v9-mapping.md`](references/v8-to-v9-mapping.md#column-sizing-vs-resizing--split)).
294
+ - All `_`-prefixed internal APIs removed; use the public equivalents.
295
+
296
+ ---
297
+
298
+ ## Migration checklist
299
+
300
+ - [ ] Replace `createAngularTable` import + call with `injectTable`.
301
+ - [ ] Add `_features: tableFeatures({...})` (or `stockFeatures`) — required.
302
+ - [ ] Convert every `get*RowModel()` option to a `_rowModels.<slot>` entry with
303
+ the matching `create*RowModel(...)` factory.
304
+ - [ ] Add `filterFns` / `sortFns` / `aggregationFns` as **factory parameters**
305
+ where needed.
306
+ - [ ] Update `createColumnHelper<Person>()` → `createColumnHelper<typeof _features, Person>()`.
307
+ - [ ] Update every `ColumnDef<Person>` / `Cell<Person, X>` etc. to include
308
+ `TFeatures`.
309
+ - [ ] Replace `table.getState()` reads with `table.store.state` (or
310
+ `table.atoms.<slice>.get()` for per-slice reactivity).
311
+ - [ ] Remove any usage of the v8 single `onStateChange` — split into per-slice
312
+ `on[State]Change`.
313
+ - [ ] In `on[State]Change` callbacks, handle both value and updater-fn shapes.
314
+ - [ ] Move `columns`, `_features`, `_rowModels` **outside** the `injectTable`
315
+ initializer.
316
+ - [ ] Switch any `flexRender` long-form to `*flexRenderCell` / `*flexRenderHeader` /
317
+ `*flexRenderFooter` shorthand where applicable.
318
+ - [ ] Where you need component rendering with explicit options, switch wrapper
319
+ shape to `flexRenderComponent(Component, { inputs, outputs, ... })`.
320
+ - [ ] Replace prop-drilled cell/header inputs with
321
+ `injectTableCellContext()` / `injectTableHeaderContext()` /
322
+ `injectFlexRenderContext()` (optional but worthwhile).
323
+ - [ ] Rename `sortingFn` → `sortFn`, `getSortingFn` → `getSortFn`,
324
+ `sortingFns` → `sortFns`, `SortingFn` → `SortFn`.
325
+ - [ ] Replace `columnSizingInfo` state / setters / change handler with the
326
+ `columnResizing` equivalents; add `columnResizingFeature` to `_features`
327
+ if you actually drag-resize.
328
+ - [ ] Replace `enablePinning` with `enableColumnPinning` / `enableRowPinning`.
329
+ - [ ] Update `ColumnMeta` module augmentation to include the `TFeatures`
330
+ generic.
331
+ - [ ] Drop any `_`-prefixed internal API usages; replace with public
332
+ equivalents.
333
+ - [ ] (Optional) Adopt `tableOptions(...)` for shared base config.
334
+ - [ ] (Optional) Adopt `createTableHook(...)` for app-wide table infrastructure.
335
+
336
+ ---
337
+
338
+ ## Failure modes
339
+
340
+ ### 1. (CRITICAL) Leaving `getCoreRowModel()` / `getSortedRowModel()` / etc. in v9 options
341
+
342
+ These options don't exist anymore. They become `_rowModels` entries with
343
+ factory functions. The TypeScript error is loud but agents sometimes silence
344
+ it with `as any` — don't.
345
+
346
+ ### 2. (CRITICAL) Reaching for `createAngularTable` from v8 muscle memory
347
+
348
+ Always `injectTable(() => ({...}))`. The injection-context requirement means
349
+ it must run from a class field, constructor, or
350
+ `runInInjectionContext(injector, () => injectTable(...))`.
351
+
352
+ ### 3. (CRITICAL) Registering a feature but forgetting its row model
353
+
354
+ ```ts
355
+ // ❌ filtering enabled, but no filtered row model — UI changes, rows don't filter
356
+ _features: tableFeatures({ columnFilteringFeature })
357
+ _rowModels: {
358
+ } // missing filteredRowModel
359
+
360
+ // ✅
361
+ _rowModels: {
362
+ filteredRowModel: createFilteredRowModel(filterFns)
363
+ }
364
+ ```
365
+
366
+ Same for sorting, pagination, expanding, grouping, faceting. Selection,
367
+ visibility, ordering, pinning, sizing, resizing do **not** need a row model.
368
+
369
+ ### 4. (HIGH) `getState()` → `table.store.state` text replacement loses reactivity
370
+
371
+ Bulk-replacing `table.getState().x` with `table.store.state.x` works for _current
372
+ value_ reads, but if you used a `computed`/`memo` around `getState()` for
373
+ reactivity, switch to `table.atoms.x.get()` — it's already signal-backed and
374
+ needs no wrapper.
375
+
376
+ ### 5. (HIGH) Stale `sortingFn` / `sortingFns` references in column defs
377
+
378
+ The rename is mechanical: `sortingFn` → `sortFn`, `sortingFns` → `sortFns`,
379
+ `getSortingFn` → `getSortFn`. Missed renames produce silent runtime fallbacks
380
+ to default sort.
381
+
382
+ ### 6. (HIGH) Column helper using v8 generic order
383
+
384
+ ```ts
385
+ // ❌ TS will complain — the first generic is TFeatures, not TData
386
+ const columnHelper = createColumnHelper<Person>()
387
+ ```
388
+
389
+ ### 7. (HIGH) Putting `_features` / `columns` / row-model factories inside the `injectTable` initializer
390
+
391
+ The v8 mental model was "build columns inside the hook". v9's
392
+ `injectTable` initializer re-runs on every signal read change — keep heavy
393
+ literals outside.
394
+
395
+ Lower-severity failure modes (MEDIUM/LOW: `stockFeatures` cleanup, `enablePinning`
396
+ removal, `columnSizingInfo` rename, single-handler porting, hand-rolled
397
+ `TFeatures` in render fns, `_`-prefix internal usage, reimplementing built-in
398
+ APIs, `ColumnMeta` augmentation drift, `flexRender` as a function, mixing v8/v9
399
+ atoms) → [`references/v8-to-v9-mapping.md`](references/v8-to-v9-mapping.md#lower-severity-failure-modes-mediumlow).
400
+
401
+ ---
402
+
403
+ ## References
404
+
405
+ - [Full v8 → v9 mapping tables (row models, features, types, renames, MEDIUM failure modes)](references/v8-to-v9-mapping.md)
406
+
407
+ ---
408
+
409
+ ## See also
410
+
411
+ - `tanstack-table/angular/getting-started` — what the v9 target shape looks like
412
+ - `tanstack-table/angular/table-state` — signal-backed atom model
413
+ - `tanstack-table/angular/angular-rendering-directives` — full rendering API
414
+ - `tanstack-table/angular/production-readiness` — once compiling, optimize the bundle
415
+ - `tanstack-table/core/migrate-v8-to-v9` — framework-agnostic core changes
@@ -0,0 +1,261 @@
1
+ # v8 → v9 Mapping Tables — Full Reference
2
+
3
+ Mechanical translation tables and detailed renames for the Angular Table v8 →
4
+ v9 migration. The SKILL.md keeps the primary patterns; this file is the
5
+ exhaustive lookup.
6
+
7
+ ---
8
+
9
+ ## Row-model migration table
10
+
11
+ | v8 option | v9 `_rowModels` key | v9 factory |
12
+ | -------------------------- | --------------------- | --------------------------------------- |
13
+ | `getCoreRowModel()` | (automatic) | — |
14
+ | `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel(filterFns)` |
15
+ | `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel(sortFns)` |
16
+ | `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` |
17
+ | `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` |
18
+ | `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel(aggregationFns)` |
19
+ | `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` |
20
+ | `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` |
21
+ | `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` |
22
+
23
+ ## Feature registration table
24
+
25
+ | v8 (implicit) | v9 feature import |
26
+ | --------------- | ------------------------- |
27
+ | filter columns | `columnFilteringFeature` |
28
+ | global filter | `globalFilteringFeature` |
29
+ | sort rows | `rowSortingFeature` |
30
+ | pagination | `rowPaginationFeature` |
31
+ | row selection | `rowSelectionFeature` |
32
+ | expanding rows | `rowExpandingFeature` |
33
+ | pin rows | `rowPinningFeature` |
34
+ | pin columns | `columnPinningFeature` |
35
+ | hide columns | `columnVisibilityFeature` |
36
+ | reorder columns | `columnOrderingFeature` |
37
+ | size columns | `columnSizingFeature` |
38
+ | resize columns | `columnResizingFeature` |
39
+ | group columns | `columnGroupingFeature` |
40
+ | facet columns | `columnFacetingFeature` |
41
+
42
+ > If you don't want to think about tree-shaking yet, you can pass
43
+ > `stockFeatures`:
44
+ >
45
+ > ```ts
46
+ > import { stockFeatures } from '@tanstack/angular-table'
47
+ > _features: stockFeatures
48
+ > ```
49
+ >
50
+ > This restores v8-like "everything bundled" behavior — but the v9 bundle
51
+ > wins are gone. Plan to migrate to a curated `tableFeatures({...})` as part of
52
+ > productionization.
53
+
54
+ ---
55
+
56
+ ## Type generics — every type takes `TFeatures` now
57
+
58
+ ```txt
59
+ v8 v9
60
+ --- ---
61
+ Column<TData> → Column<TFeatures, TData, TValue>
62
+ ColumnDef<TData> → ColumnDef<TFeatures, TData, TValue>
63
+ Table<TData> → Table<TFeatures, TData>
64
+ Row<TData> → Row<TFeatures, TData>
65
+ Cell<TData, TValue> → Cell<TFeatures, TData, TValue>
66
+ Header<TData, TValue> → Header<TFeatures, TData, TValue>
67
+ HeaderContext<TData, TValue> → HeaderContext<TFeatures, TData, TValue>
68
+ CellContext<TData, TValue> → CellContext<TFeatures, TData, TValue>
69
+ ```
70
+
71
+ Easiest fix: extract `typeof _features` once.
72
+
73
+ ```ts
74
+ const _features = tableFeatures({ rowSortingFeature, columnFilteringFeature })
75
+
76
+ type Features = typeof _features
77
+
78
+ const columns: Array<ColumnDef<Features, Person>> = [
79
+ /* … */
80
+ ]
81
+ ```
82
+
83
+ If you're on `stockFeatures`:
84
+
85
+ ```ts
86
+ import type { StockFeatures, ColumnDef } from '@tanstack/angular-table'
87
+ const columns: Array<ColumnDef<StockFeatures, Person>> = [
88
+ /* … */
89
+ ]
90
+ ```
91
+
92
+ `ColumnMeta` module augmentation also needs `TFeatures`:
93
+
94
+ ```ts
95
+ declare module '@tanstack/angular-table' {
96
+ interface ColumnMeta<
97
+ TFeatures extends TableFeatures,
98
+ TData extends RowData,
99
+ TValue,
100
+ > {
101
+ align?: 'left' | 'right'
102
+ }
103
+ }
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Naming renames — sorting
109
+
110
+ | v8 | v9 |
111
+ | ----------------------------------- | ------------------------ |
112
+ | `sortingFn` (column-def field) | `sortFn` |
113
+ | `column.getSortingFn()` | `column.getSortFn()` |
114
+ | `column.getAutoSortingFn()` | `column.getAutoSortFn()` |
115
+ | `SortingFn` type | `SortFn` |
116
+ | `SortingFns` interface | `SortFns` |
117
+ | `sortingFns` (built-in fn registry) | `sortFns` |
118
+
119
+ Find-and-replace, then update `createSortedRowModel(sortFns)`.
120
+
121
+ ---
122
+
123
+ ## Column sizing vs resizing — split
124
+
125
+ v8 combined sizing and resizing into one feature. v9 splits them so you can
126
+ ship only what you use.
127
+
128
+ | v8 | v9 |
129
+ | --------------------------------- | ---------------------------------------------------------- |
130
+ | `ColumnSizing` (single feature) | `columnSizingFeature` + `columnResizingFeature` (separate) |
131
+ | `columnSizingInfo` state | `columnResizing` state |
132
+ | `setColumnSizingInfo()` | `setColumnResizing()` |
133
+ | `onColumnSizingInfoChange` option | `onColumnResizingChange` option |
134
+
135
+ If you only render fixed-width columns and never drag-to-resize, import only
136
+ `columnSizingFeature`.
137
+
138
+ ---
139
+
140
+ ## Pinning option split
141
+
142
+ ```ts
143
+ // v8
144
+ enablePinning: true
145
+
146
+ // v9 — explicit
147
+ enableColumnPinning: true
148
+ enableRowPinning: true
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Row API: privates promoted to public
154
+
155
+ | v8 | v9 |
156
+ | ---------------------------------------- | -------------------------------------- |
157
+ | `row._getAllCellsByColumnId()` (private) | `row.getAllCellsByColumnId()` (public) |
158
+
159
+ All other `_`-prefixed internal APIs from v8 are removed in v9 — use the
160
+ public equivalents. If you were touching `_`-prefixed members, you were on
161
+ unsupported territory; v9 forces the fix.
162
+
163
+ ---
164
+
165
+ ## `tableOptions(...)` — new composition helper
166
+
167
+ v9 ships `tableOptions(...)` for type-safe partial option composition. Useful
168
+ during migration if you have shared base config:
169
+
170
+ ```ts
171
+ import { tableOptions, tableFeatures, rowSortingFeature } from '@tanstack/angular-table'
172
+
173
+ const baseOptions = tableOptions({
174
+ _features: tableFeatures({ rowSortingFeature }),
175
+ debugTable: isDevMode(),
176
+ })
177
+
178
+ readonly table = injectTable(() => ({
179
+ ...baseOptions,
180
+ _rowModels: { /* … */ },
181
+ columns: this.columns,
182
+ data: this.data(),
183
+ }))
184
+ ```
185
+
186
+ And for whole-app patterns, `createTableHook(...)` — see
187
+ `tanstack-table/angular/angular-rendering-directives`.
188
+
189
+ ---
190
+
191
+ ## `RowData` type tightened
192
+
193
+ `RowData` is now stricter (object-like; no primitives). If you had
194
+ `type Row = string`, that won't fly in v9 — wrap it in an object.
195
+
196
+ ---
197
+
198
+ ## Lower-severity failure modes (MEDIUM/LOW)
199
+
200
+ ### Skipping `stockFeatures` cleanup later
201
+
202
+ Using `stockFeatures` is a fine migration shortcut, but it forgoes the v9
203
+ bundle wins. Once everything compiles, swap `stockFeatures` for an explicit
204
+ `tableFeatures({...})` listing only the features you use.
205
+
206
+ ### Forgetting `enablePinning` is gone
207
+
208
+ `enablePinning: true` silently does nothing. Use `enableColumnPinning: true`
209
+ and/or `enableRowPinning: true`.
210
+
211
+ ### Forgetting `columnSizingInfo` is gone
212
+
213
+ Replace state name `columnSizingInfo` with `columnResizing`, setter
214
+ `setColumnSizingInfo` with `setColumnResizing`, handler
215
+ `onColumnSizingInfoChange` with `onColumnResizingChange`. And add
216
+ `columnResizingFeature` to `_features` if you actually need resizing.
217
+
218
+ ### Single global `onStateChange` ported as a giant per-slice fan-out
219
+
220
+ In v9 each slice has its own callback. If you previously used `onStateChange`
221
+ to multiplex changes, port to the specific `on[State]Change` callbacks you
222
+ actually care about — don't recreate a megaswitch.
223
+
224
+ ### Hand-rolling `TFeatures` in render-fn types
225
+
226
+ When a `cell` / `header` function signature requires `CellContext<TFeatures, TData, TValue>`,
227
+ let `createColumnHelper<typeof _features, TData>()` infer it for you. Spelling
228
+ features by hand in dozens of render-fn signatures is a sign you should be
229
+ using `createAppColumnHelper` from `createTableHook`.
230
+
231
+ ### Reaching for `_`-prefixed internals because the public method "doesn't exist"
232
+
233
+ The "missing" method usually means the feature isn't in `_features`. Add the
234
+ feature; don't peek at internals.
235
+
236
+ ### Reimplementing what the table API already does
237
+
238
+ Symptoms of v8 muscle memory carrying through:
239
+
240
+ - Manually sorting the data signal in `effect(...)` instead of
241
+ `table.setSorting(...)` and using `table.getRowModel().rows`.
242
+ - Manually paginating the rows array instead of using `paginatedRowModel`.
243
+ - Hand-rolling `getSelectedRowModel().flatRows` from a `rowSelection` signal.
244
+
245
+ If a v8 escape hatch was needed because of a bug or missing API, check the v9
246
+ public API first — many things were added in v9.
247
+
248
+ ### `ColumnMeta` augmentation without the `TFeatures` generic
249
+
250
+ The interface now takes `TFeatures` first. Old declarations get silently merged
251
+ but typed wrong.
252
+
253
+ ### Importing `flexRender` as a function
254
+
255
+ There's no `flexRender(fn, ctx)` in Angular. `FlexRender` is a directive tuple
256
+ constant. Imports + template directive form is the only shape.
257
+
258
+ ### Trying to share v9 atoms with v8 — incompatible
259
+
260
+ If you have a partial v8 codebase coexisting, **don't** try to bridge atoms
261
+ across the version boundary. Migrate per-component.