@tanstack/angular-table 9.0.0-beta.4 → 9.0.0-beta.43
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
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: table-state
|
|
3
|
+
description: >
|
|
4
|
+
Use Angular-signal-backed table.atoms, direct template reads, computed selectors, controlled signals, value-or-updater callbacks, and external Angular Store atoms while accounting for injectTable initializer reruns.
|
|
5
|
+
metadata:
|
|
6
|
+
type: framework
|
|
7
|
+
library: '@tanstack/angular-table'
|
|
8
|
+
framework: angular
|
|
9
|
+
library_version: '9.0.0-beta.43'
|
|
10
|
+
requires:
|
|
11
|
+
- '@tanstack/table-core#core'
|
|
12
|
+
- getting-started
|
|
13
|
+
sources:
|
|
14
|
+
- 'TanStack/table:docs/framework/angular/guide/table-state.md'
|
|
15
|
+
- 'TanStack/table:examples/angular/basic-external-state'
|
|
16
|
+
- 'TanStack/table:packages/angular-table/src/injectTable.ts'
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
This skill builds on `@tanstack/table-core#core` and `getting-started`. Read them first for state ownership and Angular construction.
|
|
20
|
+
|
|
21
|
+
## State Mental Model
|
|
22
|
+
|
|
23
|
+
TanStack Table is primarily a state coordinator. Keep state internal unless another system must read, persist, or drive it. Without `initialState`, `atoms`, `state`, or `on[State]Change`, the table owns all registered slices.
|
|
24
|
+
|
|
25
|
+
- `table.baseAtoms` are internal writable atoms initialized from resolved initial state.
|
|
26
|
+
- `table.atoms` are readonly derived atoms for the active owner of each registered slice.
|
|
27
|
+
- `table.store` combines those atoms into one readonly flat store.
|
|
28
|
+
|
|
29
|
+
The Angular adapter backs atoms with Angular signals. Reads participate in tracking inside templates, `computed`, and `effect`; signal reads inside the `injectTable` initializer also rerun that initializer and call `setOptions`. State is feature-based: missing pagination state or APIs indicate a missing `rowPaginationFeature`. Hoist stable `features` and `columns` outside the initializer, and return signal-backed `data` without mapping or slicing inline.
|
|
30
|
+
|
|
31
|
+
## Setup
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { computed, signal } from '@angular/core'
|
|
35
|
+
import {
|
|
36
|
+
injectTable,
|
|
37
|
+
rowPaginationFeature,
|
|
38
|
+
tableFeatures,
|
|
39
|
+
} from '@tanstack/angular-table'
|
|
40
|
+
|
|
41
|
+
const features = tableFeatures({ rowPaginationFeature })
|
|
42
|
+
const columns = [{ accessorKey: 'name' }]
|
|
43
|
+
|
|
44
|
+
export class TableComponent {
|
|
45
|
+
readonly data = signal([{ name: 'Ada' }])
|
|
46
|
+
readonly table = injectTable(() => ({ features, columns, data: this.data() }))
|
|
47
|
+
readonly pageIndex = computed(
|
|
48
|
+
() => this.table.atoms.pagination.get().pageIndex,
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Angular-backed table atom reads are signal reads. Templates, `computed`, and `effect` track them directly.
|
|
54
|
+
|
|
55
|
+
## Core Patterns
|
|
56
|
+
|
|
57
|
+
### Prefer external atoms for cross-system state
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { createAtom } from '@tanstack/angular-store'
|
|
61
|
+
import type { PaginationState } from '@tanstack/angular-table'
|
|
62
|
+
|
|
63
|
+
readonly paginationAtom = createAtom<PaginationState>({ pageIndex: 0, pageSize: 20 })
|
|
64
|
+
readonly table = injectTable(() => ({
|
|
65
|
+
features, columns, data: this.data(), atoms: { pagination: this.paginationAtom },
|
|
66
|
+
}))
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The atom can feed Query without making every state write rerun the Table initializer.
|
|
70
|
+
|
|
71
|
+
### Resolve controlled signal updaters
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
readonly pagination = signal({ pageIndex: 0, pageSize: 20 })
|
|
75
|
+
readonly table = injectTable(() => ({
|
|
76
|
+
features, columns, data: this.data(), state: { pagination: this.pagination() },
|
|
77
|
+
onPaginationChange: next => typeof next === 'function' ? this.pagination.update(next) : this.pagination.set(next),
|
|
78
|
+
}))
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Choose State Ownership
|
|
82
|
+
|
|
83
|
+
Use one owner for each slice:
|
|
84
|
+
|
|
85
|
+
- Prefer internal state and feature APIs for local interaction.
|
|
86
|
+
- Use `initialState` for starting/reset values; later changes do not reset current state.
|
|
87
|
+
- Prefer a stable atom from `@tanstack/angular-store` in `atoms` for Query or other cross-system state. Table APIs update it without a change callback.
|
|
88
|
+
- Use an Angular signal read in `state.<slice>` plus the matching callback for simple controlled state. Handle raw values and updater functions.
|
|
89
|
+
|
|
90
|
+
External atoms win over controlled `state`, which syncs into the internal base atom. Do not give a slice two owners. The global v8 `onStateChange` option is gone; subscribe to `table.store` when all state changes must be observed.
|
|
91
|
+
|
|
92
|
+
## Initialize, Update, and Reset
|
|
93
|
+
|
|
94
|
+
Prefer feature methods such as `setSorting`, `nextPage`, `toggleVisibility`, and `toggleSelected`. Direct base-atom writes are a rare escape hatch for internal state; write the external atom when it owns a slice.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
this.table.resetSorting()
|
|
98
|
+
this.table.resetPagination()
|
|
99
|
+
this.table.resetPagination(true)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Feature resets use `table.initialState` unless `true` requests the feature default, and can update external owners. Core `table.reset()` resets internal base atoms only. Use feature-specific types such as `PaginationState`; use `TableState<typeof features>` when the complete registered state type is needed.
|
|
103
|
+
|
|
104
|
+
## Common Mistakes
|
|
105
|
+
|
|
106
|
+
### MEDIUM Wrapping atoms redundantly
|
|
107
|
+
|
|
108
|
+
Wrong:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
readonly pagination = computed(() => computed(() => this.table.atoms.pagination.get())())
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Correct:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
readonly pagination = computed(() => this.table.atoms.pagination.get())
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Table atoms already bridge to Angular signals; one tracked read is sufficient.
|
|
121
|
+
|
|
122
|
+
Source: `docs/framework/angular/guide/table-state.md`
|
|
123
|
+
|
|
124
|
+
### HIGH Ignoring initializer reruns
|
|
125
|
+
|
|
126
|
+
Wrong:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
injectTable(() => ({
|
|
130
|
+
features: tableFeatures({ rowPaginationFeature }),
|
|
131
|
+
columns: makeColumns(),
|
|
132
|
+
state: { pagination: this.pagination() },
|
|
133
|
+
data,
|
|
134
|
+
}))
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Correct:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
injectTable(() => ({
|
|
141
|
+
features,
|
|
142
|
+
columns,
|
|
143
|
+
state: { pagination: this.pagination() },
|
|
144
|
+
data,
|
|
145
|
+
}))
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Controlled signal writes rerun the initializer, so static work must remain outside it.
|
|
149
|
+
|
|
150
|
+
Source: `packages/angular-table/src/injectTable.ts`
|
|
151
|
+
|
|
152
|
+
### HIGH Storing updater functions
|
|
153
|
+
|
|
154
|
+
Wrong:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
onPaginationChange: (next) => this.pagination.set(next)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Correct:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
onPaginationChange: (next) =>
|
|
164
|
+
typeof next === 'function'
|
|
165
|
+
? this.pagination.update(next)
|
|
166
|
+
: this.pagination.set(next)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Callbacks receive a value or updater; assigning the function corrupts owned state.
|
|
170
|
+
|
|
171
|
+
Source: `examples/angular/basic-external-state/src/app/app.ts`
|
|
172
|
+
|
|
173
|
+
### MEDIUM Giving one slice multiple owners
|
|
174
|
+
|
|
175
|
+
Wrong:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
{ initialState: { pagination: start }, atoms: { pagination: this.paginationAtom } }
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Correct:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
{
|
|
185
|
+
atoms: {
|
|
186
|
+
pagination: this.paginationAtom
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
External atoms/state override initial state; choose one owner for each slice.
|
|
192
|
+
|
|
193
|
+
Source: `docs/framework/angular/guide/table-state.md`
|
|
194
|
+
|
|
195
|
+
## API Discovery
|
|
196
|
+
|
|
197
|
+
Inspect `node_modules/@tanstack/angular-table/src/injectTable.ts` and `reactivity.ts`; inspect `@tanstack/angular-store/src` for external atoms and installed core feature source for state APIs.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: with-tanstack-query
|
|
3
|
+
description: >
|
|
4
|
+
Compose Angular Query with signal-owned Table filtering, sorting, and pagination state using reactive query options, manual row-model boundaries, direct query data, server counts, and valid injection context.
|
|
5
|
+
metadata:
|
|
6
|
+
type: composition
|
|
7
|
+
library: '@tanstack/angular-table'
|
|
8
|
+
framework: angular
|
|
9
|
+
library_version: '9.0.0-beta.43'
|
|
10
|
+
requires:
|
|
11
|
+
- '@tanstack/table-core#client-vs-server'
|
|
12
|
+
- getting-started
|
|
13
|
+
- table-state
|
|
14
|
+
sources:
|
|
15
|
+
- 'TanStack/table:examples/angular/with-tanstack-query'
|
|
16
|
+
- 'TanStack/table:docs/framework/angular/guide/table-state.md'
|
|
17
|
+
- 'TanStack/table:docs/framework/angular/guide/pagination.md'
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
This skill builds on `@tanstack/table-core#client-vs-server`, `getting-started`, and `table-state`. Decide the server-owned stages and dataset before wiring Query.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import {
|
|
26
|
+
injectQuery,
|
|
27
|
+
keepPreviousData,
|
|
28
|
+
} from '@tanstack/angular-query-experimental'
|
|
29
|
+
import { signal } from '@angular/core'
|
|
30
|
+
import {
|
|
31
|
+
injectTable,
|
|
32
|
+
rowPaginationFeature,
|
|
33
|
+
tableFeatures,
|
|
34
|
+
} from '@tanstack/angular-table'
|
|
35
|
+
|
|
36
|
+
const features = tableFeatures({ rowPaginationFeature })
|
|
37
|
+
const EMPTY_ROWS: never[] = []
|
|
38
|
+
|
|
39
|
+
export class PeopleTable {
|
|
40
|
+
readonly pagination = signal({ pageIndex: 0, pageSize: 20 })
|
|
41
|
+
readonly query = injectQuery(() => ({
|
|
42
|
+
queryKey: [
|
|
43
|
+
'people',
|
|
44
|
+
this.pagination().pageIndex,
|
|
45
|
+
this.pagination().pageSize,
|
|
46
|
+
],
|
|
47
|
+
queryFn: () =>
|
|
48
|
+
fetch(
|
|
49
|
+
`/api/people?page=${this.pagination().pageIndex}&size=${this.pagination().pageSize}`,
|
|
50
|
+
).then((r) => r.json()),
|
|
51
|
+
placeholderData: keepPreviousData,
|
|
52
|
+
}))
|
|
53
|
+
readonly table = injectTable(() => ({
|
|
54
|
+
features,
|
|
55
|
+
columns,
|
|
56
|
+
data: this.query.data()?.rows ?? EMPTY_ROWS,
|
|
57
|
+
rowCount: this.query.data()?.rowCount ?? 0,
|
|
58
|
+
manualPagination: true,
|
|
59
|
+
state: { pagination: this.pagination() },
|
|
60
|
+
onPaginationChange: (next) =>
|
|
61
|
+
typeof next === 'function'
|
|
62
|
+
? this.pagination.update(next)
|
|
63
|
+
: this.pagination.set(next),
|
|
64
|
+
}))
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Core Patterns
|
|
69
|
+
|
|
70
|
+
### Track every server-owned input
|
|
71
|
+
|
|
72
|
+
Read pagination, sorting, and filtering signals inside `injectQuery(() => ...)` and include them in the query key. Return data already processed for each manual stage.
|
|
73
|
+
|
|
74
|
+
### Keep Query data authoritative
|
|
75
|
+
|
|
76
|
+
Read the Query signal directly in `injectTable`. Create another signal only for a deliberate editable draft with an explicit cache synchronization policy.
|
|
77
|
+
|
|
78
|
+
## Common Mistakes
|
|
79
|
+
|
|
80
|
+
### HIGH Capturing query inputs outside tracking
|
|
81
|
+
|
|
82
|
+
Wrong:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
const page = this.pagination().pageIndex
|
|
86
|
+
readonly query = injectQuery(() => ({ queryKey: ['people', page], queryFn }))
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Correct:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
readonly query = injectQuery(() => ({ queryKey: ['people', this.pagination().pageIndex], queryFn }))
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Signal reads inside the options function establish refetch dependencies.
|
|
96
|
+
|
|
97
|
+
Source: `examples/angular/with-tanstack-query/src/app/app.ts`
|
|
98
|
+
|
|
99
|
+
### HIGH Expecting manual mode to request
|
|
100
|
+
|
|
101
|
+
Wrong:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
injectTable(() => ({ features, columns, data, manualPagination: true }))
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Correct:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
injectTable(() => ({
|
|
111
|
+
features,
|
|
112
|
+
columns,
|
|
113
|
+
data: this.query.data()?.rows ?? EMPTY_ROWS,
|
|
114
|
+
manualPagination: true,
|
|
115
|
+
}))
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Manual flags only bypass client row processing; Query performs network work.
|
|
119
|
+
|
|
120
|
+
Source: `examples/angular/with-tanstack-query/src/app/app.ts`
|
|
121
|
+
|
|
122
|
+
### HIGH Omitting total counts
|
|
123
|
+
|
|
124
|
+
Wrong:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
{ data: this.query.data()?.rows ?? EMPTY_ROWS, manualPagination: true }
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Correct:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
{ data: this.query.data()?.rows ?? EMPTY_ROWS, rowCount: this.query.data()?.rowCount ?? 0, manualPagination: true }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Table needs `rowCount` or `pageCount` to constrain navigation across server pages.
|
|
137
|
+
|
|
138
|
+
Source: `docs/framework/angular/guide/pagination.md`
|
|
139
|
+
|
|
140
|
+
## API Discovery
|
|
141
|
+
|
|
142
|
+
Inspect installed `@tanstack/angular-table/src/injectTable.ts`, the relevant core feature source, and installed Angular Query source for the exact `injectQuery` package/version contract.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: with-tanstack-virtual
|
|
3
|
+
description: >
|
|
4
|
+
Virtualize Angular Table final row or column models inside the correct injection and reactive lifecycle with signal counts, scroll elements, keys, measurement, transforms, sticky regions, grid/flex sizing, and infinite data.
|
|
5
|
+
metadata:
|
|
6
|
+
type: composition
|
|
7
|
+
library: '@tanstack/angular-table'
|
|
8
|
+
framework: angular
|
|
9
|
+
library_version: '9.0.0-beta.43'
|
|
10
|
+
requires:
|
|
11
|
+
- '@tanstack/table-core#core'
|
|
12
|
+
- getting-started
|
|
13
|
+
- table-state
|
|
14
|
+
sources:
|
|
15
|
+
- 'TanStack/table:docs/framework/angular/guide/virtualization.md'
|
|
16
|
+
- 'TanStack/table:examples/angular/virtualized-rows'
|
|
17
|
+
- 'TanStack/table:examples/angular/virtualized-columns'
|
|
18
|
+
- 'TanStack/table:examples/angular/virtualized-infinite-scrolling'
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
This skill builds on `@tanstack/table-core#core`, `getting-started`, and `table-state`. Virtual is a rendering concern over final Table models, not a feature plugin.
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { computed, viewChild } from '@angular/core'
|
|
27
|
+
import { injectVirtualizer } from '@tanstack/angular-virtual'
|
|
28
|
+
import type { ElementRef } from '@angular/core'
|
|
29
|
+
|
|
30
|
+
export class VirtualTable {
|
|
31
|
+
readonly scrollElement =
|
|
32
|
+
viewChild<ElementRef<HTMLDivElement>>('scrollElement')
|
|
33
|
+
readonly rows = computed(() => this.table.getRowModel().rows)
|
|
34
|
+
readonly rowVirtualizer = injectVirtualizer(() => ({
|
|
35
|
+
count: this.rows().length,
|
|
36
|
+
scrollElement: this.scrollElement()?.nativeElement,
|
|
37
|
+
estimateSize: () => 34,
|
|
38
|
+
getItemKey: (index) => this.rows()[index]!.id,
|
|
39
|
+
overscan: 5,
|
|
40
|
+
}))
|
|
41
|
+
readonly virtualRows = computed(() => this.rowVirtualizer.getVirtualItems())
|
|
42
|
+
readonly totalSize = computed(() => this.rowVirtualizer.getTotalSize())
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Core Patterns
|
|
47
|
+
|
|
48
|
+
### Derive reactive models
|
|
49
|
+
|
|
50
|
+
Use `computed(() => table.getRowModel().rows)` and `computed(() => table.getVisibleLeafColumns())`. `injectVirtualizer` tracks signal reads in its initializer and requires injection context.
|
|
51
|
+
|
|
52
|
+
### Own the layout contract
|
|
53
|
+
|
|
54
|
+
Give the scroll element bounded overflow, create a total-size spacer, and translate or measure each virtual item. Use grid/flex widths for dynamic rows/columns and keep sticky headers inside the correct scroll geometry.
|
|
55
|
+
|
|
56
|
+
### Gate infinite fetches
|
|
57
|
+
|
|
58
|
+
When the last virtual item approaches fetched length, fetch only if more server rows exist and a request is not active. Manual sorting requires server-sorted pages and a reset/refetch policy.
|
|
59
|
+
|
|
60
|
+
## Common Mistakes
|
|
61
|
+
|
|
62
|
+
### CRITICAL Constructing outside injection context
|
|
63
|
+
|
|
64
|
+
Wrong:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
export function virtualize(options) {
|
|
68
|
+
return injectVirtualizer(() => options)
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Correct:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
export class VirtualTable {
|
|
76
|
+
readonly virtualizer = injectVirtualizer(() => options())
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The Angular virtualizer follows DI lifecycle rules just like `injectTable`.
|
|
81
|
+
|
|
82
|
+
Source: `examples/angular/virtualized-rows/src/app/app.ts`
|
|
83
|
+
|
|
84
|
+
### HIGH Virtualizing raw data
|
|
85
|
+
|
|
86
|
+
Wrong:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
readonly rows = computed(() => this.data())
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Correct:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
readonly rows = computed(() => this.table.getRowModel().rows)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Raw data bypasses Table filtering, sorting, expansion, grouping, and pagination.
|
|
99
|
+
|
|
100
|
+
Source: `docs/framework/angular/guide/virtualization.md`
|
|
101
|
+
|
|
102
|
+
### HIGH Forgetting spacer and transforms
|
|
103
|
+
|
|
104
|
+
Wrong:
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
@for (item of virtualRows(); track item.key) {
|
|
108
|
+
<div>{{ rows()[item.index].id }}</div>
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Correct:
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<div [style.height.px]="totalSize()" style="position:relative">
|
|
116
|
+
<div style="position:absolute"></div>
|
|
117
|
+
</div>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Virtual computes ranges and sizes but does not apply DOM geometry, sticky CSS, or column widths.
|
|
121
|
+
|
|
122
|
+
Source: `examples/angular/virtualized-columns/src/app/app.ts`
|
|
123
|
+
|
|
124
|
+
## API Discovery
|
|
125
|
+
|
|
126
|
+
Inspect installed `@tanstack/angular-table/src`, installed `@tanstack/angular-virtual/src`, and the maintained Angular examples for current row, column, measurement, and infinite patterns.
|
|
@@ -107,13 +107,13 @@ interface FlexRenderOptions<
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
type Inputs<T> = {
|
|
110
|
-
[K in keyof T as T[K] extends InputSignal<infer
|
|
110
|
+
[K in keyof T as T[K] extends InputSignal<infer _R>
|
|
111
111
|
? K
|
|
112
112
|
: never]?: T[K] extends InputSignal<infer R> ? R : never
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
type Outputs<T> = {
|
|
116
|
-
[K in keyof T as T[K] extends OutputEmitterRef<infer
|
|
116
|
+
[K in keyof T as T[K] extends OutputEmitterRef<infer _R>
|
|
117
117
|
? K
|
|
118
118
|
: never]?: T[K] extends OutputEmitterRef<infer R>
|
|
119
119
|
? OutputEmitterRef<R>['emit']
|
package/src/flex-render/view.ts
CHANGED
package/src/helpers/cell.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface TanStackTableCellContext<
|
|
|
23
23
|
* This token is provided by the {@link TanStackTableCell} directive.
|
|
24
24
|
*/
|
|
25
25
|
export const TanStackTableCellToken = new InjectionToken<
|
|
26
|
-
TanStackTableCellContext<
|
|
26
|
+
TanStackTableCellContext<TableFeatures, RowData, CellData>['cell']
|
|
27
27
|
>('[TanStack Table] CellContext')
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -100,5 +100,9 @@ export function injectTableCellContext<
|
|
|
100
100
|
TData extends RowData,
|
|
101
101
|
TValue extends CellData,
|
|
102
102
|
>(): TanStackTableCellContext<TFeatures, TData, TValue>['cell'] {
|
|
103
|
-
return inject(TanStackTableCellToken)
|
|
103
|
+
return inject(TanStackTableCellToken) as unknown as TanStackTableCellContext<
|
|
104
|
+
TFeatures,
|
|
105
|
+
TData,
|
|
106
|
+
TValue
|
|
107
|
+
>['cell']
|
|
104
108
|
}
|
|
@@ -28,7 +28,6 @@ import type {
|
|
|
28
28
|
TableFeature,
|
|
29
29
|
TableFeatures,
|
|
30
30
|
TableOptions,
|
|
31
|
-
TableState,
|
|
32
31
|
} from '@tanstack/table-core'
|
|
33
32
|
import type { Signal, Type } from '@angular/core'
|
|
34
33
|
|
|
@@ -314,16 +313,16 @@ export type CreateTableHookResult<
|
|
|
314
313
|
THeaderComponents
|
|
315
314
|
>
|
|
316
315
|
injectTableContext: <TData extends RowData = RowData>() => Signal<
|
|
317
|
-
AngularTable<TFeatures, TData>
|
|
316
|
+
AngularTable<TFeatures, TData> & TTableComponents
|
|
318
317
|
>
|
|
319
318
|
injectTableHeaderContext: <
|
|
320
319
|
TValue extends CellData = CellData,
|
|
321
320
|
TRowData extends RowData = RowData,
|
|
322
|
-
>() => Signal<Header<TFeatures, TRowData, TValue
|
|
321
|
+
>() => Signal<Header<TFeatures, TRowData, TValue> & THeaderComponents>
|
|
323
322
|
injectTableCellContext: <
|
|
324
323
|
TValue extends CellData = CellData,
|
|
325
324
|
TRowData extends RowData = RowData,
|
|
326
|
-
>() => Signal<Cell<TFeatures, TRowData, TValue
|
|
325
|
+
>() => Signal<Cell<TFeatures, TRowData, TValue> & TCellComponents>
|
|
327
326
|
injectFlexRenderHeaderContext: <
|
|
328
327
|
TData extends RowData,
|
|
329
328
|
TValue extends CellData,
|
|
@@ -333,10 +332,7 @@ export type CreateTableHookResult<
|
|
|
333
332
|
TValue extends CellData,
|
|
334
333
|
>() => CellContext<TFeatures, TData, TValue>
|
|
335
334
|
injectAppTable: <TData extends RowData>(
|
|
336
|
-
tableOptions: () => Omit<
|
|
337
|
-
TableOptions<TFeatures, TData>,
|
|
338
|
-
'features' | 'rowModels'
|
|
339
|
-
>,
|
|
335
|
+
tableOptions: () => Omit<TableOptions<TFeatures, TData>, 'features'>,
|
|
340
336
|
) => AppAngularTable<
|
|
341
337
|
TFeatures,
|
|
342
338
|
TData,
|
|
@@ -358,7 +354,6 @@ export type CreateTableHookResult<
|
|
|
358
354
|
* ```ts
|
|
359
355
|
* const { injectAppTable, createAppColumnHelper } = createTableHook({
|
|
360
356
|
* features,
|
|
361
|
-
* rowModels: {},
|
|
362
357
|
* tableComponents: {},
|
|
363
358
|
* cellComponents: {},
|
|
364
359
|
* headerComponents: {},
|
|
@@ -387,23 +382,46 @@ export function createTableHook<
|
|
|
387
382
|
THeaderComponents
|
|
388
383
|
> {
|
|
389
384
|
function injectTableContext<TData extends RowData = RowData>(): Signal<
|
|
390
|
-
AngularTable<TFeatures, TData>
|
|
385
|
+
AngularTable<TFeatures, TData> & TTableComponents
|
|
391
386
|
> {
|
|
392
|
-
|
|
387
|
+
// `injectAppTable` Object.assign-es `tableComponents` onto the same table
|
|
388
|
+
// instance it returns (via `constructTableAPIs`), and that instance is what
|
|
389
|
+
// gets provided to DI, so this asserts the runtime shape.
|
|
390
|
+
return _injectTableContext<TFeatures, TData>() as unknown as Signal<
|
|
391
|
+
AngularTable<TFeatures, TData> & TTableComponents
|
|
392
|
+
>
|
|
393
393
|
}
|
|
394
394
|
|
|
395
395
|
function injectTableHeaderContext<
|
|
396
396
|
TValue extends CellData = CellData,
|
|
397
397
|
TRowData extends RowData = RowData,
|
|
398
|
-
>(): Signal<Header<TFeatures, TRowData, TValue
|
|
399
|
-
|
|
398
|
+
>(): Signal<Header<TFeatures, TRowData, TValue> & THeaderComponents> {
|
|
399
|
+
// `injectAppTable` Object.assign-es `headerComponents` onto the header
|
|
400
|
+
// prototype (via `assignHeaderPrototype`), so every header instance carries
|
|
401
|
+
// them. This asserts the runtime shape.
|
|
402
|
+
return _injectTableHeaderContext<
|
|
403
|
+
TFeatures,
|
|
404
|
+
TRowData,
|
|
405
|
+
TValue
|
|
406
|
+
>() as unknown as Signal<
|
|
407
|
+
Header<TFeatures, TRowData, TValue> & THeaderComponents
|
|
408
|
+
>
|
|
400
409
|
}
|
|
401
410
|
|
|
402
411
|
function injectTableCellContext<
|
|
403
412
|
TValue extends CellData = CellData,
|
|
404
413
|
TRowData extends RowData = RowData,
|
|
405
|
-
>(): Signal<Cell<TFeatures, TRowData, TValue
|
|
406
|
-
|
|
414
|
+
>(): Signal<Cell<TFeatures, TRowData, TValue> & TCellComponents> {
|
|
415
|
+
// `injectAppTable` Object.assign-es `cellComponents` onto the cell prototype
|
|
416
|
+
// (via `assignCellPrototype`), so every cell instance carries them. This
|
|
417
|
+
// asserts the runtime shape.
|
|
418
|
+
return _injectTableCellContext<
|
|
419
|
+
TFeatures,
|
|
420
|
+
TRowData,
|
|
421
|
+
TValue
|
|
422
|
+
>() as unknown as Signal<
|
|
423
|
+
Cell<TFeatures, TRowData, TValue> & TCellComponents
|
|
424
|
+
>
|
|
407
425
|
}
|
|
408
426
|
|
|
409
427
|
function injectFlexRenderHeaderContext<
|
|
@@ -420,14 +438,8 @@ export function createTableHook<
|
|
|
420
438
|
return injectFlexRenderContext<CellContext<TFeatures, TData, TValue>>()
|
|
421
439
|
}
|
|
422
440
|
|
|
423
|
-
function injectAppTable<
|
|
424
|
-
|
|
425
|
-
TSelected = TableState<TFeatures>,
|
|
426
|
-
>(
|
|
427
|
-
tableOptions: () => Omit<
|
|
428
|
-
TableOptions<TFeatures, TData>,
|
|
429
|
-
'features' | 'rowModels'
|
|
430
|
-
>,
|
|
441
|
+
function injectAppTable<TData extends RowData>(
|
|
442
|
+
tableOptions: () => Omit<TableOptions<TFeatures, TData>, 'features'>,
|
|
431
443
|
): AppAngularTable<
|
|
432
444
|
TFeatures,
|
|
433
445
|
TData,
|
|
@@ -447,7 +459,7 @@ export function createTableHook<
|
|
|
447
459
|
return footer as Header<TFeatures, TData, any> & THeaderComponents
|
|
448
460
|
}
|
|
449
461
|
|
|
450
|
-
const appTableFeatures: TableFeature
|
|
462
|
+
const appTableFeatures: TableFeature = {
|
|
451
463
|
constructTableAPIs: (table) => {
|
|
452
464
|
Object.assign(table, tableComponents, { appCell, appHeader, appFooter })
|
|
453
465
|
},
|
|
@@ -467,8 +479,14 @@ export function createTableHook<
|
|
|
467
479
|
...defaultTableOptions.features,
|
|
468
480
|
appTableFeatures,
|
|
469
481
|
},
|
|
470
|
-
} as TableOptions<TFeatures, TData>
|
|
471
|
-
}) as
|
|
482
|
+
} as unknown as TableOptions<TFeatures, TData>
|
|
483
|
+
}) as unknown as AppAngularTable<
|
|
484
|
+
TFeatures,
|
|
485
|
+
TData,
|
|
486
|
+
TTableComponents,
|
|
487
|
+
TCellComponents,
|
|
488
|
+
THeaderComponents
|
|
489
|
+
>
|
|
472
490
|
}
|
|
473
491
|
|
|
474
492
|
function createAppColumnHelper<TData extends RowData>(): AppColumnHelper<
|
|
@@ -94,6 +94,20 @@ export class FlexRenderCell<
|
|
|
94
94
|
const header = this.header()
|
|
95
95
|
const footer = this.footer()
|
|
96
96
|
if (cell) {
|
|
97
|
+
const def = cell.column.columnDef
|
|
98
|
+
const groupingCell = cell as typeof cell & {
|
|
99
|
+
getIsAggregated?: () => boolean
|
|
100
|
+
getIsPlaceholder?: () => boolean
|
|
101
|
+
}
|
|
102
|
+
const groupingDef = def as typeof def & {
|
|
103
|
+
aggregatedCell?: typeof def.cell
|
|
104
|
+
}
|
|
105
|
+
if (groupingCell.getIsAggregated?.()) {
|
|
106
|
+
return [groupingDef.aggregatedCell ?? def.cell, cell.getContext()]
|
|
107
|
+
}
|
|
108
|
+
if (groupingCell.getIsPlaceholder?.()) {
|
|
109
|
+
return [null, null]
|
|
110
|
+
}
|
|
97
111
|
return [cell.column.columnDef.cell, cell.getContext()]
|
|
98
112
|
}
|
|
99
113
|
if (header) {
|
package/src/helpers/header.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface TanStackTableHeaderContext<
|
|
|
23
23
|
* This token is provided by the {@link TanStackTableHeader} directive.
|
|
24
24
|
*/
|
|
25
25
|
export const TanStackTableHeaderToken = new InjectionToken<
|
|
26
|
-
TanStackTableHeaderContext<
|
|
26
|
+
TanStackTableHeaderContext<TableFeatures, RowData, CellData>['header']
|
|
27
27
|
>('[TanStack Table] HeaderContext')
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -95,5 +95,7 @@ export function injectTableHeaderContext<
|
|
|
95
95
|
TData extends RowData,
|
|
96
96
|
TValue extends CellData,
|
|
97
97
|
>(): TanStackTableHeaderContext<TFeatures, TData, TValue>['header'] {
|
|
98
|
-
return inject(
|
|
98
|
+
return inject(
|
|
99
|
+
TanStackTableHeaderToken,
|
|
100
|
+
) as unknown as TanStackTableHeaderContext<TFeatures, TData, TValue>['header']
|
|
99
101
|
}
|