@tanstack/angular-query-experimental 5.103.2 → 5.104.0
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/dist/create-base-query.d.mts +168 -0
- package/dist/create-base-query.d.mts.map +1 -0
- package/dist/create-base-query.mjs +91 -0
- package/dist/create-base-query.mjs.map +1 -0
- package/dist/devtools/index.d.mts +3 -0
- package/dist/devtools/index.mjs +2 -0
- package/dist/devtools/production/index.d.mts +4 -0
- package/dist/devtools/production/index.mjs +2 -0
- package/dist/devtools/stub.d.mts +5 -0
- package/dist/devtools/stub.d.mts.map +1 -0
- package/dist/devtools/stub.mjs +9 -0
- package/dist/devtools/stub.mjs.map +1 -0
- package/dist/devtools/types.d.mts +106 -0
- package/dist/devtools/types.d.mts.map +1 -0
- package/dist/devtools/types.mjs +1 -0
- package/{devtools/with-devtools.d.ts → dist/devtools/with-devtools.d.mts} +4 -1
- package/dist/devtools/with-devtools.d.mts.map +1 -0
- package/dist/devtools/with-devtools.mjs +110 -0
- package/dist/devtools/with-devtools.mjs.map +1 -0
- package/dist/devtools-panel/index.d.mts +3 -0
- package/dist/devtools-panel/index.mjs +2 -0
- package/{devtools-panel/inject-devtools-panel.d.ts → dist/devtools-panel/inject-devtools-panel.d.mts} +4 -1
- package/dist/devtools-panel/inject-devtools-panel.d.mts.map +1 -0
- package/dist/devtools-panel/inject-devtools-panel.mjs +67 -0
- package/dist/devtools-panel/inject-devtools-panel.mjs.map +1 -0
- package/dist/devtools-panel/production/index.d.mts +4 -0
- package/dist/devtools-panel/production/index.mjs +2 -0
- package/dist/devtools-panel/stub.d.mts +5 -0
- package/dist/devtools-panel/stub.d.mts.map +1 -0
- package/dist/devtools-panel/stub.mjs +7 -0
- package/dist/devtools-panel/stub.mjs.map +1 -0
- package/dist/devtools-panel/types.d.mts +51 -0
- package/dist/devtools-panel/types.d.mts.map +1 -0
- package/dist/devtools-panel/types.mjs +1 -0
- package/dist/index.d.mts +16 -0
- package/{index.mjs → dist/index.mjs} +8 -21
- package/dist/index.mjs.map +1 -0
- package/{infinite-query-options.d.ts → dist/infinite-query-options.d.mts} +28 -25
- package/dist/infinite-query-options.d.mts.map +1 -0
- package/dist/infinite-query-options.mjs +8 -0
- package/dist/infinite-query-options.mjs.map +1 -0
- package/{inject-infinite-query.d.ts → dist/inject-infinite-query.d.mts} +13 -10
- package/dist/inject-infinite-query.d.mts.map +1 -0
- package/dist/inject-infinite-query.mjs +13 -0
- package/dist/inject-infinite-query.mjs.map +1 -0
- package/{inject-is-fetching.d.ts → dist/inject-is-fetching.d.mts} +11 -8
- package/dist/inject-is-fetching.d.mts.map +1 -0
- package/dist/inject-is-fetching.mjs +69 -0
- package/dist/inject-is-fetching.mjs.map +1 -0
- package/{inject-is-mutating.d.ts → dist/inject-is-mutating.d.mts} +11 -8
- package/dist/inject-is-mutating.d.mts.map +1 -0
- package/dist/inject-is-mutating.mjs +52 -0
- package/dist/inject-is-mutating.mjs.map +1 -0
- package/{inject-is-restoring.d.ts → dist/inject-is-restoring.d.mts} +10 -8
- package/dist/inject-is-restoring.d.mts.map +1 -0
- package/dist/inject-is-restoring.mjs +35 -0
- package/dist/inject-is-restoring.mjs.map +1 -0
- package/{inject-mutation-state.d.ts → dist/inject-mutation-state.d.mts} +13 -11
- package/dist/inject-mutation-state.d.mts.map +1 -0
- package/dist/inject-mutation-state.mjs +103 -0
- package/dist/inject-mutation-state.mjs.map +1 -0
- package/{inject-mutation.d.ts → dist/inject-mutation.d.mts} +12 -9
- package/dist/inject-mutation.d.mts.map +1 -0
- package/dist/inject-mutation.mjs +226 -0
- package/dist/inject-mutation.mjs.map +1 -0
- package/dist/inject-queries-experimental/index.d.mts +2 -0
- package/dist/inject-queries-experimental/index.mjs +2 -0
- package/{inject-queries.d.ts → dist/inject-queries.d.mts} +33 -53
- package/dist/inject-queries.d.mts.map +1 -0
- package/dist/inject-queries.mjs +203 -0
- package/dist/inject-queries.mjs.map +1 -0
- package/{inject-query-client.d.ts → dist/inject-query-client.d.mts} +6 -3
- package/dist/inject-query-client.d.mts.map +1 -0
- package/dist/inject-query-client.mjs +23 -0
- package/dist/inject-query-client.mjs.map +1 -0
- package/{inject-query.d.ts → dist/inject-query.d.mts} +13 -10
- package/dist/inject-query.d.mts.map +1 -0
- package/dist/inject-query.mjs +12 -0
- package/dist/inject-query.mjs.map +1 -0
- package/{mutation-options.d.ts → dist/mutation-options.d.mts} +5 -2
- package/dist/mutation-options.d.mts.map +1 -0
- package/dist/mutation-options.mjs +8 -0
- package/dist/mutation-options.mjs.map +1 -0
- package/dist/pending-tasks-compat.d.mts +9 -0
- package/dist/pending-tasks-compat.d.mts.map +1 -0
- package/dist/pending-tasks-compat.mjs +13 -0
- package/dist/pending-tasks-compat.mjs.map +1 -0
- package/{providers.d.ts → dist/providers.d.mts} +7 -5
- package/dist/providers.d.mts.map +1 -0
- package/dist/providers.mjs +124 -0
- package/dist/providers.mjs.map +1 -0
- package/{query-options.d.ts → dist/query-options.d.mts} +34 -31
- package/dist/query-options.d.mts.map +1 -0
- package/dist/query-options.mjs +8 -0
- package/dist/query-options.mjs.map +1 -0
- package/{signal-proxy.d.ts → dist/signal-proxy.d.mts} +5 -4
- package/dist/signal-proxy.d.mts.map +1 -0
- package/dist/signal-proxy.mjs +35 -0
- package/dist/signal-proxy.mjs.map +1 -0
- package/{types.d.ts → dist/types.d.mts} +24 -26
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +6 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +50 -20
- package/src/__tests__/infinite-query-options.test-d.ts +314 -0
- package/src/__tests__/infinite-query-options.test.ts +19 -0
- package/src/__tests__/inject-devtools-panel.test.ts +196 -0
- package/src/__tests__/inject-infinite-query.test-d.ts +66 -0
- package/src/__tests__/inject-infinite-query.test.ts +227 -0
- package/src/__tests__/inject-is-fetching.test.ts +108 -0
- package/src/__tests__/inject-is-mutating.test.ts +113 -0
- package/src/__tests__/inject-is-restoring.test.ts +93 -0
- package/src/__tests__/inject-mutation-state.test-d.ts +22 -0
- package/src/__tests__/inject-mutation-state.test.ts +198 -0
- package/src/__tests__/inject-mutation.test-d.ts +158 -0
- package/src/__tests__/inject-mutation.test.ts +1161 -0
- package/src/__tests__/inject-queries.test-d.ts +196 -0
- package/src/__tests__/inject-queries.test.ts +240 -0
- package/src/__tests__/inject-query.test-d.ts +357 -0
- package/src/__tests__/inject-query.test.ts +700 -0
- package/src/__tests__/mutation-options.test-d.ts +234 -0
- package/src/__tests__/mutation-options.test.ts +344 -0
- package/src/__tests__/pending-tasks.test.ts +644 -0
- package/src/__tests__/provide-query-client.test.ts +38 -0
- package/src/__tests__/provide-tanstack-query.test.ts +38 -0
- package/src/__tests__/query-options.test-d.ts +262 -0
- package/src/__tests__/query-options.test.ts +14 -0
- package/src/__tests__/signal-proxy.test.ts +27 -0
- package/src/__tests__/test-utils.ts +77 -0
- package/src/__tests__/with-devtools.test.ts +674 -0
- package/src/create-base-query.ts +176 -0
- package/src/devtools/index.ts +8 -0
- package/src/devtools/production/index.ts +1 -0
- package/src/devtools/stub.ts +7 -0
- package/src/devtools/types.ts +116 -0
- package/src/devtools/with-devtools.ts +182 -0
- package/src/devtools-panel/index.ts +8 -0
- package/src/devtools-panel/inject-devtools-panel.ts +110 -0
- package/src/devtools-panel/production/index.ts +1 -0
- package/src/devtools-panel/stub.ts +7 -0
- package/src/devtools-panel/types.ts +57 -0
- package/src/index.ts +60 -0
- package/src/infinite-query-options.ts +354 -0
- package/src/inject-infinite-query.ts +296 -0
- package/src/inject-is-fetching.ts +97 -0
- package/src/inject-is-mutating.ts +80 -0
- package/src/inject-is-restoring.ts +53 -0
- package/src/inject-mutation-state.ts +167 -0
- package/src/inject-mutation.ts +328 -0
- package/src/inject-queries-experimental/index.ts +1 -0
- package/src/inject-queries.ts +480 -0
- package/src/inject-query-client.ts +22 -0
- package/src/inject-query.ts +208 -0
- package/src/mutation-options.ts +121 -0
- package/src/pending-tasks-compat.ts +27 -0
- package/src/providers.ts +170 -0
- package/src/query-options.ts +292 -0
- package/src/signal-proxy.ts +46 -0
- package/src/types.ts +429 -0
- package/create-base-query.d.ts +0 -164
- package/create-base-query.mjs +0 -99
- package/create-base-query.mjs.map +0 -1
- package/devtools/index.d.ts +0 -2
- package/devtools/index.mjs +0 -5
- package/devtools/index.mjs.map +0 -1
- package/devtools/production/index.d.ts +0 -1
- package/devtools/stub.d.ts +0 -2
- package/devtools/stub.mjs +0 -8
- package/devtools/stub.mjs.map +0 -1
- package/devtools/types.d.ts +0 -103
- package/devtools/with-devtools.mjs +0 -106
- package/devtools/with-devtools.mjs.map +0 -1
- package/devtools-panel/index.d.ts +0 -2
- package/devtools-panel/index.mjs +0 -5
- package/devtools-panel/index.mjs.map +0 -1
- package/devtools-panel/inject-devtools-panel.mjs +0 -72
- package/devtools-panel/inject-devtools-panel.mjs.map +0 -1
- package/devtools-panel/production/index.d.ts +0 -1
- package/devtools-panel/stub.d.ts +0 -2
- package/devtools-panel/stub.mjs +0 -8
- package/devtools-panel/stub.mjs.map +0 -1
- package/devtools-panel/types.d.ts +0 -48
- package/index.d.ts +0 -25
- package/index.mjs.map +0 -1
- package/infinite-query-options.mjs +0 -7
- package/infinite-query-options.mjs.map +0 -1
- package/inject-infinite-query.mjs +0 -18
- package/inject-infinite-query.mjs.map +0 -1
- package/inject-is-fetching.mjs +0 -31
- package/inject-is-fetching.mjs.map +0 -1
- package/inject-is-mutating.mjs +0 -31
- package/inject-is-mutating.mjs.map +0 -1
- package/inject-is-restoring.mjs +0 -21
- package/inject-is-restoring.mjs.map +0 -1
- package/inject-mutation-state.mjs +0 -51
- package/inject-mutation-state.mjs.map +0 -1
- package/inject-mutation.mjs +0 -94
- package/inject-mutation.mjs.map +0 -1
- package/inject-queries-experimental/index.d.ts +0 -1
- package/inject-queries-experimental/index.mjs +0 -5
- package/inject-queries-experimental/index.mjs.map +0 -1
- package/inject-queries.mjs +0 -82
- package/inject-queries.mjs.map +0 -1
- package/inject-query-client.mjs +0 -9
- package/inject-query-client.mjs.map +0 -1
- package/inject-query.mjs +0 -14
- package/inject-query.mjs.map +0 -1
- package/mutation-options.mjs +0 -7
- package/mutation-options.mjs.map +0 -1
- package/pending-tasks-compat.d.ts +0 -7
- package/pending-tasks-compat.mjs +0 -19
- package/pending-tasks-compat.mjs.map +0 -1
- package/providers.mjs +0 -32
- package/providers.mjs.map +0 -1
- package/query-options.mjs +0 -7
- package/query-options.mjs.map +0 -1
- package/signal-proxy.mjs +0 -29
- package/signal-proxy.mjs.map +0 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { QueryObserver } from '@tanstack/query-core'
|
|
2
|
+
import {
|
|
3
|
+
Injector,
|
|
4
|
+
assertInInjectionContext,
|
|
5
|
+
inject,
|
|
6
|
+
runInInjectionContext,
|
|
7
|
+
} from '@angular/core'
|
|
8
|
+
import { createBaseQuery } from './create-base-query'
|
|
9
|
+
import type { DefaultError, QueryKey } from '@tanstack/query-core'
|
|
10
|
+
import type {
|
|
11
|
+
CreateQueryOptions,
|
|
12
|
+
CreateQueryResult,
|
|
13
|
+
DefinedCreateQueryResult,
|
|
14
|
+
} from './types'
|
|
15
|
+
import type {
|
|
16
|
+
DefinedInitialDataOptions,
|
|
17
|
+
UndefinedInitialDataOptions,
|
|
18
|
+
} from './query-options'
|
|
19
|
+
|
|
20
|
+
export interface InjectQueryOptions {
|
|
21
|
+
/**
|
|
22
|
+
* The `Injector` in which to create the query.
|
|
23
|
+
*
|
|
24
|
+
* If this is not provided, the current injection context will be used instead (via `inject`).
|
|
25
|
+
*/
|
|
26
|
+
injector?: Injector
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* This overload is selected when `initialData` is set on the options returned by `injectQueryFn`, so the
|
|
31
|
+
* resulting `data` signal is never `undefined` (unless a `select` changes `TData` to include `undefined`).
|
|
32
|
+
*
|
|
33
|
+
* @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries
|
|
34
|
+
* @see {@link queryOptions} to share these options between `injectQuery` and imperative APIs like
|
|
35
|
+
* `queryClient.fetchQuery`.
|
|
36
|
+
* @param injectQueryFn - A function returning the {@link DefinedInitialDataOptions} to use — everything you
|
|
37
|
+
* can pass to `injectQuery`, with `initialData` set. Similar to `computed` from Angular, this function runs
|
|
38
|
+
* in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query.
|
|
39
|
+
* @param options - Additional configuration
|
|
40
|
+
* @returns The query result, typed so that `data` is never `undefined` (unless a `select` changes `TData` to
|
|
41
|
+
* include `undefined`).
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```angular-ts
|
|
45
|
+
* @Component({
|
|
46
|
+
* selector: 'posts',
|
|
47
|
+
* template: `
|
|
48
|
+
* <!-- `postsQuery.data()` is `Post[]`, never `undefined`, thanks to `initialData` — even if a
|
|
49
|
+
* refetch fails, so the list stays visible alongside the error. -->
|
|
50
|
+
* @if (postsQuery.isError()) {
|
|
51
|
+
* <span>Error: {{ postsQuery.error()?.message }}</span>
|
|
52
|
+
* }
|
|
53
|
+
* <ul>
|
|
54
|
+
* @for (post of postsQuery.data(); track post.id) {
|
|
55
|
+
* <li>{{ post.title }}</li>
|
|
56
|
+
* }
|
|
57
|
+
* </ul>
|
|
58
|
+
* `,
|
|
59
|
+
* })
|
|
60
|
+
* export class Posts {
|
|
61
|
+
* readonly postsQuery = injectQuery(() => ({
|
|
62
|
+
* queryKey: ['posts'],
|
|
63
|
+
* queryFn: fetchPosts,
|
|
64
|
+
* initialData: [],
|
|
65
|
+
* }))
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export function injectQuery<
|
|
70
|
+
TQueryFnData = unknown,
|
|
71
|
+
TError = DefaultError,
|
|
72
|
+
TData = TQueryFnData,
|
|
73
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
74
|
+
>(
|
|
75
|
+
injectQueryFn: () => DefinedInitialDataOptions<
|
|
76
|
+
TQueryFnData,
|
|
77
|
+
TError,
|
|
78
|
+
TData,
|
|
79
|
+
TQueryKey
|
|
80
|
+
>,
|
|
81
|
+
options?: InjectQueryOptions,
|
|
82
|
+
): DefinedCreateQueryResult<TData, TError>
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key.
|
|
86
|
+
*
|
|
87
|
+
* @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries
|
|
88
|
+
* @see {@link queryOptions} to share these options between `injectQuery` and imperative APIs like
|
|
89
|
+
* `queryClient.fetchQuery`.
|
|
90
|
+
* @param injectQueryFn - A function returning the {@link UndefinedInitialDataOptions} to use — everything
|
|
91
|
+
* you can pass to `injectQuery`. Similar to `computed` from Angular, this function runs in the reactive
|
|
92
|
+
* context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query.
|
|
93
|
+
* @param options - Additional configuration
|
|
94
|
+
* @returns The query result. `status()` is `'pending'` if there is no cached data to display, `'error'` if
|
|
95
|
+
* the last fetch attempt failed, or `'success'` if the query has data to display. `isPending`/`isSuccess`/
|
|
96
|
+
* `isError` are type-guard methods for convenience.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```angular-ts
|
|
100
|
+
* @Component({
|
|
101
|
+
* selector: 'posts',
|
|
102
|
+
* template: `
|
|
103
|
+
* @if (postsQuery.isPending()) {
|
|
104
|
+
* Loading...
|
|
105
|
+
* } @else if (postsQuery.isError()) {
|
|
106
|
+
* <span>Error: {{ postsQuery.error()?.message }}</span>
|
|
107
|
+
* } @else {
|
|
108
|
+
* <ul>
|
|
109
|
+
* @for (post of postsQuery.data(); track post.id) {
|
|
110
|
+
* <li>{{ post.title }}</li>
|
|
111
|
+
* }
|
|
112
|
+
* </ul>
|
|
113
|
+
* }
|
|
114
|
+
* `,
|
|
115
|
+
* })
|
|
116
|
+
* export class Posts {
|
|
117
|
+
* readonly postsQuery = injectQuery(() => ({
|
|
118
|
+
* queryKey: ['posts'],
|
|
119
|
+
* queryFn: fetchPosts,
|
|
120
|
+
* }))
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* Similar to `computed` from Angular, the function passed to `injectQuery` runs in the reactive context. In
|
|
126
|
+
* the example below, the query is automatically enabled and executed when the filter signal changes to a
|
|
127
|
+
* truthy value. When the filter signal changes back to a falsy value, the query is disabled.
|
|
128
|
+
* ```angular-ts
|
|
129
|
+
* @Component({
|
|
130
|
+
* selector: 'posts',
|
|
131
|
+
* template: `
|
|
132
|
+
* <input [ngModel]="filter()" (ngModelChange)="filter.set($event)" />
|
|
133
|
+
* @if (postsQuery.isPending()) {
|
|
134
|
+
* Loading...
|
|
135
|
+
* } @else if (postsQuery.isError()) {
|
|
136
|
+
* <span>Error: {{ postsQuery.error()?.message }}</span>
|
|
137
|
+
* } @else {
|
|
138
|
+
* <ul>
|
|
139
|
+
* @for (post of postsQuery.data(); track post.id) {
|
|
140
|
+
* <li>{{ post.title }}</li>
|
|
141
|
+
* }
|
|
142
|
+
* </ul>
|
|
143
|
+
* }
|
|
144
|
+
* `,
|
|
145
|
+
* })
|
|
146
|
+
* export class Posts {
|
|
147
|
+
* readonly filter = signal('')
|
|
148
|
+
*
|
|
149
|
+
* readonly postsQuery = injectQuery(() => ({
|
|
150
|
+
* queryKey: ['posts', this.filter()],
|
|
151
|
+
* queryFn: () => fetchPosts(this.filter()),
|
|
152
|
+
* // Signals can be combined with expressions
|
|
153
|
+
* enabled: !!this.filter(),
|
|
154
|
+
* }))
|
|
155
|
+
* }
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export function injectQuery<
|
|
159
|
+
TQueryFnData = unknown,
|
|
160
|
+
TError = DefaultError,
|
|
161
|
+
TData = TQueryFnData,
|
|
162
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
163
|
+
>(
|
|
164
|
+
injectQueryFn: () => UndefinedInitialDataOptions<
|
|
165
|
+
TQueryFnData,
|
|
166
|
+
TError,
|
|
167
|
+
TData,
|
|
168
|
+
TQueryKey
|
|
169
|
+
>,
|
|
170
|
+
options?: InjectQueryOptions,
|
|
171
|
+
): CreateQueryResult<TData, TError>
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* This overload accepts the general {@link CreateQueryOptions} shape rather than the `initialData`-aware
|
|
175
|
+
* overloads above, so whether `data` is defined can't be inferred from the call site — useful when wrapping
|
|
176
|
+
* `injectQuery` in your own helper function that forwards caller-provided options.
|
|
177
|
+
*
|
|
178
|
+
* @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries
|
|
179
|
+
* @param injectQueryFn - A function that returns query options. Similar to `computed` from Angular, this
|
|
180
|
+
* function runs in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive
|
|
181
|
+
* the query.
|
|
182
|
+
* @param options - Additional configuration
|
|
183
|
+
* @returns The query result.
|
|
184
|
+
*/
|
|
185
|
+
export function injectQuery<
|
|
186
|
+
TQueryFnData = unknown,
|
|
187
|
+
TError = DefaultError,
|
|
188
|
+
TData = TQueryFnData,
|
|
189
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
190
|
+
>(
|
|
191
|
+
injectQueryFn: () => CreateQueryOptions<
|
|
192
|
+
TQueryFnData,
|
|
193
|
+
TError,
|
|
194
|
+
TData,
|
|
195
|
+
TQueryKey
|
|
196
|
+
>,
|
|
197
|
+
options?: InjectQueryOptions,
|
|
198
|
+
): CreateQueryResult<TData, TError>
|
|
199
|
+
|
|
200
|
+
export function injectQuery(
|
|
201
|
+
injectQueryFn: () => CreateQueryOptions,
|
|
202
|
+
options?: InjectQueryOptions,
|
|
203
|
+
) {
|
|
204
|
+
!options?.injector && assertInInjectionContext(injectQuery)
|
|
205
|
+
return runInInjectionContext(options?.injector ?? inject(Injector), () =>
|
|
206
|
+
createBaseQuery(injectQueryFn, QueryObserver),
|
|
207
|
+
) as unknown as CreateQueryResult
|
|
208
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { DefaultError, WithRequired } from '@tanstack/query-core'
|
|
2
|
+
import type { CreateMutationOptions } from './types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* You can generally pass everything to `mutationOptions` that you can also pass to `injectMutation`. A
|
|
6
|
+
* `mutationKey` is required on this overload so the mutation can be looked up later, e.g. with
|
|
7
|
+
* `injectMutationState`.
|
|
8
|
+
*
|
|
9
|
+
* @see {@link injectMutation} to run the mutation these options describe.
|
|
10
|
+
* @param options - The mutation options to use, identical to what you'd pass to `injectMutation`, with a
|
|
11
|
+
* required `mutationKey`.
|
|
12
|
+
* @returns The same options object, unchanged.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* Looking the mutation up elsewhere via its `mutationKey`, e.g. for a global "saving…" indicator:
|
|
16
|
+
* ```angular-ts
|
|
17
|
+
* import { mutationOptions, injectMutationState } from '@tanstack/angular-query-experimental'
|
|
18
|
+
*
|
|
19
|
+
* const createPostOptions = mutationOptions({
|
|
20
|
+
* mutationKey: ['posts', 'create'],
|
|
21
|
+
* mutationFn: createPost,
|
|
22
|
+
* })
|
|
23
|
+
*
|
|
24
|
+
* @Component({
|
|
25
|
+
* selector: 'saving-indicator',
|
|
26
|
+
* template: `
|
|
27
|
+
* @if (isCreatingPost()) {
|
|
28
|
+
* <span>Saving…</span>
|
|
29
|
+
* }
|
|
30
|
+
* `,
|
|
31
|
+
* })
|
|
32
|
+
* export class SavingIndicator {
|
|
33
|
+
* readonly #pendingCreates = injectMutationState(() => ({
|
|
34
|
+
* filters: { mutationKey: createPostOptions.mutationKey, status: 'pending' },
|
|
35
|
+
* }))
|
|
36
|
+
* readonly isCreatingPost = computed(() => this.#pendingCreates().length > 0)
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function mutationOptions<
|
|
41
|
+
TData = unknown,
|
|
42
|
+
TError = DefaultError,
|
|
43
|
+
TVariables = void,
|
|
44
|
+
TOnMutateResult = unknown,
|
|
45
|
+
>(
|
|
46
|
+
options: WithRequired<
|
|
47
|
+
CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
|
48
|
+
'mutationKey'
|
|
49
|
+
>,
|
|
50
|
+
): WithRequired<
|
|
51
|
+
CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
|
52
|
+
'mutationKey'
|
|
53
|
+
>
|
|
54
|
+
/**
|
|
55
|
+
* You can generally pass everything to `mutationOptions` that you can also pass to `injectMutation`. No
|
|
56
|
+
* `mutationKey` is required on this overload — use this when you don't need to target the mutation via a
|
|
57
|
+
* `mutationKey` filter later (e.g. with `injectMutationState`); it can still be observed through other
|
|
58
|
+
* filters, such as `status`.
|
|
59
|
+
*
|
|
60
|
+
* @see {@link injectMutation} to run the mutation these options describe.
|
|
61
|
+
* @param options - The mutation options to use, identical to what you'd pass to `injectMutation`, without a
|
|
62
|
+
* `mutationKey`.
|
|
63
|
+
* @returns The same options object, unchanged.
|
|
64
|
+
* @remarks See the other overload's example for looking a mutation up via `injectMutationState`.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* Sharing options across services, so `QueriesService` stays the single place a mutation is defined:
|
|
68
|
+
* ```angular-ts
|
|
69
|
+
* import { mutationOptions, injectMutation } from '@tanstack/angular-query-experimental'
|
|
70
|
+
*
|
|
71
|
+
* @Injectable({ providedIn: 'root' })
|
|
72
|
+
* export class QueriesService {
|
|
73
|
+
* readonly #queryClient = inject(QueryClient)
|
|
74
|
+
*
|
|
75
|
+
* updatePost(id: number) {
|
|
76
|
+
* return mutationOptions({
|
|
77
|
+
* mutationFn: (post: Partial<Post>) => putPost(id, post),
|
|
78
|
+
* onSuccess: (newPost) => this.#queryClient.setQueryData(['posts', id], newPost),
|
|
79
|
+
* })
|
|
80
|
+
* }
|
|
81
|
+
* }
|
|
82
|
+
*
|
|
83
|
+
* @Component({
|
|
84
|
+
* selector: 'post',
|
|
85
|
+
* template: `<button (click)="save()">Save</button>`,
|
|
86
|
+
* })
|
|
87
|
+
* export class Post {
|
|
88
|
+
* readonly queries = inject(QueriesService)
|
|
89
|
+
* readonly id = signal(0)
|
|
90
|
+
* readonly updatePostMutation = injectMutation(() => this.queries.updatePost(this.id()))
|
|
91
|
+
*
|
|
92
|
+
* save() {
|
|
93
|
+
* this.updatePostMutation.mutate({ title: 'New Title' })
|
|
94
|
+
* }
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export function mutationOptions<
|
|
99
|
+
TData = unknown,
|
|
100
|
+
TError = DefaultError,
|
|
101
|
+
TVariables = void,
|
|
102
|
+
TOnMutateResult = unknown,
|
|
103
|
+
>(
|
|
104
|
+
options: Omit<
|
|
105
|
+
CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
|
106
|
+
'mutationKey'
|
|
107
|
+
>,
|
|
108
|
+
): Omit<
|
|
109
|
+
CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
|
110
|
+
'mutationKey'
|
|
111
|
+
>
|
|
112
|
+
export function mutationOptions<
|
|
113
|
+
TData = unknown,
|
|
114
|
+
TError = DefaultError,
|
|
115
|
+
TVariables = void,
|
|
116
|
+
TOnMutateResult = unknown,
|
|
117
|
+
>(
|
|
118
|
+
options: CreateMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
|
119
|
+
): CreateMutationOptions<TData, TError, TVariables, TOnMutateResult> {
|
|
120
|
+
return options
|
|
121
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { InjectionToken, inject } from '@angular/core'
|
|
2
|
+
import * as ng from '@angular/core'
|
|
3
|
+
import { noop } from '@tanstack/query-core'
|
|
4
|
+
|
|
5
|
+
type PendingTasksCompat = { add: () => PendingTaskRef }
|
|
6
|
+
|
|
7
|
+
export type PendingTaskRef = () => void
|
|
8
|
+
|
|
9
|
+
export const PENDING_TASKS = new InjectionToken<PendingTasksCompat>(
|
|
10
|
+
'PENDING_TASKS',
|
|
11
|
+
{
|
|
12
|
+
factory: (): PendingTasksCompat => {
|
|
13
|
+
// Access via Reflect so bundlers stay quiet when the token is absent (Angular < 19).
|
|
14
|
+
const token = Reflect.get(ng, 'PendingTasks') as unknown as
|
|
15
|
+
Parameters<typeof inject>[0] | undefined
|
|
16
|
+
|
|
17
|
+
const svc: PendingTasksCompat | null = token
|
|
18
|
+
? (inject(token, { optional: true }) as PendingTasksCompat | null)
|
|
19
|
+
: null
|
|
20
|
+
|
|
21
|
+
// Without PendingTasks we fall back to a stable no-op shim.
|
|
22
|
+
return {
|
|
23
|
+
add: svc ? () => svc.add() : () => noop,
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
)
|
package/src/providers.ts
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { DestroyRef, InjectionToken, inject } from '@angular/core'
|
|
2
|
+
import { QueryClient } from '@tanstack/query-core'
|
|
3
|
+
import type { Provider } from '@angular/core'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Usually {@link provideTanStackQuery} is used once to set up TanStack Query and the
|
|
7
|
+
* [`QueryClient`](https://tanstack.com/query/latest/docs/reference/QueryClient) for the entire application —
|
|
8
|
+
* it calls `provideQueryClient` internally. Use `provideQueryClient` directly to provide a different
|
|
9
|
+
* `QueryClient` instance for part of the application, or for unit testing.
|
|
10
|
+
* @param queryClient - A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`.
|
|
11
|
+
* @returns A provider object that can be used to provide the `QueryClient` instance.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* Providing a test-only `QueryClient` in a component test, without wiring up `provideTanStackQuery`'s other
|
|
15
|
+
* defaults:
|
|
16
|
+
* ```ts
|
|
17
|
+
* TestBed.configureTestingModule({
|
|
18
|
+
* providers: [provideQueryClient(new QueryClient())],
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function provideQueryClient(
|
|
23
|
+
queryClient: QueryClient | InjectionToken<QueryClient>,
|
|
24
|
+
): Provider {
|
|
25
|
+
return {
|
|
26
|
+
provide: QueryClient,
|
|
27
|
+
useFactory: () => {
|
|
28
|
+
const client =
|
|
29
|
+
queryClient instanceof InjectionToken
|
|
30
|
+
? inject(queryClient)
|
|
31
|
+
: queryClient
|
|
32
|
+
// Unmount the query client on injector destroy
|
|
33
|
+
inject(DestroyRef).onDestroy(() => client.unmount())
|
|
34
|
+
client.mount()
|
|
35
|
+
return client
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sets up providers necessary to enable TanStack Query functionality for Angular applications. Allows
|
|
42
|
+
* configuring a `QueryClient` and optional features such as developer tools.
|
|
43
|
+
*
|
|
44
|
+
* @see https://tanstack.com/query/v5/docs/framework/angular/quick-start
|
|
45
|
+
* @see {@link withDevtools}
|
|
46
|
+
* @param queryClient - A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`.
|
|
47
|
+
* @param features - Optional features to configure additional Query functionality.
|
|
48
|
+
* @returns A set of providers to set up TanStack Query.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental'
|
|
53
|
+
*
|
|
54
|
+
* bootstrapApplication(AppComponent, {
|
|
55
|
+
* providers: [provideTanStackQuery(new QueryClient())],
|
|
56
|
+
* })
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* The same, in an `NgModule`-based application:
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental'
|
|
63
|
+
*
|
|
64
|
+
* @NgModule({
|
|
65
|
+
* declarations: [AppComponent],
|
|
66
|
+
* imports: [BrowserModule],
|
|
67
|
+
* providers: [provideTanStackQuery(new QueryClient())],
|
|
68
|
+
* bootstrap: [AppComponent],
|
|
69
|
+
* })
|
|
70
|
+
* export class AppModule {}
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* Enabling optional developer tools by adding `withDevtools` — by default, the tools are then loaded when
|
|
75
|
+
* your app is in development mode:
|
|
76
|
+
* ```ts
|
|
77
|
+
* import {
|
|
78
|
+
* provideTanStackQuery,
|
|
79
|
+
* withDevtools,
|
|
80
|
+
* QueryClient,
|
|
81
|
+
* } from '@tanstack/angular-query-experimental'
|
|
82
|
+
*
|
|
83
|
+
* bootstrapApplication(AppComponent, {
|
|
84
|
+
* providers: [provideTanStackQuery(new QueryClient(), withDevtools())],
|
|
85
|
+
* })
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* Using an `InjectionToken` for the `QueryClient` — an advanced optimization that lets TanStack Query be
|
|
90
|
+
* absent from the main application bundle, useful for including it on lazy-loaded routes only while still
|
|
91
|
+
* sharing a `QueryClient`. This is a small optimization; for most applications it's preferable to provide
|
|
92
|
+
* the `QueryClient` in the main application config, as in the examples above:
|
|
93
|
+
* ```ts
|
|
94
|
+
* export const MY_QUERY_CLIENT = new InjectionToken('', {
|
|
95
|
+
* factory: () => new QueryClient(),
|
|
96
|
+
* })
|
|
97
|
+
*
|
|
98
|
+
* // In a lazy loaded route or lazy loaded component's providers array:
|
|
99
|
+
* providers: [provideTanStackQuery(MY_QUERY_CLIENT)]
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export function provideTanStackQuery(
|
|
103
|
+
queryClient: QueryClient | InjectionToken<QueryClient>,
|
|
104
|
+
...features: Array<QueryFeatures>
|
|
105
|
+
): Array<Provider> {
|
|
106
|
+
return [
|
|
107
|
+
provideQueryClient(queryClient),
|
|
108
|
+
features.map((feature) => feature.ɵproviders),
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Sets up providers necessary to enable TanStack Query functionality for Angular applications.
|
|
114
|
+
*
|
|
115
|
+
* Allows configuring a `QueryClient`.
|
|
116
|
+
* @see https://tanstack.com/query/v5/docs/framework/angular/quick-start
|
|
117
|
+
* @param queryClient - A `QueryClient` instance.
|
|
118
|
+
* @returns A set of providers to set up TanStack Query.
|
|
119
|
+
* @deprecated Use `provideTanStackQuery` instead.
|
|
120
|
+
*/
|
|
121
|
+
export function provideAngularQuery(queryClient: QueryClient): Array<Provider> {
|
|
122
|
+
return provideTanStackQuery(queryClient)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const queryFeatures = ['Devtools', 'PersistQueryClient'] as const
|
|
126
|
+
|
|
127
|
+
type QueryFeatureKind = (typeof queryFeatures)[number]
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Helper type to represent a Query feature.
|
|
131
|
+
*/
|
|
132
|
+
export interface QueryFeature<TFeatureKind extends QueryFeatureKind> {
|
|
133
|
+
ɵkind: TFeatureKind
|
|
134
|
+
ɵproviders: Array<Provider>
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Helper function to create an object that represents a Query feature.
|
|
139
|
+
* @param kind - The kind of feature, e.g. `'Devtools'`.
|
|
140
|
+
* @param providers - The Angular providers this feature contributes to `provideTanStackQuery`.
|
|
141
|
+
* @returns A Query feature.
|
|
142
|
+
*/
|
|
143
|
+
export function queryFeature<TFeatureKind extends QueryFeatureKind>(
|
|
144
|
+
kind: TFeatureKind,
|
|
145
|
+
providers: Array<Provider>,
|
|
146
|
+
): QueryFeature<TFeatureKind> {
|
|
147
|
+
return { ɵkind: kind, ɵproviders: providers }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* A type alias that represents a feature which enables developer tools.
|
|
152
|
+
* The type is used to describe the return value of the `withDevtools` function.
|
|
153
|
+
* @see {@link withDevtools}
|
|
154
|
+
*/
|
|
155
|
+
export type DevtoolsFeature = QueryFeature<'Devtools'>
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A type alias that represents a feature which enables persistence.
|
|
159
|
+
* The type is used to describe the return value of the `withPersistQueryClient` function.
|
|
160
|
+
*/
|
|
161
|
+
export type PersistQueryClientFeature = QueryFeature<'PersistQueryClient'>
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* A type alias that represents all Query features available for use with `provideTanStackQuery`.
|
|
165
|
+
* Features can be enabled by adding special functions to the `provideTanStackQuery` call.
|
|
166
|
+
* See documentation for each symbol to find corresponding function name. See also `provideTanStackQuery`
|
|
167
|
+
* documentation on how to use those functions.
|
|
168
|
+
* @see {@link provideTanStackQuery}
|
|
169
|
+
*/
|
|
170
|
+
export type QueryFeatures = DevtoolsFeature | PersistQueryClientFeature
|