@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,487 +0,0 @@
1
- ---
2
- name: angular/getting-started
3
- description: >
4
- End-to-end first-table journey for TanStack Table v9 in Angular: install
5
- `@tanstack/angular-table`, declare `features` with `tableFeatures()` (row-model factories and
6
- fn registries live on the features object), build columns with the `TFeatures, TData` generic
7
- order, call `injectTable(() => ({...}))` from an injection context, and render with `FlexRender` /
8
- `*flexRenderHeader` / `*flexRenderCell` / `*flexRenderFooter`. Covers the minimum-viable
9
- signal-backed table plus the upgrade path to sorting + filtering + pagination.
10
- type: lifecycle
11
- library: tanstack-table
12
- framework: angular
13
- library_version: '9.0.0-alpha.48'
14
- requires:
15
- - angular/table-state
16
- - angular/angular-rendering-directives
17
- - setup
18
- - column-definitions
19
- sources:
20
- - TanStack/table:docs/framework/angular/angular-table.md
21
- - TanStack/table:docs/framework/angular/guide/table-state.md
22
- - TanStack/table:docs/framework/angular/guide/rendering.md
23
- - TanStack/table:packages/angular-table/src/injectTable.ts
24
- - TanStack/table:examples/angular/basic-inject-table/
25
- - TanStack/table:examples/angular/basic-app-table/
26
- ---
27
-
28
- # Getting Started — Angular Table v9
29
-
30
- > Goal: from zero to a working signal-backed, sorted + paginated, type-safe
31
- > table in Angular ≥19.
32
- >
33
- > v9 is **explicit**: tell the table which features you want with `features`.
34
- > Row-model factories and fn registries live on the features object alongside
35
- > the feature flags. That explicitness is what makes the v9 bundle tree-shakeable.
36
-
37
- ---
38
-
39
- ## 1. Install
40
-
41
- ```bash
42
- pnpm add @tanstack/angular-table
43
- # or npm / yarn / bun
44
- ```
45
-
46
- Requires Angular ≥19 (signal APIs, `input()`, structural directive metadata).
47
- Standalone components are assumed.
48
-
49
- ---
50
-
51
- ## 2. The simplest possible table (core only)
52
-
53
- ```ts
54
- // app.ts
55
- import { ChangeDetectionStrategy, Component, signal } from '@angular/core'
56
- import {
57
- FlexRender,
58
- injectTable,
59
- tableFeatures,
60
- type ColumnDef,
61
- } from '@tanstack/angular-table'
62
-
63
- type Person = {
64
- id: string
65
- firstName: string
66
- lastName: string
67
- age: number
68
- }
69
-
70
- // 1. features OUTSIDE the component class (stable reference)
71
- const features = tableFeatures({}) // empty = core row model only
72
-
73
- // 2. columns OUTSIDE the component class (stable reference)
74
- const columns: Array<ColumnDef<typeof features, Person>> = [
75
- {
76
- accessorKey: 'firstName',
77
- header: 'First name',
78
- cell: (info) => info.getValue(),
79
- },
80
- {
81
- accessorKey: 'lastName',
82
- header: 'Last name',
83
- cell: (info) => info.getValue(),
84
- },
85
- {
86
- accessorKey: 'age',
87
- header: () => 'Age',
88
- cell: (info) => info.getValue(),
89
- },
90
- ]
91
-
92
- @Component({
93
- selector: 'app-root',
94
- imports: [FlexRender], // tuple imports BOTH directives
95
- templateUrl: './app.html',
96
- changeDetection: ChangeDetectionStrategy.OnPush,
97
- })
98
- export class App {
99
- readonly data = signal<Array<Person>>([
100
- { id: '1', firstName: 'Ada', lastName: 'Lovelace', age: 36 },
101
- { id: '2', firstName: 'Alan', lastName: 'Turing', age: 41 },
102
- ])
103
-
104
- // 3. injectTable in an injection context (a class field qualifies)
105
- readonly table = injectTable(() => ({
106
- features, // required in v9; core row model is automatic
107
- columns, // stable ref
108
- data: this.data(), // signal read → re-syncs the table on change
109
- }))
110
- }
111
- ```
112
-
113
- ```html
114
- <!-- app.html -->
115
- <table>
116
- <thead>
117
- @for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {
118
- <tr>
119
- @for (header of headerGroup.headers; track header.id) {
120
- <th>
121
- @if (!header.isPlaceholder) {
122
- <ng-container *flexRenderHeader="header; let value">
123
- {{ value }}
124
- </ng-container>
125
- }
126
- </th>
127
- }
128
- </tr>
129
- }
130
- </thead>
131
-
132
- <tbody>
133
- @for (row of table.getRowModel().rows; track row.id) {
134
- <tr>
135
- @for (cell of row.getVisibleCells(); track cell.id) {
136
- <td>
137
- <ng-container *flexRenderCell="cell; let value">
138
- {{ value }}
139
- </ng-container>
140
- </td>
141
- }
142
- </tr>
143
- }
144
- </tbody>
145
- </table>
146
- ```
147
-
148
- That's a complete v9 table. No sorting, no pagination — just `<table>` markup
149
- driven by the row model.
150
-
151
- ### What the boilerplate is doing
152
-
153
- - `tableFeatures({})` registers no opt-in features. The core row model
154
- (`getRowModel()`) is always available. With `features: tableFeatures({})`,
155
- `table.atoms.*` only contains the slices core ships with — no `pagination`,
156
- no `sorting`, no `rowSelection` until you add the matching features.
157
- - `injectTable(() => ({...}))` runs the initializer, builds the table, and
158
- re-runs the initializer whenever any signal read inside changes. Stable
159
- references outside the initializer keep `columns` / `features`
160
- from getting recreated on every data update.
161
-
162
- ---
163
-
164
- ## 3. Add a feature — sorting
165
-
166
- Each opt-in feature has two pieces in v9:
167
-
168
- 1. The **feature** itself (`rowSortingFeature`) in `features` — adds APIs
169
- like `column.toggleSorting()` and the `sorting` state slice.
170
- 2. The **row-model factory** (`createSortedRowModel()`) on the features object
171
- — produces the sorted output. Without it, `table.getRowModel().rows` is
172
- unsorted regardless of sort state.
173
-
174
- ```ts
175
- import {
176
- injectTable,
177
- tableFeatures,
178
- rowSortingFeature,
179
- createSortedRowModel,
180
- sortFns,
181
- type ColumnDef,
182
- } from '@tanstack/angular-table'
183
-
184
- const features = tableFeatures({
185
- rowSortingFeature,
186
- sortedRowModel: createSortedRowModel(), // <-- enables sorting output
187
- sortFns,
188
- })
189
-
190
- readonly table = injectTable(() => ({
191
- features,
192
- columns,
193
- data: this.data(),
194
- }))
195
- ```
196
-
197
- In the template, drive sorting from the header:
198
-
199
- ```html
200
- @if (!header.isPlaceholder) {
201
- <th
202
- (click)="header.column.toggleSorting()"
203
- [style.cursor]="header.column.getCanSort() ? 'pointer' : ''"
204
- >
205
- <ng-container *flexRenderHeader="header; let value">{{ value }}</ng-container>
206
- @switch (header.column.getIsSorted()) { @case ('asc') { ▲ } @case ('desc') { ▼
207
- } }
208
- </th>
209
- }
210
- ```
211
-
212
- > **Use `column.toggleSorting()`, not your own sort handler.** It correctly
213
- > handles the asc → desc → unsorted cycle. Same applies for every other
214
- > feature.
215
-
216
- `sortFns` is the registry of built-in sort functions
217
- (`alphanumeric`, `basic`, `datetime`, etc.). Pass only the ones you use to
218
- tree-shake (`sortFns: { basic: sortFns.basic }`), or pass `sortFns`
219
- in its entirety for all of them.
220
-
221
- ---
222
-
223
- ## 4. Add filtering + pagination
224
-
225
- ```ts
226
- import {
227
- injectTable,
228
- tableFeatures,
229
- rowSortingFeature,
230
- columnFilteringFeature,
231
- rowPaginationFeature,
232
- createSortedRowModel,
233
- createFilteredRowModel,
234
- createPaginatedRowModel,
235
- sortFns,
236
- filterFns,
237
- type ColumnDef,
238
- } from '@tanstack/angular-table'
239
-
240
- const features = tableFeatures({
241
- rowSortingFeature,
242
- columnFilteringFeature,
243
- rowPaginationFeature,
244
- sortedRowModel: createSortedRowModel(),
245
- filteredRowModel: createFilteredRowModel(),
246
- paginatedRowModel: createPaginatedRowModel(),
247
- sortFns,
248
- filterFns,
249
- })
250
-
251
- readonly table = injectTable(() => ({
252
- features,
253
- columns,
254
- data: this.data(),
255
- initialState: {
256
- pagination: { pageIndex: 0, pageSize: 10 },
257
- },
258
- }))
259
- ```
260
-
261
- Pagination controls — again, prefer the table APIs:
262
-
263
- ```html
264
- <button (click)="table.previousPage()" [disabled]="!table.getCanPreviousPage()">
265
-
266
- </button>
267
- <span>
268
- Page {{ table.atoms.pagination.get().pageIndex + 1 }} of {{
269
- table.getPageCount() }}
270
- </span>
271
- <button (click)="table.nextPage()" [disabled]="!table.getCanNextPage()">
272
-
273
- </button>
274
-
275
- <select
276
- [value]="table.atoms.pagination.get().pageSize"
277
- (change)="table.setPageSize(Number($any($event.target).value))"
278
- >
279
- @for (size of [10, 20, 50]; track size) {
280
- <option [value]="size">{{ size }}</option>
281
- }
282
- </select>
283
- ```
284
-
285
- Reading state in the template via `table.atoms.<slice>.get()` is signal-backed
286
- — Angular tracks it and re-renders on change.
287
-
288
- ---
289
-
290
- ## 5. Use the column helper for safer types
291
-
292
- `createColumnHelper<TFeatures, TData>()` (generic order: features first!) gives
293
- type-safe accessor / display / group definitions, plus a `columns(...)` method
294
- for better inference across heterogeneous columns:
295
-
296
- ```ts
297
- import { createColumnHelper } from '@tanstack/angular-table'
298
-
299
- const columnHelper = createColumnHelper<typeof features, Person>()
300
-
301
- const columns = columnHelper.columns([
302
- columnHelper.accessor('firstName', {
303
- header: 'First name',
304
- cell: (info) => info.getValue(),
305
- }),
306
- columnHelper.accessor((row) => `${row.firstName} ${row.lastName}`, {
307
- id: 'fullName',
308
- header: 'Full name',
309
- }),
310
- columnHelper.display({
311
- id: 'actions',
312
- header: 'Actions',
313
- cell: ({ row }) => `Edit #${row.original.id}`,
314
- }),
315
- ])
316
- ```
317
-
318
- > v9 changed the generic order: `createColumnHelper<typeof features, Person>()`,
319
- > **not** `createColumnHelper<Person>()`. Same for `ColumnDef<typeof features, Person>`.
320
-
321
- If multiple components share the same `features` object, factor them into a
322
- `createTableHook(...)` call — see
323
- `tanstack-table/angular/angular-rendering-directives` §10 and the
324
- `composable-tables` example.
325
-
326
- ---
327
-
328
- ## 6. Stable row identity — set `getRowId`
329
-
330
- If your rows have a primary key, set `getRowId`. This makes row selection,
331
- row pinning, and refetch-based updates correct.
332
-
333
- ```ts
334
- readonly table = injectTable(() => ({
335
- features,
336
- columns,
337
- data: this.data(),
338
- getRowId: (row) => row.id, // ← stable ID across re-fetches
339
- }))
340
- ```
341
-
342
- Without `getRowId`, the row index becomes the ID — selection state
343
- ("rows 0–4 selected") survives sorting but breaks across server refetches that
344
- return rows in a new order.
345
-
346
- ---
347
-
348
- ## 7. State ownership — start with internal, hoist when you need to
349
-
350
- The simplest table lets TanStack Table own all state internally. You set
351
- starting values with `initialState`, and you use APIs like `table.nextPage()`
352
- and `table.setSorting(...)` to drive updates.
353
-
354
- ```ts
355
- readonly table = injectTable(() => ({
356
- features,
357
- columns,
358
- data: this.data(),
359
- initialState: {
360
- pagination: { pageIndex: 0, pageSize: 25 },
361
- sorting: [{ id: 'age', desc: true }],
362
- },
363
- }))
364
- ```
365
-
366
- Hoist a slice into an Angular signal only when something outside the table
367
- needs to read or react to it (URL sync, debounced server fetch, persistence,
368
- cross-component coordination). The pattern is `state` + `on[State]Change` →
369
- see `tanstack-table/angular/table-state` §6.
370
-
371
- For full server-driven tables, see `tanstack-table/angular/client-to-server`.
372
-
373
- ---
374
-
375
- ## Failure modes
376
-
377
- ### 1. (CRITICAL) Calling `injectTable` outside an injection context
378
-
379
- `injectTable` calls `assertInInjectionContext`. It must be invoked from a
380
- class-field initializer, constructor, or factory inside a DI scope. Calling it
381
- from a service method or a `setTimeout` callback throws:
382
-
383
- > `NG0203: inject() must be called from an injection context...`
384
-
385
- If you need to construct a table from a service method, capture the injector
386
- and use `runInInjectionContext(injector, () => injectTable(...))`.
387
-
388
- ### 2. (CRITICAL) Hallucinating v8 `createAngularTable` or `getCoreRowModel()`
389
-
390
- ```ts
391
- // ❌ v8
392
- import { createAngularTable, getCoreRowModel } from '@tanstack/angular-table'
393
-
394
- // ✅ v9
395
- import { injectTable, tableFeatures } from '@tanstack/angular-table'
396
- ```
397
-
398
- There is no `getCoreRowModel()` / `getSortedRowModel()` / `getFilteredRowModel()`
399
- in v9. Core row model is automatic; the rest are
400
- `createSortedRowModel()` / `createFilteredRowModel()` / etc.
401
- registered as slots on the `features` object alongside `sortFns` / `filterFns`.
402
-
403
- ### 3. (CRITICAL) Reimplementing what the table API already does
404
-
405
- Telltale AI signs in a getting-started snippet:
406
-
407
- - Custom `sortBy()` on the data signal instead of `table.setSorting()` /
408
- `column.toggleSorting()`.
409
- - Manual `pageIndex` math instead of `table.nextPage()` / `table.getCanNextPage()`.
410
- - Computing `getCanNextPage()` as `pageIndex < Math.ceil(rows / pageSize) - 1`
411
- instead of asking the table.
412
- - Manual filtering of the data array before passing it to the table when you
413
- could just register `columnFilteringFeature` + `createFilteredRowModel`.
414
-
415
- The table already does all of this. Use it.
416
-
417
- ### 4. (HIGH) Feature without its row model (or vice versa)
418
-
419
- ```ts
420
- // ❌ rowSortingFeature without sortedRowModel → sort state changes, rows don't reorder
421
- const features = tableFeatures({ rowSortingFeature })
422
-
423
- // ✅ factory and fn registry on the features object
424
- const features = tableFeatures({
425
- rowSortingFeature,
426
- sortedRowModel: createSortedRowModel(),
427
- sortFns,
428
- })
429
- ```
430
-
431
- Full mapping table → [`references/feature-row-model-mapping.md`](references/feature-row-model-mapping.md).
432
-
433
- ### 5. (HIGH) Declaring `columns` / `features` inside the initializer
434
-
435
- ```ts
436
- // ❌ Recreated on every signal change
437
- readonly table = injectTable(() => ({
438
- features: tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns }),
439
- columns: [/* … */],
440
- data: this.data(),
441
- }))
442
-
443
- // ✅ Stable references outside, signal reads inside
444
- const features = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns })
445
- const columns: Array<ColumnDef<typeof features, Person>> = [/* … */]
446
-
447
- readonly table = injectTable(() => ({
448
- features,
449
- columns,
450
- data: this.data(),
451
- }))
452
- ```
453
-
454
- ### 6. (HIGH) Importing a non-existent `flexRender` function
455
-
456
- In Angular, `FlexRender` is a directive tuple, not a function. There is no
457
- `flexRender(fn, ctx)` call expression — that's the React/Vue API. Always:
458
-
459
- ```ts
460
- import { FlexRender } from '@tanstack/angular-table'
461
- @Component({ imports: [FlexRender] })
462
- ```
463
-
464
- and use `*flexRenderCell` / `*flexRenderHeader` / `*flexRenderFooter` in the
465
- template.
466
-
467
- Lower-severity failure modes (MEDIUM: `createColumnHelper` generic-order flip,
468
- importing only `FlexRenderDirective` without the shorthand) →
469
- [`references/feature-row-model-mapping.md`](references/feature-row-model-mapping.md#lower-severity-failure-modes-medium).
470
-
471
- ---
472
-
473
- ## References
474
-
475
- - [Feature → row-model mapping table and MEDIUM failure modes](references/feature-row-model-mapping.md)
476
-
477
- ---
478
-
479
- ## See also
480
-
481
- - `tanstack-table/angular/table-state` — state model, ownership, controlled vs internal
482
- - `tanstack-table/angular/angular-rendering-directives` — full rendering API surface
483
- - `tanstack-table/angular/migrate-v8-to-v9` — for projects upgrading from v8
484
- - `tanstack-table/angular/client-to-server` — flipping a working table to a server endpoint
485
- - `tanstack-table/angular/production-readiness` — tree-shaking, stable refs, selectors
486
- - Example: `examples/angular/basic-inject-table/`
487
- - Example: `examples/angular/basic-app-table/` (uses `createTableHook`)
@@ -1,51 +0,0 @@
1
- # Feature → Row Model Mapping & Optional Patterns
2
-
3
- Reference material extracted from the getting-started SKILL.md.
4
-
5
- ---
6
-
7
- ## Feature → row model mapping
8
-
9
- Every opt-in v9 feature has two pieces:
10
-
11
- 1. The **feature** itself in `features` — adds APIs (e.g.
12
- `column.toggleSorting()`) and the matching state slice.
13
- 2. A **row-model factory slot** on the `features` object — produces the derived
14
- row output. Without it, sort/filter/paginate UI updates but rows don't reorder.
15
-
16
- Fn registries (`sortFns`, `filterFns`, `aggregationFns`) are also slots on the
17
- `features` object, not parameters to the factory calls.
18
-
19
- | Feature | Slot needed on `features` |
20
- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
21
- | `rowSortingFeature` | `sortedRowModel: createSortedRowModel()` + `sortFns` |
22
- | `columnFilteringFeature` / `globalFilteringFeature` | `filteredRowModel: createFilteredRowModel()` + `filterFns` |
23
- | `rowPaginationFeature` | `paginatedRowModel: createPaginatedRowModel()` |
24
- | `rowExpandingFeature` | `expandedRowModel: createExpandedRowModel()` |
25
- | `columnGroupingFeature` | `groupedRowModel: createGroupedRowModel()` + `aggregationFns` |
26
- | `columnFacetingFeature` | `facetedRowModel: createFacetedRowModel()` (+ `facetedMinMaxValues` / `facetedUniqueValues`) |
27
- | `rowSelectionFeature` | (no row model needed) |
28
- | `columnVisibilityFeature` / `columnOrderingFeature` / `columnPinningFeature` / `columnSizingFeature` / `columnResizingFeature` / `rowPinningFeature` | (no row model needed) |
29
-
30
- ---
31
-
32
- ## Lower-severity failure modes (MEDIUM)
33
-
34
- ### Wrong `createColumnHelper` generic order
35
-
36
- ```ts
37
- // ❌ v8 shape
38
- const columnHelper = createColumnHelper<Person>()
39
-
40
- // ✅ v9 — features first
41
- const columnHelper = createColumnHelper<typeof features, Person>()
42
- ```
43
-
44
- Or use `createAppColumnHelper<Person>()` from a `createTableHook(...)` factory,
45
- which pre-binds `TFeatures`.
46
-
47
- ### Importing only `FlexRenderDirective` and missing the shorthand
48
-
49
- `FlexRender` is preferred — it imports both `FlexRenderDirective` (the long
50
- `*flexRender`) and `FlexRenderCell` (the shorthand). If you only import one,
51
- `*flexRenderCell` won't compile.