@tanstack/angular-table 9.0.0-alpha.8 → 9.0.0-beta.1
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 +127 -0
- package/dist/README.md +127 -0
- package/dist/fesm2022/tanstack-angular-table-static-functions.mjs +6 -0
- package/dist/fesm2022/tanstack-angular-table-static-functions.mjs.map +1 -0
- package/dist/fesm2022/tanstack-angular-table.mjs +1337 -242
- package/dist/fesm2022/tanstack-angular-table.mjs.map +1 -1
- package/dist/types/tanstack-angular-table-static-functions.d.ts +1 -0
- package/dist/types/tanstack-angular-table.d.ts +793 -0
- package/package.json +39 -19
- package/skills/angular/angular-rendering-directives/SKILL.md +415 -0
- package/skills/angular/angular-rendering-directives/references/content-shapes.md +142 -0
- package/skills/angular/angular-rendering-directives/references/create-table-hook-registries.md +89 -0
- package/skills/angular/angular-rendering-directives/references/di-tokens.md +171 -0
- package/skills/angular/angular-rendering-directives/references/flex-render-component-options.md +64 -0
- package/skills/angular/client-to-server/SKILL.md +467 -0
- package/skills/angular/compose-with-tanstack-query/SKILL.md +482 -0
- package/skills/angular/compose-with-tanstack-store/SKILL.md +397 -0
- package/skills/angular/compose-with-tanstack-virtual/SKILL.md +400 -0
- package/skills/angular/getting-started/SKILL.md +496 -0
- package/skills/angular/getting-started/references/feature-row-model-mapping.md +48 -0
- package/skills/angular/migrate-v8-to-v9/SKILL.md +419 -0
- package/skills/angular/migrate-v8-to-v9/references/v8-to-v9-mapping.md +261 -0
- package/skills/angular/production-readiness/SKILL.md +469 -0
- package/skills/angular/table-state/SKILL.md +429 -0
- package/skills/angular/table-state/references/external-atoms-and-app-hook.md +152 -0
- package/src/flex-render/context.ts +14 -0
- package/src/flex-render/flags.ts +34 -0
- package/src/flex-render/flexRenderComponent.ts +288 -0
- package/src/flex-render/flexRenderComponentFactory.ts +251 -0
- package/src/flex-render/renderer.ts +393 -0
- package/src/flex-render/view.ts +226 -0
- package/src/flexRender.ts +124 -0
- package/src/helpers/cell.ts +104 -0
- package/src/helpers/createTableHook.ts +499 -0
- package/src/helpers/flexRenderCell.ts +136 -0
- package/src/helpers/header.ts +99 -0
- package/src/helpers/table.ts +85 -0
- package/src/index.ts +23 -70
- package/src/injectTable.ts +212 -0
- package/src/{lazy-signal-initializer.ts → lazySignalInitializer.ts} +2 -2
- package/src/reactivity.ts +105 -0
- package/static-functions/index.ts +1 -0
- package/static-functions/ng-package.json +6 -0
- package/dist/esm2022/flex-render.mjs +0 -150
- package/dist/esm2022/index.mjs +0 -48
- package/dist/esm2022/lazy-signal-initializer.mjs +0 -43
- package/dist/esm2022/proxy.mjs +0 -83
- package/dist/esm2022/tanstack-angular-table.mjs +0 -5
- package/dist/flex-render.d.ts +0 -31
- package/dist/index.d.ts +0 -5
- package/dist/lazy-signal-initializer.d.ts +0 -5
- package/dist/proxy.d.ts +0 -3
- package/src/flex-render.ts +0 -187
- package/src/proxy.ts +0 -97
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: angular/compose-with-tanstack-virtual
|
|
3
|
+
description: >
|
|
4
|
+
Compose TanStack Table v9 with `@tanstack/angular-virtual` for virtualized rendering of large
|
|
5
|
+
row sets. TanStack Table does NOT virtualize on its own. Pattern: get `rows = table.getRowModel().rows`,
|
|
6
|
+
feed `rows.length` to `injectVirtualizer({ count, estimateSize, getScrollElement, overscan })`,
|
|
7
|
+
iterate `virtualizer.getVirtualItems()` in the template, position each row with
|
|
8
|
+
`transform: translateY(item.start)` inside a tall sentinel, set
|
|
9
|
+
`[style.height.px]="virtualizer.getTotalSize()"` to make the scrollbar correct. Handle the
|
|
10
|
+
table-feature interactions: row-expanding (variable subRow heights → measure with
|
|
11
|
+
`measureElement`), column sizing/pinning (column virtualization is separate),
|
|
12
|
+
row-selection (selection state survives virtualization because it's keyed by row ID).
|
|
13
|
+
type: composition
|
|
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/virtual:packages/angular-virtual/src/
|
|
24
|
+
- TanStack/table:examples/angular/basic-inject-table/
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# Compose with TanStack Virtual (Angular)
|
|
28
|
+
|
|
29
|
+
> TanStack Table is headless — it computes which rows / cells exist, but does
|
|
30
|
+
> not decide which ones to render to the DOM. For tables larger than a few
|
|
31
|
+
> hundred visible rows, pair with [`@tanstack/angular-virtual`](https://tanstack.com/virtual)
|
|
32
|
+
> so only the rows in the viewport (+ overscan) actually mount.
|
|
33
|
+
>
|
|
34
|
+
> Required reading: `tanstack-table/angular/getting-started` and
|
|
35
|
+
> `tanstack-table/angular/table-state`.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 1. Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pnpm add @tanstack/angular-virtual
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Requires the same Angular version as `@tanstack/angular-table`.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 2. The integration in one shape
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import {
|
|
53
|
+
AfterViewInit,
|
|
54
|
+
ChangeDetectionStrategy,
|
|
55
|
+
Component,
|
|
56
|
+
computed,
|
|
57
|
+
signal,
|
|
58
|
+
viewChild,
|
|
59
|
+
ElementRef,
|
|
60
|
+
} from '@angular/core'
|
|
61
|
+
import {
|
|
62
|
+
FlexRender,
|
|
63
|
+
injectTable,
|
|
64
|
+
tableFeatures,
|
|
65
|
+
type ColumnDef,
|
|
66
|
+
} from '@tanstack/angular-table'
|
|
67
|
+
import { injectVirtualizer } from '@tanstack/angular-virtual'
|
|
68
|
+
|
|
69
|
+
const features = tableFeatures({})
|
|
70
|
+
|
|
71
|
+
@Component({
|
|
72
|
+
selector: 'app-virtual-table',
|
|
73
|
+
imports: [FlexRender],
|
|
74
|
+
templateUrl: './virtual-table.html',
|
|
75
|
+
styleUrl: './virtual-table.css',
|
|
76
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
77
|
+
})
|
|
78
|
+
export class VirtualTable {
|
|
79
|
+
readonly data = signal<Array<Person>>(makeData(50_000))
|
|
80
|
+
readonly scrollContainer =
|
|
81
|
+
viewChild.required<ElementRef<HTMLDivElement>>('scroll')
|
|
82
|
+
|
|
83
|
+
readonly table = injectTable(() => ({
|
|
84
|
+
features,
|
|
85
|
+
rowModels: {},
|
|
86
|
+
columns,
|
|
87
|
+
data: this.data(),
|
|
88
|
+
getRowId: (row) => row.id,
|
|
89
|
+
}))
|
|
90
|
+
|
|
91
|
+
// Stable reference to the rows array for the virtualizer
|
|
92
|
+
readonly rows = computed(() => this.table.getRowModel().rows)
|
|
93
|
+
|
|
94
|
+
readonly rowVirtualizer = injectVirtualizer(() => ({
|
|
95
|
+
count: this.rows().length,
|
|
96
|
+
getScrollElement: () => this.scrollContainer().nativeElement,
|
|
97
|
+
estimateSize: () => 36, // fixed-height rows
|
|
98
|
+
overscan: 10,
|
|
99
|
+
}))
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<!-- virtual-table.html -->
|
|
105
|
+
<div #scroll class="scroll-container" style="height: 600px; overflow: auto">
|
|
106
|
+
<table style="display: grid">
|
|
107
|
+
<thead style="display: grid; position: sticky; top: 0; z-index: 1">
|
|
108
|
+
@for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {
|
|
109
|
+
<tr style="display: flex; width: 100%">
|
|
110
|
+
@for (header of headerGroup.headers; track header.id) {
|
|
111
|
+
<th style="display: flex">
|
|
112
|
+
<ng-container *flexRenderHeader="header; let value"
|
|
113
|
+
>{{ value }}</ng-container
|
|
114
|
+
>
|
|
115
|
+
</th>
|
|
116
|
+
}
|
|
117
|
+
</tr>
|
|
118
|
+
}
|
|
119
|
+
</thead>
|
|
120
|
+
|
|
121
|
+
<tbody
|
|
122
|
+
style="display: grid; position: relative"
|
|
123
|
+
[style.height.px]="rowVirtualizer.getTotalSize()"
|
|
124
|
+
>
|
|
125
|
+
@for (virtualRow of rowVirtualizer.getVirtualItems(); track
|
|
126
|
+
virtualRow.key) { @let row = rows()[virtualRow.index];
|
|
127
|
+
<tr
|
|
128
|
+
[attr.data-index]="virtualRow.index"
|
|
129
|
+
style="display: flex; position: absolute; width: 100%"
|
|
130
|
+
[style.transform]="'translateY(' + virtualRow.start + 'px)'"
|
|
131
|
+
>
|
|
132
|
+
@for (cell of row.getVisibleCells(); track cell.id) {
|
|
133
|
+
<td style="display: flex">
|
|
134
|
+
<ng-container *flexRenderCell="cell; let value"
|
|
135
|
+
>{{ value }}</ng-container
|
|
136
|
+
>
|
|
137
|
+
</td>
|
|
138
|
+
}
|
|
139
|
+
</tr>
|
|
140
|
+
}
|
|
141
|
+
</tbody>
|
|
142
|
+
</table>
|
|
143
|
+
</div>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### What's doing what
|
|
147
|
+
|
|
148
|
+
- **Table** produces `rows` (`table.getRowModel().rows`). Length, identity,
|
|
149
|
+
order are decided by registered features (sort, filter, pagination,
|
|
150
|
+
grouping).
|
|
151
|
+
- **Virtualizer** turns `rows.length` into the subset of "virtual items"
|
|
152
|
+
currently visible (+ `overscan`). It tracks scroll on `getScrollElement()`
|
|
153
|
+
and emits `getVirtualItems()` keyed by `virtualRow.key` (the row index by
|
|
154
|
+
default).
|
|
155
|
+
- **Template** renders only the virtual items, positions each with
|
|
156
|
+
`translateY(virtualRow.start)` inside a sentinel of total height
|
|
157
|
+
`getTotalSize()`. The scrollbar reflects the full row count, but only the
|
|
158
|
+
visible window has DOM nodes.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 3. Mandatory layout details
|
|
163
|
+
|
|
164
|
+
This integration touches CSS in a few non-obvious places. None are optional:
|
|
165
|
+
|
|
166
|
+
- **Scroll container has a fixed height** (`height: 600px` / `100vh` /
|
|
167
|
+
whatever) **and `overflow: auto`**. The virtualizer needs both to compute
|
|
168
|
+
visible range.
|
|
169
|
+
- **Use `display: grid` on `<table>`, `display: flex` on `<thead>` /
|
|
170
|
+
`<tbody>` / `<tr>`**, or use `<div>` markup. Native `<table>` layout
|
|
171
|
+
defeats positioning rows absolutely. The virtual example above uses CSS
|
|
172
|
+
grid to keep semantic table markup while letting the rows position freely.
|
|
173
|
+
- **Container `<tbody>` is `position: relative` with explicit
|
|
174
|
+
`height = virtualizer.getTotalSize()`.** Without that height, the scrollbar
|
|
175
|
+
doesn't reflect the full data.
|
|
176
|
+
- **Rows are `position: absolute; top: 0; left: 0; width: 100%` with
|
|
177
|
+
`transform: translateY(virtualRow.start)`.**
|
|
178
|
+
- **Sticky header**: `position: sticky; top: 0; z-index: 1` on the `<thead>`
|
|
179
|
+
/ its `<tr>` — the scroll container provides the scrolling.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 4. Variable row heights — measure dynamically
|
|
184
|
+
|
|
185
|
+
When rows can be different heights (expanded subRows, dynamic cell content),
|
|
186
|
+
pass `measureElement` and a sensible `estimateSize`:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
readonly rowVirtualizer = injectVirtualizer(() => ({
|
|
190
|
+
count: this.rows().length,
|
|
191
|
+
getScrollElement: () => this.scrollContainer().nativeElement,
|
|
192
|
+
estimateSize: () => 36,
|
|
193
|
+
overscan: 10,
|
|
194
|
+
measureElement: (element) => element?.getBoundingClientRect().height ?? 36,
|
|
195
|
+
}))
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
In the template, bind the element so the virtualizer can measure it:
|
|
199
|
+
|
|
200
|
+
```html
|
|
201
|
+
<tr
|
|
202
|
+
#rowEl
|
|
203
|
+
[attr.data-index]="virtualRow.index"
|
|
204
|
+
[virtualizerMeasureElement]="rowVirtualizer"
|
|
205
|
+
...
|
|
206
|
+
></tr>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
(See `@tanstack/angular-virtual` docs for the exact directive name and API;
|
|
210
|
+
the principle is: every mounted row reports its real size, the virtualizer
|
|
211
|
+
caches that, scrollbar adjusts.)
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 5. Row expanding — `rowExpandingFeature`
|
|
216
|
+
|
|
217
|
+
Combine with `rowExpandingFeature` for "click to expand details":
|
|
218
|
+
|
|
219
|
+
- Register `rowExpandingFeature` in `features` and
|
|
220
|
+
`expandedRowModel: createExpandedRowModel()` in `rowModels`.
|
|
221
|
+
- Use `table.getExpandedRowModel().rows` (or `getRowModel().rows`, which
|
|
222
|
+
already includes expansion under `paginateExpandedRows: true` semantics —
|
|
223
|
+
see `tanstack-table/core/row-expanding`).
|
|
224
|
+
- **Always use `measureElement`** because expansion changes row heights.
|
|
225
|
+
- The virtualizer keys items by index; expanded subRows shift later rows
|
|
226
|
+
down — that's correct and expected.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 6. Row selection works transparently
|
|
231
|
+
|
|
232
|
+
Row selection is keyed by row ID (`getRowId`), not by DOM presence. A row can
|
|
233
|
+
be selected while off-screen; scrolling it into view shows the right checkbox
|
|
234
|
+
state. **Always set `getRowId`** — critical for both selection and
|
|
235
|
+
virtualizer key stability.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 7. Column virtualization (horizontal)
|
|
240
|
+
|
|
241
|
+
For very wide tables (50+ columns), virtualize columns too — a second
|
|
242
|
+
`injectVirtualizer` over `table.getVisibleLeafColumns().length`. The pattern
|
|
243
|
+
mirrors row virtualization but on the X axis. Combine with
|
|
244
|
+
`columnPinningFeature` so pinned columns escape the virtualizer (always
|
|
245
|
+
rendered, sticky).
|
|
246
|
+
|
|
247
|
+
That's a meaningfully bigger lift — most tables don't need it. Reach for it
|
|
248
|
+
only when you've profiled and column count is the bottleneck.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 8. Interaction with pagination
|
|
253
|
+
|
|
254
|
+
**If you paginate, you usually don't virtualize.** Pagination already caps
|
|
255
|
+
the rendered row count to `pageSize`. Adding virtualization on top is
|
|
256
|
+
typically wasted effort — you've already solved the rendering bottleneck.
|
|
257
|
+
|
|
258
|
+
The exceptions:
|
|
259
|
+
|
|
260
|
+
- Pages can hold thousands of rows (rare).
|
|
261
|
+
- Pagination is "load more" / infinite scroll style — then virtualize the
|
|
262
|
+
accumulated rows.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## 9. Interaction with sticky / pinned rows
|
|
267
|
+
|
|
268
|
+
`rowPinningFeature` + virtualization is fiddly. Pinned rows live at the
|
|
269
|
+
top/bottom of the table; they should render _outside_ the virtualizer's
|
|
270
|
+
absolute positioning. Render them in dedicated `<thead>` /
|
|
271
|
+
top/bottom-of-`<tbody>` sections, and call `table.getCenterRows()` (the
|
|
272
|
+
non-pinned rows) to feed the virtualizer. See
|
|
273
|
+
`tanstack-table/core/row-pinning` for the API surface.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## 10. SSR / first-paint
|
|
278
|
+
|
|
279
|
+
On the server / first hydration, the scroll container's height is unknown;
|
|
280
|
+
the virtualizer can render zero rows. Two mitigations:
|
|
281
|
+
|
|
282
|
+
- Render a small initial chunk server-side (without the virtualizer) and let
|
|
283
|
+
Angular hydrate into the virtualized version client-side.
|
|
284
|
+
- Provide an explicit `initialRect: { width, height }` to the virtualizer
|
|
285
|
+
options for SSR.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Failure modes
|
|
290
|
+
|
|
291
|
+
### 1. (CRITICAL) Trying to use TanStack Table's own virtualization
|
|
292
|
+
|
|
293
|
+
There is none. TanStack Table doesn't ship a virtualizer. If an agent
|
|
294
|
+
suggests `getVirtualizedRows()` or `enableVirtualization: true` on the table —
|
|
295
|
+
those don't exist. Use `@tanstack/angular-virtual`.
|
|
296
|
+
|
|
297
|
+
### 2. (CRITICAL) Missing height on the scroll container
|
|
298
|
+
|
|
299
|
+
```html
|
|
300
|
+
<!-- ❌ no height → virtualizer reports 0 visible items → nothing renders -->
|
|
301
|
+
<div #scroll style="overflow: auto">
|
|
302
|
+
<!-- ✅ -->
|
|
303
|
+
<div #scroll style="height: 600px; overflow: auto"></div>
|
|
304
|
+
</div>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
The virtualizer measures the _scroll element_'s viewport. Without an explicit
|
|
308
|
+
or computed height, the viewport is 0 and nothing renders.
|
|
309
|
+
|
|
310
|
+
### 3. (CRITICAL) Missing `getTotalSize()` height on the row container
|
|
311
|
+
|
|
312
|
+
```html
|
|
313
|
+
<!-- ❌ scrollbar reflects only the rendered rows, not the full dataset -->
|
|
314
|
+
<tbody>
|
|
315
|
+
<!-- ✅ -->
|
|
316
|
+
</tbody>
|
|
317
|
+
|
|
318
|
+
<tbody [style.height.px]="rowVirtualizer.getTotalSize()"></tbody>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Without this, you can scroll to the bottom of the _visible_ rows but can
|
|
322
|
+
never reach row 1000. The scrollbar lies.
|
|
323
|
+
|
|
324
|
+
### 4. (CRITICAL) Forgetting `transform: translateY(...)` per row
|
|
325
|
+
|
|
326
|
+
Absolutely-positioned rows without `transform` stack at `top: 0` — every row
|
|
327
|
+
renders on top of every other.
|
|
328
|
+
|
|
329
|
+
### 5. (CRITICAL) Using native `<table>` layout with absolute-positioned rows
|
|
330
|
+
|
|
331
|
+
Native `<table>` layout overrides positioning on `<tr>` / `<td>`. Either:
|
|
332
|
+
|
|
333
|
+
- Use `display: grid` on `<table>` and `display: flex` on `<tr>` / `<td>`
|
|
334
|
+
(see §3), preserving semantic markup, OR
|
|
335
|
+
- Use `<div>` markup throughout.
|
|
336
|
+
|
|
337
|
+
### 6. (HIGH) Variable row heights without `measureElement`
|
|
338
|
+
|
|
339
|
+
Default `estimateSize` is a constant. Different real heights → wrong
|
|
340
|
+
positions → rows visually overlap or leave gaps. Pass `measureElement` and a
|
|
341
|
+
way for each mounted row to report its real size.
|
|
342
|
+
|
|
343
|
+
### 7. (HIGH) Pagination + virtualization both enabled
|
|
344
|
+
|
|
345
|
+
Pagination already caps row count. Adding virtualization on top doubles the
|
|
346
|
+
indirection for no win. Pick one.
|
|
347
|
+
|
|
348
|
+
### 8. (HIGH) Reimplementing virtualization with `IntersectionObserver`
|
|
349
|
+
|
|
350
|
+
Saw an agent build a homegrown "render rows when visible" with
|
|
351
|
+
`IntersectionObserver`? That's hundreds of lines of broken virtualization.
|
|
352
|
+
Use the library.
|
|
353
|
+
|
|
354
|
+
### 9. (HIGH) Wrong `track` in the virtualized `@for`
|
|
355
|
+
|
|
356
|
+
```html
|
|
357
|
+
<!-- ❌ tracking by row.id confuses Angular because positions shift -->
|
|
358
|
+
@for (virtualRow of rowVirtualizer.getVirtualItems(); track row.id)
|
|
359
|
+
|
|
360
|
+
<!-- ✅ -->
|
|
361
|
+
@for (virtualRow of rowVirtualizer.getVirtualItems(); track virtualRow.key)
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Track by the virtual item's stable key (or index). The row is _inside_ the
|
|
365
|
+
virtual item — Angular uses the outer track for DOM reuse.
|
|
366
|
+
|
|
367
|
+
### 10. (MEDIUM) `injectVirtualizer` outside an injection context
|
|
368
|
+
|
|
369
|
+
Like `injectTable`, `injectVirtualizer` calls `assertInInjectionContext()`.
|
|
370
|
+
Place it on a class field, in a constructor, or inside `runInInjectionContext`.
|
|
371
|
+
|
|
372
|
+
### 11. (MEDIUM) Recreating `count` / `estimateSize` on every signal change without
|
|
373
|
+
|
|
374
|
+
stable callbacks
|
|
375
|
+
|
|
376
|
+
Move `estimateSize`, `measureElement`, `getScrollElement` to stable
|
|
377
|
+
references (class arrow methods or module-scope functions) where possible.
|
|
378
|
+
Otherwise the virtualizer re-initializes its internal state on every change.
|
|
379
|
+
|
|
380
|
+
### 12. (MEDIUM) Missing `getRowId` — selection breaks across re-sorts in a
|
|
381
|
+
|
|
382
|
+
virtualized table
|
|
383
|
+
|
|
384
|
+
This isn't virtualization-specific, but it's especially visible here because
|
|
385
|
+
virtualization renders a window of rows; refreshing that window via scroll
|
|
386
|
+
makes mismatched checkbox state obvious. `getRowId: (row) => row.id` is
|
|
387
|
+
mandatory.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## See also
|
|
392
|
+
|
|
393
|
+
- `tanstack-table/angular/getting-started` — baseline table that this skill
|
|
394
|
+
layers virtualization on top of
|
|
395
|
+
- `tanstack-table/angular/production-readiness` — when to reach for
|
|
396
|
+
virtualization vs server-side pagination
|
|
397
|
+
- `tanstack-table/core/row-expanding` — variable subRow heights + virtual
|
|
398
|
+
- `tanstack-table/core/column-layout` — pinning interaction
|
|
399
|
+
- `@tanstack/angular-virtual` docs — `injectVirtualizer`, options reference,
|
|
400
|
+
variable-height patterns
|