@tanstack/angular-table 9.0.0-beta.4 → 9.0.0-beta.42

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.
Files changed (39) hide show
  1. package/README.md +2 -0
  2. package/dist/README.md +2 -0
  3. package/dist/fesm2022/tanstack-angular-table-experimental-worker-plugin.mjs +6 -0
  4. package/dist/fesm2022/tanstack-angular-table-experimental-worker-plugin.mjs.map +1 -0
  5. package/dist/fesm2022/tanstack-angular-table.mjs +41 -21
  6. package/dist/fesm2022/tanstack-angular-table.mjs.map +1 -1
  7. package/dist/types/tanstack-angular-table-experimental-worker-plugin.d.ts +1 -0
  8. package/dist/types/tanstack-angular-table.d.ts +10 -19
  9. package/package.json +13 -8
  10. package/skills/create-table-hook/SKILL.md +148 -0
  11. package/skills/getting-started/SKILL.md +138 -0
  12. package/skills/migrate-v8-to-v9/SKILL.md +194 -0
  13. package/skills/table-state/SKILL.md +197 -0
  14. package/skills/with-tanstack-query/SKILL.md +142 -0
  15. package/skills/with-tanstack-virtual/SKILL.md +126 -0
  16. package/src/flex-render/flexRenderComponent.ts +2 -2
  17. package/src/flex-render/view.ts +1 -1
  18. package/src/helpers/cell.ts +6 -2
  19. package/src/helpers/createTableHook.ts +44 -26
  20. package/src/helpers/flexRenderCell.ts +14 -0
  21. package/src/helpers/header.ts +4 -2
  22. package/src/helpers/table.ts +4 -2
  23. package/src/injectTable.ts +7 -12
  24. package/skills/angular/angular-rendering-directives/SKILL.md +0 -415
  25. package/skills/angular/angular-rendering-directives/references/content-shapes.md +0 -142
  26. package/skills/angular/angular-rendering-directives/references/create-table-hook-registries.md +0 -89
  27. package/skills/angular/angular-rendering-directives/references/di-tokens.md +0 -171
  28. package/skills/angular/angular-rendering-directives/references/flex-render-component-options.md +0 -64
  29. package/skills/angular/client-to-server/SKILL.md +0 -467
  30. package/skills/angular/compose-with-tanstack-query/SKILL.md +0 -482
  31. package/skills/angular/compose-with-tanstack-store/SKILL.md +0 -397
  32. package/skills/angular/compose-with-tanstack-virtual/SKILL.md +0 -400
  33. package/skills/angular/getting-started/SKILL.md +0 -496
  34. package/skills/angular/getting-started/references/feature-row-model-mapping.md +0 -48
  35. package/skills/angular/migrate-v8-to-v9/SKILL.md +0 -419
  36. package/skills/angular/migrate-v8-to-v9/references/v8-to-v9-mapping.md +0 -261
  37. package/skills/angular/production-readiness/SKILL.md +0 -469
  38. package/skills/angular/table-state/SKILL.md +0 -429
  39. package/skills/angular/table-state/references/external-atoms-and-app-hook.md +0 -152
