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