@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.
- package/README.md +2 -0
- package/dist/README.md +2 -0
- package/dist/fesm2022/tanstack-angular-table-experimental-worker-plugin.mjs +6 -0
- package/dist/fesm2022/tanstack-angular-table-experimental-worker-plugin.mjs.map +1 -0
- package/dist/fesm2022/tanstack-angular-table.mjs +41 -21
- package/dist/fesm2022/tanstack-angular-table.mjs.map +1 -1
- package/dist/types/tanstack-angular-table-experimental-worker-plugin.d.ts +1 -0
- package/dist/types/tanstack-angular-table.d.ts +10 -19
- package/package.json +13 -8
- package/skills/create-table-hook/SKILL.md +148 -0
- package/skills/getting-started/SKILL.md +138 -0
- package/skills/migrate-v8-to-v9/SKILL.md +194 -0
- package/skills/table-state/SKILL.md +197 -0
- package/skills/with-tanstack-query/SKILL.md +142 -0
- package/skills/with-tanstack-virtual/SKILL.md +126 -0
- package/src/flex-render/flexRenderComponent.ts +2 -2
- package/src/flex-render/view.ts +1 -1
- package/src/helpers/cell.ts +6 -2
- package/src/helpers/createTableHook.ts +44 -26
- package/src/helpers/flexRenderCell.ts +14 -0
- package/src/helpers/header.ts +4 -2
- package/src/helpers/table.ts +4 -2
- package/src/injectTable.ts +7 -12
- package/skills/angular/angular-rendering-directives/SKILL.md +0 -415
- package/skills/angular/angular-rendering-directives/references/content-shapes.md +0 -142
- package/skills/angular/angular-rendering-directives/references/create-table-hook-registries.md +0 -89
- package/skills/angular/angular-rendering-directives/references/di-tokens.md +0 -171
- package/skills/angular/angular-rendering-directives/references/flex-render-component-options.md +0 -64
- package/skills/angular/client-to-server/SKILL.md +0 -467
- package/skills/angular/compose-with-tanstack-query/SKILL.md +0 -482
- package/skills/angular/compose-with-tanstack-store/SKILL.md +0 -397
- package/skills/angular/compose-with-tanstack-virtual/SKILL.md +0 -400
- package/skills/angular/getting-started/SKILL.md +0 -496
- package/skills/angular/getting-started/references/feature-row-model-mapping.md +0 -48
- package/skills/angular/migrate-v8-to-v9/SKILL.md +0 -419
- package/skills/angular/migrate-v8-to-v9/references/v8-to-v9-mapping.md +0 -261
- package/skills/angular/production-readiness/SKILL.md +0 -469
- package/skills/angular/table-state/SKILL.md +0 -429
- package/skills/angular/table-state/references/external-atoms-and-app-hook.md +0 -152
|
@@ -1,469 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: angular/production-readiness
|
|
3
|
-
description: >
|
|
4
|
-
Ship-ready optimizations for Angular Table v9: register only the `features` you actually use
|
|
5
|
-
(tree-shake the bundle); keep `columns` / `features` / `rowModels` / feature-fn maps as
|
|
6
|
-
stable references OUTSIDE the `injectTable` initializer; pass only the `*Fns` your data needs
|
|
7
|
-
to `createSortedRowModel` / `createFilteredRowModel` / `createGroupedRowModel`; use
|
|
8
|
-
`ChangeDetectionStrategy.OnPush`; lean on signal-backed atoms (`table.atoms.<slice>.get()`)
|
|
9
|
-
instead of broad `table.state` reads where granularity matters; use `{ equal: shallow }`
|
|
10
|
-
on object/array `computed` selectors; set `getRowId` for stable identity; track by `id` in
|
|
11
|
-
every `@for`; defer cell components with `flexRenderComponent` only when you need its options;
|
|
12
|
-
scope DI tokens via `[tanStackTable*]` directives to kill prop drilling.
|
|
13
|
-
type: lifecycle
|
|
14
|
-
library: tanstack-table
|
|
15
|
-
framework: angular
|
|
16
|
-
library_version: '9.0.0-alpha.48'
|
|
17
|
-
requires:
|
|
18
|
-
- angular/table-state
|
|
19
|
-
- angular/getting-started
|
|
20
|
-
- angular/angular-rendering-directives
|
|
21
|
-
sources:
|
|
22
|
-
- TanStack/table:docs/framework/angular/angular-table.md
|
|
23
|
-
- TanStack/table:docs/framework/angular/guide/table-state.md
|
|
24
|
-
- TanStack/table:docs/framework/angular/guide/migrating.md
|
|
25
|
-
- TanStack/table:packages/angular-table/src/injectTable.ts
|
|
26
|
-
- TanStack/table:packages/angular-table/src/reactivity.ts
|
|
27
|
-
- TanStack/table:examples/angular/composable-tables/
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
# Production Readiness (Angular Table v9)
|
|
31
|
-
|
|
32
|
-
> Once your table compiles and renders, this is the cost reduction pass.
|
|
33
|
-
> Angular's signal-backed adapter makes most of v9's perf "free" if you don't
|
|
34
|
-
> fight it — the work is mostly about _what you don't do_: not recreating
|
|
35
|
-
> objects, not pulling in features you don't use, not over-wrapping with
|
|
36
|
-
> selectors.
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## 1. Bundle: register only the features you use
|
|
41
|
-
|
|
42
|
-
The single biggest v9 win is feature tree-shaking. Every feature you put in
|
|
43
|
-
`tableFeatures({...})` pulls in its code; everything you leave out is dropped
|
|
44
|
-
by the bundler.
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
// ❌ Pulls in EVERY feature, even unused ones
|
|
48
|
-
const features = stockFeatures
|
|
49
|
-
|
|
50
|
-
// ✅ Only what this table actually uses
|
|
51
|
-
const features = tableFeatures({
|
|
52
|
-
rowSortingFeature,
|
|
53
|
-
rowPaginationFeature,
|
|
54
|
-
columnFilteringFeature,
|
|
55
|
-
})
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Use `stockFeatures` to bootstrap during a v8 → v9 migration, then **come back
|
|
59
|
-
and curate**. The bundle wins only land once you do.
|
|
60
|
-
|
|
61
|
-
The same applies to feature-fn registries — pass only the `*Fns` your data
|
|
62
|
-
needs:
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
import {
|
|
66
|
-
createSortedRowModel,
|
|
67
|
-
createFilteredRowModel,
|
|
68
|
-
sortFns,
|
|
69
|
-
filterFns,
|
|
70
|
-
} from '@tanstack/angular-table'
|
|
71
|
-
|
|
72
|
-
// ❌ pulls in every built-in sort + filter fn
|
|
73
|
-
rowModels: {
|
|
74
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
75
|
-
filteredRowModel: createFilteredRowModel(filterFns),
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// ✅ only what you use
|
|
79
|
-
rowModels: {
|
|
80
|
-
sortedRowModel: createSortedRowModel({ basic: sortFns.basic, datetime: sortFns.datetime }),
|
|
81
|
-
filteredRowModel: createFilteredRowModel({ includesString: filterFns.includesString }),
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Same logic for `aggregationFns` if you use grouping.
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## 2. Stable references — keep them OUTSIDE the initializer
|
|
90
|
-
|
|
91
|
-
`injectTable(() => ({...}))` **re-runs the initializer every time a signal read
|
|
92
|
-
inside it changes** and then calls `table.setOptions({ ...prev, ...new })`.
|
|
93
|
-
Anything you create inside the initializer is recreated on every signal
|
|
94
|
-
change.
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
// ❌ columns / features / rowModels / feature fns recreated on every data() change
|
|
98
|
-
@Component({...})
|
|
99
|
-
export class App {
|
|
100
|
-
readonly table = injectTable(() => ({
|
|
101
|
-
features: tableFeatures({ rowSortingFeature }), // ← new ref each run
|
|
102
|
-
rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, // ← new ref each run
|
|
103
|
-
columns: [/* … */], // ← new ref each run
|
|
104
|
-
data: this.data(),
|
|
105
|
-
}))
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ✅ stable references outside; only reactive reads inside
|
|
109
|
-
const features = tableFeatures({ rowSortingFeature })
|
|
110
|
-
const rowModels = { sortedRowModel: createSortedRowModel(sortFns) }
|
|
111
|
-
const columns: Array<ColumnDef<typeof features, Person>> = [/* … */]
|
|
112
|
-
|
|
113
|
-
@Component({...})
|
|
114
|
-
export class App {
|
|
115
|
-
readonly table = injectTable(() => ({
|
|
116
|
-
features,
|
|
117
|
-
rowModels,
|
|
118
|
-
columns,
|
|
119
|
-
data: this.data(),
|
|
120
|
-
}))
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Same rule for the controlled-state pattern — keep `state: { pagination: this.pagination() }`
|
|
125
|
-
inside the initializer, but keep the signal definitions on the class.
|
|
126
|
-
|
|
127
|
-
For shared infrastructure across multiple tables, `createTableHook(...)` lets
|
|
128
|
-
you define `features` / `rowModels` / default options once at module scope.
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## 3. `ChangeDetectionStrategy.OnPush` everywhere
|
|
133
|
-
|
|
134
|
-
Every component that hosts or renders a TanStack Table should be:
|
|
135
|
-
|
|
136
|
-
```ts
|
|
137
|
-
@Component({
|
|
138
|
-
// ...
|
|
139
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
140
|
-
})
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
With signal-backed atoms, OnPush is sufficient — atom reads in the template
|
|
144
|
-
are tracked through `computed`, so Angular schedules a check when the signal
|
|
145
|
-
changes. Default change detection causes redundant work on every event.
|
|
146
|
-
|
|
147
|
-
All `examples/angular/*` use `OnPush`. Match that.
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## 4. Read narrowly — `table.atoms.<slice>.get()` over `table.state`
|
|
152
|
-
|
|
153
|
-
Both surfaces are signal-backed. The difference is _which signal_ gets read.
|
|
154
|
-
|
|
155
|
-
```ts
|
|
156
|
-
// Wider — reads through the flat state proxy
|
|
157
|
-
const pageIndex = computed(() => this.table.state.pagination.pageIndex)
|
|
158
|
-
|
|
159
|
-
// Narrower — depends only on the pagination atom
|
|
160
|
-
const pageIndex = computed(() => this.table.atoms.pagination.get().pageIndex)
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
For most apps the difference is negligible. For render code, high-frequency
|
|
164
|
-
atoms, or deeply-derived components, prefer per-atom reads. Keep `table.state`
|
|
165
|
-
for places that genuinely want the full-state shape, such as debug JSON.
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
## 5. `{ equal: shallow }` on object/array `computed`
|
|
170
|
-
|
|
171
|
-
When you derive an object or array slice, downstream `effect`s and `computed`s
|
|
172
|
-
re-run whenever the reference changes — even if the structural contents
|
|
173
|
-
didn't. Use `shallow` from `@tanstack/angular-table` to short-circuit:
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
import { computed } from '@angular/core'
|
|
177
|
-
import { shallow } from '@tanstack/angular-table'
|
|
178
|
-
|
|
179
|
-
readonly pagination = computed(
|
|
180
|
-
() => this.table.atoms.pagination.get(),
|
|
181
|
-
{ equal: shallow },
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
readonly visibleColumns = computed(
|
|
185
|
-
() => this.table.atoms.columnVisibility.get(),
|
|
186
|
-
{ equal: shallow },
|
|
187
|
-
)
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
This is **not** about reactivity — atoms are reactive already. This is about
|
|
191
|
-
skipping no-op downstream recomputations when the slice rebuilds with the same
|
|
192
|
-
values.
|
|
193
|
-
|
|
194
|
-
**Don't reach for it on every read.** Reserve it for derived selectors whose
|
|
195
|
-
downstream is expensive (effects that hit the server, big template
|
|
196
|
-
re-renders).
|
|
197
|
-
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
## 6. `track row.id` and `getRowId`
|
|
201
|
-
|
|
202
|
-
Always provide a stable identity:
|
|
203
|
-
|
|
204
|
-
```ts
|
|
205
|
-
readonly table = injectTable(() => ({
|
|
206
|
-
// ...
|
|
207
|
-
getRowId: (row) => row.id, // stable primary key
|
|
208
|
-
}))
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
Then in every `@for`:
|
|
212
|
-
|
|
213
|
-
```html
|
|
214
|
-
@for (row of table.getRowModel().rows; track row.id) { ... } @for (cell of
|
|
215
|
-
row.getVisibleCells(); track cell.id) { ... } @for (header of
|
|
216
|
-
headerGroup.headers; track header.id) { ... }
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
Without `getRowId`, IDs default to row index — re-rendering the entire row list
|
|
220
|
-
on a sort flip, refetch, or pagination move because Angular thinks every row
|
|
221
|
-
is new.
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## 7. Render-cost rules for cells
|
|
226
|
-
|
|
227
|
-
Cell render fns run for every visible cell on every re-render. Cheap is the
|
|
228
|
-
goal.
|
|
229
|
-
|
|
230
|
-
- **Return a primitive when you can.** `cell: (info) => info.getValue()` is
|
|
231
|
-
fastest.
|
|
232
|
-
- **Return a component class (not a wrapper) when only inputs need wiring.**
|
|
233
|
-
The renderer's `KeyValueDiffers` skips `setInput` for unchanged values.
|
|
234
|
-
- **Reach for `flexRenderComponent(...)` only for explicit options** —
|
|
235
|
-
custom inputs not derived from context, output callbacks, an injector,
|
|
236
|
-
Angular v20+ `bindings` / `directives`.
|
|
237
|
-
- **Don't put expensive `inject()` calls in render fns.** They run inside
|
|
238
|
-
`runInInjectionContext` every render. Inject at the component level and
|
|
239
|
-
close over the value.
|
|
240
|
-
- **Don't allocate inside render fns when you can avoid it.** Closures, new
|
|
241
|
-
array literals, etc.
|
|
242
|
-
|
|
243
|
-
### Stable input references
|
|
244
|
-
|
|
245
|
-
For object inputs (like `data` arrays you pass into a sub-component), keep the
|
|
246
|
-
reference stable across renders. `KeyValueDiffers` is reference-based for
|
|
247
|
-
Angular's default input equality, so a `{ ...obj }` literal on every render
|
|
248
|
-
defeats it.
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
## 8. Kill prop drilling with DI tokens
|
|
253
|
-
|
|
254
|
-
Passing `cell` / `header` / `table` through 2+ component layers is both
|
|
255
|
-
ergonomic noise and a perf hazard (each input has its own diffing cost).
|
|
256
|
-
Replace with the host directives + inject helpers:
|
|
257
|
-
|
|
258
|
-
```html
|
|
259
|
-
<td [tanStackTableCell]="cell">
|
|
260
|
-
<ng-container *flexRenderCell="cell; let value">{{ value }}</ng-container>
|
|
261
|
-
<app-cell-actions />
|
|
262
|
-
<!-- no `cell` input needed -->
|
|
263
|
-
</td>
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
```ts
|
|
267
|
-
export class CellActionsComponent {
|
|
268
|
-
readonly cell = injectTableCellContext()
|
|
269
|
-
// cell() is a Signal<Cell<...>>; reads are reactive
|
|
270
|
-
}
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
Inside `*flexRender*` components, the tokens are auto-provided (no host
|
|
274
|
-
directive needed) — see `tanstack-table/angular/angular-rendering-directives`
|
|
275
|
-
§7.
|
|
276
|
-
|
|
277
|
-
---
|
|
278
|
-
|
|
279
|
-
## 9. Large data — let virtualization do the work
|
|
280
|
-
|
|
281
|
-
A 10k-row table is fine in v9 in terms of state, but rendering 10k rows is
|
|
282
|
-
slow. **Don't render what's off-screen.** Pair with `@tanstack/angular-virtual`
|
|
283
|
-
— see `tanstack-table/angular/compose-with-tanstack-virtual`.
|
|
284
|
-
|
|
285
|
-
Also consider:
|
|
286
|
-
|
|
287
|
-
- Server-side pagination if data is huge — see
|
|
288
|
-
`tanstack-table/angular/client-to-server`.
|
|
289
|
-
- `defaultColumn: { size, minSize, maxSize }` to set sane sizing defaults if
|
|
290
|
-
you've registered `columnSizingFeature`.
|
|
291
|
-
|
|
292
|
-
---
|
|
293
|
-
|
|
294
|
-
## 10. Avoid `effect(...)` for cross-slice sync — write directly
|
|
295
|
-
|
|
296
|
-
```ts
|
|
297
|
-
// ❌ Effect chain — runs after CD, can layer-cake
|
|
298
|
-
effect(() => {
|
|
299
|
-
const filter = this.globalFilter()
|
|
300
|
-
this.pagination.update((p) => ({ ...p, pageIndex: 0 }))
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
// ✅ Reset inline in the on*Change handler
|
|
304
|
-
onGlobalFilterChange: (u) => {
|
|
305
|
-
typeof u === 'function'
|
|
306
|
-
? this.globalFilter.update(u)
|
|
307
|
-
: this.globalFilter.set(u)
|
|
308
|
-
this.pagination.update((p) => ({ ...p, pageIndex: 0 }))
|
|
309
|
-
}
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
The on\*Change handler runs synchronously at the source of truth; an `effect`
|
|
313
|
-
runs after Angular's CD pass, which can lead to double renders.
|
|
314
|
-
|
|
315
|
-
---
|
|
316
|
-
|
|
317
|
-
## 11. Avoid double-controlling a slice
|
|
318
|
-
|
|
319
|
-
Don't supply both `state.x` and `atoms.x` for the same slice — atom wins
|
|
320
|
-
silently, the Angular signal becomes a write-only sink, and you've doubled
|
|
321
|
-
the wiring cost. Pick exactly one source of truth per slice (see
|
|
322
|
-
`tanstack-table/angular/table-state` §6–§7).
|
|
323
|
-
|
|
324
|
-
---
|
|
325
|
-
|
|
326
|
-
## 12. Build hygiene
|
|
327
|
-
|
|
328
|
-
- **`bundle-stats` / `source-map-explorer`**: after curating `features`,
|
|
329
|
-
verify your final bundle doesn't include retired features. If you see
|
|
330
|
-
`rowGroupingFeature` in the bundle but never imported it, something is
|
|
331
|
-
pulling in `stockFeatures` indirectly.
|
|
332
|
-
- **`debugTable: isDevMode()`** — only in dev. Don't leave `debugTable: true`
|
|
333
|
-
in production.
|
|
334
|
-
|
|
335
|
-
---
|
|
336
|
-
|
|
337
|
-
## 13. Quick wins checklist
|
|
338
|
-
|
|
339
|
-
- [ ] `features` listed explicitly (no `stockFeatures` in production).
|
|
340
|
-
- [ ] `*Fns` registries passed only what you use to
|
|
341
|
-
`createSortedRowModel` / `createFilteredRowModel` /
|
|
342
|
-
`createGroupedRowModel`.
|
|
343
|
-
- [ ] `columns`, `features`, `rowModels`, feature fns are at module scope
|
|
344
|
-
or stable class fields — never inside the `injectTable` initializer.
|
|
345
|
-
- [ ] Component is `ChangeDetectionStrategy.OnPush`.
|
|
346
|
-
- [ ] `getRowId` set when rows have a stable primary key.
|
|
347
|
-
- [ ] All `@for` blocks track by `id`.
|
|
348
|
-
- [ ] Cell render fns return primitives or component classes when possible;
|
|
349
|
-
`flexRenderComponent(...)` reserved for explicit-option cases.
|
|
350
|
-
- [ ] Reading state inside `effect`s / heavy `computed`s uses
|
|
351
|
-
`table.atoms.<slice>.get()` (not the flat state proxy).
|
|
352
|
-
- [ ] Object/array `computed` selectors that feed expensive downstream use
|
|
353
|
-
`{ equal: shallow }`.
|
|
354
|
-
- [ ] No `cell` / `header` / `table` inputs drilled through multiple
|
|
355
|
-
components — replaced with `injectTableCellContext()` / etc.
|
|
356
|
-
- [ ] `debugTable` only in `isDevMode()`.
|
|
357
|
-
- [ ] Tables larger than a few hundred visible rows use virtualization.
|
|
358
|
-
|
|
359
|
-
---
|
|
360
|
-
|
|
361
|
-
## Failure modes
|
|
362
|
-
|
|
363
|
-
### 1. (CRITICAL) Shipping `stockFeatures` to production
|
|
364
|
-
|
|
365
|
-
`stockFeatures` defeats the v9 bundle wins. The migrating skill explicitly
|
|
366
|
-
calls this out — `stockFeatures` is a v8 → v9 bootstrap, not a production
|
|
367
|
-
end-state.
|
|
368
|
-
|
|
369
|
-
### 2. (CRITICAL) Recreating `columns` / `features` / `rowModels` inside the
|
|
370
|
-
|
|
371
|
-
`injectTable` initializer
|
|
372
|
-
|
|
373
|
-
The initializer re-runs on every signal read change. New `columns` reference
|
|
374
|
-
triggers full column-model rebuilds — for big tables this is visibly slow.
|
|
375
|
-
Module-scope it.
|
|
376
|
-
|
|
377
|
-
### 3. (CRITICAL) Reimplementing what the table already does
|
|
378
|
-
|
|
379
|
-
Symptoms:
|
|
380
|
-
|
|
381
|
-
- Manual sort on the data array inside a `computed`/`effect`, then passing
|
|
382
|
-
the sorted array to the table.
|
|
383
|
-
- Manual pagination math driving `data: paged()` — paginated by the user, not
|
|
384
|
-
the table.
|
|
385
|
-
- Hand-rolled global filter `.filter(...)` inside `effect`.
|
|
386
|
-
|
|
387
|
-
All of these are far slower than the built-in row models (which memoize and
|
|
388
|
-
short-circuit) and ship more code. Use `table.setSorting(...)`,
|
|
389
|
-
`table.setColumnFilters(...)`, the registered `rowModels` factories.
|
|
390
|
-
|
|
391
|
-
### 4. (HIGH) `OnPush` not set
|
|
392
|
-
|
|
393
|
-
Default change detection runs on every event in the entire app. Even with
|
|
394
|
-
signal-backed atoms, you're paying for unnecessary template checks. `OnPush`
|
|
395
|
-
is the table's idiomatic setting.
|
|
396
|
-
|
|
397
|
-
### 5. (HIGH) `@for` without stable `track`
|
|
398
|
-
|
|
399
|
-
`@for (row of rows)` without a `track` value at all is a build error in
|
|
400
|
-
Angular ≥17 strict mode; `track $index` defeats DOM reuse on sort/refetch.
|
|
401
|
-
Always `track row.id` (and `getRowId` on the table).
|
|
402
|
-
|
|
403
|
-
### 6. (HIGH) Over-wrapping every read in `computed(...)`
|
|
404
|
-
|
|
405
|
-
```ts
|
|
406
|
-
// ❌ adds a computed layer for no reason
|
|
407
|
-
readonly pagination = computed(() => this.table.atoms.pagination.get())
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
The atom is already signal-backed. Use `computed` for derivation, custom
|
|
411
|
-
equality, or shared selectors — not for "make it reactive."
|
|
412
|
-
|
|
413
|
-
### 7. (HIGH) `{ equal: shallow }` on every `computed`
|
|
414
|
-
|
|
415
|
-
Shallow equality has a runtime cost (one pass over keys). For primitive
|
|
416
|
-
selectors it's strictly slower than `Object.is`. Reserve it for derived
|
|
417
|
-
object/array slices whose downstream is expensive.
|
|
418
|
-
|
|
419
|
-
### 8. (HIGH) Drilling `cell` / `header` / `table` through multiple
|
|
420
|
-
|
|
421
|
-
components
|
|
422
|
-
|
|
423
|
-
Inputs add diffing cost on every change-detection cycle. Replace with the
|
|
424
|
-
`[tanStackTableCell]` / `[tanStackTableHeader]` / `[tanStackTable]` host
|
|
425
|
-
directives and `injectTableCellContext()` / etc. at the leaf.
|
|
426
|
-
|
|
427
|
-
### 9. (HIGH) `flexRenderComponent(...)` for every cell
|
|
428
|
-
|
|
429
|
-
`flexRenderComponent` adds a wrapper with `reflectComponentType` overhead
|
|
430
|
-
and an `OutputEmitterRef` subscription scan. For plain component pass-through
|
|
431
|
-
where context inputs cover everything, **return the component class
|
|
432
|
-
directly** — the renderer does `setInput` on its own.
|
|
433
|
-
|
|
434
|
-
### 10. (MEDIUM) `effect(...)` chains for what should be on\*Change inline
|
|
435
|
-
|
|
436
|
-
If the user changes `globalFilter` and your `pagination` reset lives in an
|
|
437
|
-
`effect`, you get a CD pass for the filter and a second one for the reset.
|
|
438
|
-
Inline the reset in `onGlobalFilterChange`.
|
|
439
|
-
|
|
440
|
-
### 11. (MEDIUM) Forgetting `autoResetPageIndex: false` for server-driven tables
|
|
441
|
-
|
|
442
|
-
Every fetch produces a new array reference, which triggers the default
|
|
443
|
-
auto-reset and bounces the user back to page 0 mid-pagination. See
|
|
444
|
-
`tanstack-table/angular/client-to-server` §9.
|
|
445
|
-
|
|
446
|
-
### 12. (MEDIUM) `debugTable: true` left in production
|
|
447
|
-
|
|
448
|
-
Turns on per-operation `console.info` logging from the core. Use
|
|
449
|
-
`debugTable: isDevMode()`.
|
|
450
|
-
|
|
451
|
-
### 13. (MEDIUM) Reaching for `Subscribe` patterns ported from React docs
|
|
452
|
-
|
|
453
|
-
Angular doesn't need a `Subscribe` boundary the way React does. The
|
|
454
|
-
adapter's signal binding handles fine-grained reactivity at the atom level —
|
|
455
|
-
templates re-evaluate the dependencies they actually read.
|
|
456
|
-
|
|
457
|
-
---
|
|
458
|
-
|
|
459
|
-
## See also
|
|
460
|
-
|
|
461
|
-
- `tanstack-table/angular/getting-started` — the first-table baseline
|
|
462
|
-
- `tanstack-table/angular/table-state` — narrow vs wide reads, controlled state
|
|
463
|
-
- `tanstack-table/angular/angular-rendering-directives` — `flexRenderComponent`,
|
|
464
|
-
DI tokens
|
|
465
|
-
- `tanstack-table/angular/client-to-server` — server-driven optimizations
|
|
466
|
-
- `tanstack-table/angular/compose-with-tanstack-virtual` — virtualizing big
|
|
467
|
-
tables
|
|
468
|
-
- Example: `examples/angular/composable-tables/` — `createTableHook` for
|
|
469
|
-
app-wide infrastructure
|