@@ -1,429 +0,0 @@
1
- ---
2
- name: angular/table-state
3
- description: >
4
- TanStack Table v9 state ownership in Angular: signal-backed atoms via `angularReactivity`,
5
- the `injectTable(() => ({...}))` lazy initializer pattern, reading `table.atoms.<slice>.get()`
6
- inside templates / `computed(...)` / `effect(...)`, `shallow` for object slices, controlled state
7
- with Angular signals + `state` + `on[State]Change`, and when to reach for external TanStack Store
8
- atoms instead. Required reading before any other Angular Table v9 skill.
9
- type: framework
10
- library: tanstack-table
11
- framework: angular
12
- library_version: '9.0.0-alpha.48'
13
- requires:
14
- - state-management
15
- - setup
16
- sources:
17
- - TanStack/table:docs/framework/angular/angular-table.md
18
- - TanStack/table:docs/framework/angular/guide/table-state.md
19
- - TanStack/table:docs/framework/angular/guide/migrating.md
20
- - TanStack/table:packages/angular-table/src/injectTable.ts
21
- - TanStack/table:packages/angular-table/src/reactivity.ts
22
- - TanStack/table:packages/angular-table/src/lazySignalInitializer.ts
23
- - TanStack/table:examples/angular/basic-inject-table/
24
- - TanStack/table:examples/angular/row-selection-signal/
25
- ---
26
-
27
- # Angular Table State (v9)
28
-
29
- > **TanStack Table is a state-management coordinator.** v9 rebuilt that coordinator
30
- > on top of TanStack Store (`alien-signals`). In Angular, the adapter bridges those
31
- > atoms to native Angular signals, so reading `table.atoms.<slice>.get()` from a
32
- > template, `computed(...)`, or `effect(...)` participates in Angular reactivity.
33
- > Prefer that direct atom read when you need a specific state slice.
34
- >
35
- > This is the prerequisite for every other Angular Table skill. Don't skip it.
36
-
37
- ---
38
-
39
- ## 1. Prerequisites — `features` and `rowModels` decide what state exists
40
-
41
- In v9, **a state slice only exists if its feature is registered in `features`**.
42
- This is the #1 v9-specific gotcha and the root cause of many "missing API"
43
- TypeScript errors.
44
-
45
- ```ts
46
- import {
47
- injectTable,
48
- tableFeatures,
49
- rowPaginationFeature,
50
- rowSortingFeature,
51
- createPaginatedRowModel,
52
- createSortedRowModel,
53
- sortFns,
54
- } from '@tanstack/angular-table'
55
-
56
- // Declare features OUTSIDE the initializer (see §2 below)
57
- const features = tableFeatures({
58
- rowPaginationFeature,
59
- rowSortingFeature,
60
- })
61
-
62
- readonly table = injectTable(() => ({
63
- features,
64
- rowModels: {
65
- paginatedRowModel: createPaginatedRowModel(),
66
- sortedRowModel: createSortedRowModel(sortFns),
67
- },
68
- columns,
69
- data: this.data(),
70
- }))
71
-
72
- this.table.atoms.pagination.get() // ✅
73
- this.table.atoms.sorting.get() // ✅
74
- // this.table.atoms.rowSelection // ❌ TS error — rowSelectionFeature not registered
75
- ```
76
-
77
- If you see `Property 'atoms.rowSelection' does not exist` or
78
- `table.toggleRowSelected is not a function`, **add the feature to `features`** —
79
- don't reach for `@ts-ignore`, don't reimplement the API, don't switch to
80
- `stockFeatures` until you understand which features you actually need.
81
-
82
- `tableFeatures({})` (empty) is valid — you get the core row model only.
83
-
84
- ---
85
-
86
- ## 2. The `injectTable` lazy-initializer model
87
-
88
- `injectTable` is the v9 entrypoint (replacing v8's `createAngularTable`). It must
89
- run inside an Angular injection context (a component constructor / class field).
90
-
91
- ```ts
92
- readonly table = injectTable(() => ({
93
- features,
94
- rowModels: {},
95
- columns,
96
- data: this.data(),
97
- }))
98
- ```
99
-
100
- ### The initializer is a `computed`-like function
101
-
102
- The initializer runs **whenever any Angular signal read inside it changes**. The
103
- adapter then calls `table.setOptions({ ...previous, ...newOptions })` to sync.
104
- That means:
105
-
106
- - **Reactive values that should re-sync the table** (`this.data()`, controlled
107
- state signals) go _inside_ the initializer.
108
- - **Stable references** (`columns`, `features`, `rowModels`, feature-fn maps)
109
- go _outside_ — or you'll recreate the column model on every data update.
110
-
111
- ```ts
112
- // ❌ WRONG — columns + features recreated on every data change
113
- readonly table = injectTable(() => ({
114
- features: tableFeatures({ rowSortingFeature }), // new reference each run
115
- rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, // ditto
116
- columns: [/* … */], // ditto
117
- data: this.data(),
118
- }))
119
-
120
- // ✅ Stable references outside, signal reads inside
121
- const features = tableFeatures({ rowSortingFeature })
122
- const columns: Array<ColumnDef<typeof features, Person>> = [/* … */]
123
-
124
- readonly table = injectTable(() => ({
125
- features,
126
- rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
127
- columns,
128
- data: this.data(), // ← only the signal read should be inside
129
- }))
130
- ```
131
-
132
- ### The returned table is signal-reactive too
133
-
134
- The `table` returned by `injectTable` exposes APIs that read signal-backed atoms
135
- internally, so calling `table.getRowModel()`, `table.getSelectedRowModel()`,
136
- `table.atoms.pagination.get()`, etc. inside templates / `computed` / `effect`
137
- _just works_ — no manual subscriptions.
138
-
139
- ---
140
-
141
- ## 3. The three state surfaces
142
-
143
- A table instance has three ways to look at its state:
144
-
145
- | Surface | Shape | Use when |
146
- | ------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------- |
147
- | `table.baseAtoms.<slice>` | writable TanStack Store atom (always exists for registered slices) | low-level direct write; rare |
148
- | `table.atoms.<slice>` | **readonly** derived atom per registered feature; backed by Angular `computed` | reading current value or driving reactivity |
149
- | `table.state` | flat proxy object over every registered slice | full-state JSON/debug output |
150
-
151
- All three are signal-backed in Angular. Reading any of them inside a template,
152
- `computed(...)`, or `effect(...)` registers an Angular dependency. For normal
153
- render code, prefer `table.atoms.<slice>.get()` so the read is explicit and
154
- limited to the slice the component needs. Use `table.state` when you actually
155
- need the flat full-state shape, such as `JSON.stringify(table.state, null, 2)`.
156
-
157
- ```ts
158
- // Read current value (anywhere)
159
- const pagination = this.table.atoms.pagination.get()
160
-
161
- // Same value through the flat proxy; use mainly for full-state debug output
162
- const pagination2 = this.table.state.pagination
163
-
164
- // Reactive derivation with custom equality
165
- import { computed } from '@angular/core'
166
- import { shallow } from '@tanstack/angular-table'
167
-
168
- readonly pageIndex = computed(
169
- () => this.table.atoms.pagination.get().pageIndex,
170
- )
171
-
172
- readonly pagination = computed(
173
- () => this.table.atoms.pagination.get(),
174
- { equal: shallow }, // structural equality — skip downstream work on no-op updates
175
- )
176
- ```
177
-
178
- ### When do I need `computed(...)`?
179
-
180
- You **don't need `computed`** just to make an atom reactive. The atom is already
181
- signal-backed. Use `computed(...)` only when you want:
182
-
183
- 1. **Derivation** — `computed(() => this.table.atoms.pagination.get().pageIndex)`
184
- 2. **Custom equality** — `{ equal: shallow }` on object/array slices, so
185
- downstream `effect`s skip no-op updates when the slice is recreated with the
186
- same values.
187
- 3. **Caching of an expensive transformation** that reads from multiple atoms.
188
-
189
- For plain reads in a template, `{{ table.atoms.pagination.get().pageIndex }}`
190
- is fine.
191
-
192
- ---
193
-
194
- ## 4. Setting state — use feature APIs, not direct writes
195
-
196
- TanStack Table exposes a method for nearly every state transition. **Use those
197
- methods.** Don't reimplement what's already in the public API — that's the #1
198
- tell of AI-generated table code.
199
-
200
- ```ts
201
- // ✅ Use the API
202
- this.table.setPageIndex(0)
203
- this.table.nextPage()
204
- this.table.setPageSize(25)
205
- this.table.setSorting([{ id: 'age', desc: true }])
206
- this.table.setColumnFilters([{ id: 'status', value: 'active' }])
207
- this.table.toggleAllRowsSelected(true)
208
- this.table.resetSorting()
209
- this.table.resetPagination()
210
- this.table.resetPagination(true) // reset to feature default, not initialState
211
- row.toggleSelected()
212
- column.toggleVisibility()
213
- column.toggleSorting()
214
-
215
- // ❌ Don't write to atoms directly unless you really have to
216
- this.table.baseAtoms.pagination.set((old) => ({ ...old, pageIndex: 0 }))
217
- ```
218
-
219
- Direct `baseAtoms` writes bypass `on[State]Change` handlers and won't update
220
- externally owned state — if you've controlled the slice with an Angular signal,
221
- you must update the signal, not the base atom.
222
-
223
- ---
224
-
225
- ## 5. Setting starting values — `initialState`
226
-
227
- `initialState` is the single right place to seed registered slices. It is also
228
- the value that reset APIs reset to.
229
-
230
- ```ts
231
- readonly table = injectTable(() => ({
232
- features,
233
- rowModels: { /* … */ },
234
- columns,
235
- data: this.data(),
236
- initialState: {
237
- sorting: [{ id: 'age', desc: true }],
238
- pagination: { pageIndex: 0, pageSize: 25 },
239
- },
240
- }))
241
-
242
- // Later
243
- this.table.resetSorting() // → initialState.sorting
244
- this.table.resetSorting(true) // → feature default ([])
245
- ```
246
-
247
- `initialState` only applies to slices whose feature is registered. Mutating
248
- `initialState` after construction does **not** re-seed state — use it for
249
- starting values only.
250
-
251
- ---
252
-
253
- ## 6. Controlled state — the recommended Angular pattern
254
-
255
- Most Angular Table apps that need cross-component access to a state slice use
256
- **Angular signals + `state` + `on[State]Change`**. This keeps ownership in
257
- Angular's signal model while `injectTable` keeps the table in sync.
258
-
259
- ```ts
260
- import { signal } from '@angular/core'
261
- import {
262
- injectTable,
263
- rowPaginationFeature,
264
- rowSortingFeature,
265
- tableFeatures,
266
- type PaginationState,
267
- type SortingState,
268
- } from '@tanstack/angular-table'
269
-
270
- const features = tableFeatures({ rowPaginationFeature, rowSortingFeature })
271
-
272
- export class Component {
273
- readonly data = signal<Array<Person>>([])
274
- readonly sorting = signal<SortingState>([])
275
- readonly pagination = signal<PaginationState>({ pageIndex: 0, pageSize: 10 })
276
-
277
- readonly table = injectTable(() => ({
278
- features,
279
- rowModels: {
280
- /* … */
281
- },
282
- columns,
283
- data: this.data(),
284
- state: {
285
- sorting: this.sorting(),
286
- pagination: this.pagination(),
287
- },
288
- onSortingChange: (updater) => {
289
- updater instanceof Function
290
- ? this.sorting.update(updater)
291
- : this.sorting.set(updater)
292
- },
293
- onPaginationChange: (updater) => {
294
- updater instanceof Function
295
- ? this.pagination.update(updater)
296
- : this.pagination.set(updater)
297
- },
298
- }))
299
- }
300
- ```
301
-
302
- ### `on[State]Change` rules
303
-
304
- - **Always pass an updater-or-value handler.** TanStack Table calls
305
- `on[State]Change(updaterOrValue)` where `updaterOrValue` is either a new value
306
- or `(old) => new` — check with `instanceof Function` / `typeof === 'function'`.
307
- - **Pair `on[State]Change` with `state.<slice>`.** Providing
308
- `onPaginationChange` without `state.pagination` will result in your callback
309
- firing but the table reading its own internal atom — confusing.
310
- - **The v8 `onStateChange` (single global handler) is gone in v9.** Slices are
311
- controlled individually.
312
-
313
- ### Don't double-own a slice
314
-
315
- For any given slice, exactly one of these should be the source of truth:
316
-
317
- - `initialState.<slice>` (uncontrolled, internal)
318
- - `state.<slice>` + `on[State]Change` (controlled by Angular signal)
319
- - `atoms.<slice>` (controlled by external TanStack Store atom — see §7)
320
-
321
- If you supply both `state.x` and `atoms.x`, the external atom wins silently. If
322
- you supply both `initialState.x` and `state.x`, `state.x` wins. Pick one.
323
-
324
- ---
325
-
326
- ## 7. Beyond signals: external atoms, state types, app-wide hooks
327
-
328
- For most Angular apps, **signals + `state` + `on[State]Change` (§6) is the
329
- right ownership model.** When you need more, see
330
- [`references/external-atoms-and-app-hook.md`](references/external-atoms-and-app-hook.md):
331
-
332
- - **External TanStack Store atoms** — `atoms: { pagination: paginationAtom }`
333
- for slices owned by `@tanstack/store` / `@tanstack/angular-store`, when
334
- multiple non-table parts of the app share the slice.
335
- - **State type imports** — `PaginationState`, `SortingState`,
336
- `RowSelectionState`, `TableState<typeof features>`, etc.
337
- - **`createTableHook(...)`** — app-wide `injectAppTable` /
338
- `createAppColumnHelper` that pre-bind `features` and `rowModels`. Also
339
- exposes `tableComponents` / `cellComponents` / `headerComponents` registries
340
- (covered in `angular-rendering-directives`).
341
-
342
- ---
343
-
344
- ## Failure modes
345
-
346
- ### 1. (CRITICAL) Hallucinating v8 `createAngularTable` / pre-v9 APIs
347
-
348
- ```ts
349
- // ❌ v8 — does not exist in v9
350
- import { createAngularTable, getCoreRowModel } from '@tanstack/angular-table'
351
- const table = createAngularTable(() => ({
352
- columns,
353
- data: data(),
354
- getCoreRowModel: getCoreRowModel(),
355
- }))
356
-
357
- // ✅ v9
358
- import { injectTable, tableFeatures } from '@tanstack/angular-table'
359
- const features = tableFeatures({})
360
- const table = injectTable(() => ({
361
- features,
362
- rowModels: {},
363
- columns,
364
- data: data(),
365
- }))
366
- ```
367
-
368
- Also retired: `getFilteredRowModel`, `getSortedRowModel`, `getPaginationRowModel`
369
- as top-level options → migrated to `rowModels: { filteredRowModel: ..., sortedRowModel: ..., paginatedRowModel: ... }`
370
- with explicit `*Fns` parameters.
371
-
372
- ### 2. (CRITICAL) Missing API because feature not in `features`
373
-
374
- `table.atoms.rowSelection`, `table.toggleAllRowsSelected`,
375
- `row.getCanSelect`, `column.getCanSort` etc. are **only** present when the
376
- matching feature is in `features`. The fix is to add the feature, not to
377
- patch around it.
378
-
379
- ### 3. (CRITICAL) Reimplementing built-in state transitions
380
-
381
- ```ts
382
- // ❌ DON'T
383
- this.pagination.update((p) => ({ ...p, pageIndex: p.pageIndex + 1 }))
384
-
385
- // ✅
386
- this.table.nextPage()
387
- ```
388
-
389
- Same for `setPageIndex`, `setPageSize`, `setSorting`, `toggleSorting`,
390
- `setColumnFilters`, `setGlobalFilter`, `toggleSelected`, `toggleAllRowsSelected`,
391
- `setColumnVisibility`, `setColumnOrder`, `setExpanded`, `toggleExpanded`,
392
- `resetSorting`, `resetPagination`, `resetRowSelection`, etc.
393
-
394
- ### 4. (HIGH) Expensive values declared **inside** the `injectTable` initializer
395
-
396
- Because the initializer re-runs when any reactive read inside it changes,
397
- declaring `columns`, `features`, `rowModels`, or feature-fn maps inside the
398
- function causes them to be recreated and re-applied on every data update.
399
- Move them outside the class or to stable class fields.
400
-
401
- ### 5. (HIGH) Forgetting that the initializer re-runs
402
-
403
- If you `console.log` inside the `injectTable` initializer, you'll see it fire
404
- multiple times during the component lifetime — that's correct. The adapter
405
- handles the diff and calls `table.setOptions`. Don't kick off side-effects from
406
- inside the initializer; put them in an `effect(...)` reading the relevant
407
- atoms.
408
-
409
- Lower-severity failure modes (MEDIUM/LOW: `state.x` vs `atoms.x` conflict,
410
- updater-fn handling in `on[State]Change`, in-place mutation of state values,
411
- premature `computed` wrapping) →
412
- [`references/external-atoms-and-app-hook.md`](references/external-atoms-and-app-hook.md#lower-severity-failure-modes-mediumlow).
413
-
414
- ---
415
-
416
- ## References
417
-
418
- - [External TanStack Store atoms, state types, `createTableHook` setup, and MEDIUM failure modes](references/external-atoms-and-app-hook.md)
419
-
420
- ---
421
-
422
- ## See also
423
-
424
- - `tanstack-table/angular/getting-started` — end-to-end first table
425
- - `tanstack-table/angular/angular-rendering-directives` — `*flexRender*`, DI tokens, `flexRenderComponent`
426
- - `tanstack-table/angular/migrate-v8-to-v9` — v8 → v9 mechanical mapping
427
- - `tanstack-table/angular/compose-with-tanstack-store` — external atoms in depth
428
- - `tanstack-table/angular/client-to-server` — controlled state for server-driven tables
429
- - `tanstack-table/core/state-management` — framework-agnostic atom model
@@ -1,152 +0,0 @@
1
- # External Atoms & `createTableHook` — Full Reference
2
-
3
- Extended ownership options beyond signals + `state` + `on[State]Change`.
4
-
5
- ---
6
-
7
- ## External TanStack Store atoms — alternative ownership
8
-
9
- The core `atoms` table option is re-exported from Angular Table. Use it when:
10
-
11
- - You already have a `Store` / `Atom` from `@tanstack/store` or
12
- `@tanstack/angular-store` that should drive a table slice.
13
- - Multiple non-table parts of the app need to read/write the slice through the
14
- same atom (e.g. URL sync, persistence, multi-table coordination).
15
-
16
- **For most Angular apps, prefer signals + `state` first.** Atoms are the
17
- cross-app sharing alternative, not the default. See
18
- `compose-with-tanstack-store` for the full pattern.
19
-
20
- ```ts
21
- import { Store } from '@tanstack/store'
22
-
23
- const paginationAtom = new Store<PaginationState>({ pageIndex: 0, pageSize: 10 })
24
-
25
- readonly table = injectTable(() => ({
26
- features,
27
- rowModels: { /* … */ },
28
- columns,
29
- data: this.data(),
30
- atoms: {
31
- pagination: paginationAtom, // external atom owns the slice
32
- },
33
- }))
34
- ```
35
-
36
- When `atoms.pagination` is registered, `table.atoms.pagination.get()` reads from
37
- the external atom (not the internal base atom). `table.setPagination(...)`
38
- writes through the external atom's setter.
39
-
40
- ---
41
-
42
- ## State types
43
-
44
- Import slice types directly from `@tanstack/angular-table`:
45
-
46
- ```ts
47
- import type {
48
- PaginationState,
49
- SortingState,
50
- RowSelectionState,
51
- ColumnFiltersState,
52
- GlobalFilterTableState,
53
- ColumnVisibilityState,
54
- ColumnOrderState,
55
- ColumnPinningState,
56
- ColumnSizingState,
57
- ColumnResizingState,
58
- ExpandedState,
59
- GroupingState,
60
- TableState,
61
- } from '@tanstack/angular-table'
62
-
63
- // TableState<typeof features> is inferred from registered features
64
- type MyTableState = TableState<typeof features>
65
- ```
66
-
67
- ---
68
-
69
- ## `createTableHook` — app-wide table infrastructure
70
-
71
- When multiple tables in an app share the same `features`, `rowModels`, and
72
- component conventions, factor them into a `createTableHook(...)` call once and
73
- import the resulting `injectAppTable` / `createAppColumnHelper`.
74
-
75
- ```ts
76
- // app-table.ts
77
- import {
78
- createTableHook,
79
- tableFeatures,
80
- rowSortingFeature,
81
- rowPaginationFeature,
82
- createSortedRowModel,
83
- createPaginatedRowModel,
84
- sortFns,
85
- } from '@tanstack/angular-table'
86
-
87
- export const {
88
- injectAppTable,
89
- createAppColumnHelper,
90
- injectTableContext,
91
- injectTableCellContext,
92
- injectTableHeaderContext,
93
- } = createTableHook({
94
- features: tableFeatures({ rowSortingFeature, rowPaginationFeature }),
95
- rowModels: {
96
- sortedRowModel: createSortedRowModel(sortFns),
97
- paginatedRowModel: createPaginatedRowModel(),
98
- },
99
- getRowId: (row) => row.id,
100
- })
101
- ```
102
-
103
- Then in components:
104
-
105
- ```ts
106
- import { injectAppTable, createAppColumnHelper } from './app-table'
107
-
108
- const columnHelper = createAppColumnHelper<Person>() // only TData generic needed
109
-
110
- readonly table = injectAppTable(() => ({
111
- columns,
112
- data: this.data(),
113
- })) // features & rowModels inherited
114
- ```
115
-
116
- `createTableHook` also lets you register `tableComponents` / `cellComponents` /
117
- `headerComponents` registries — see `angular-rendering-directives` for that.
118
-
119
- ---
120
-
121
- ## Lower-severity failure modes (MEDIUM/LOW)
122
-
123
- ### Using `state.x` and `atoms.x` together for the same slice
124
-
125
- External atom silently wins. If you intended Angular-signal ownership, remove
126
- `atoms.x` (or vice versa). Pick exactly one source of truth per slice.
127
-
128
- ### Forgetting the updater function form in `on[State]Change`
129
-
130
- ```ts
131
- // ❌ Crashes when TanStack Table passes a function
132
- onSortingChange: (value) => this.sorting.set(value)
133
-
134
- // ✅
135
- onSortingChange: (updater) => {
136
- updater instanceof Function
137
- ? this.sorting.update(updater)
138
- : this.sorting.set(updater)
139
- }
140
- ```
141
-
142
- ### Mutating `state` slice values in place
143
-
144
- `state: { sorting: this.sorting() }` snapshots the current value. Mutating the
145
- underlying signal value with `.push()` won't trigger re-evaluation. Always
146
- produce a new reference: `this.sorting.update(s => [...s, newSort])`.
147
-
148
- ### Premature `computed` selector wrapping for a single read
149
-
150
- For a single atom read in a template (`{{ table.atoms.pagination.get().pageIndex }}`),
151
- a wrapper `computed` adds no value. Reach for `computed` only for derivation,
152
- shared selectors, or `{ equal: shallow }`.