Skip to content

Commit dc0ec79

Browse files
committed
feat(angular-query): angular 19 support
1 parent 73e783b commit dc0ec79

File tree

230 files changed

+8718
-3486
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

230 files changed

+8718
-3486
lines changed

.changeset/config.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
"$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
33
"changelog": [
44
"@svitejs/changesets-changelog-github-compact",
5-
{ "repo": "TanStack/query" }
5+
{
6+
"repo": "TanStack/query"
7+
}
68
],
79
"commit": false,
810
"access": "public",
@@ -11,6 +13,7 @@
1113
"fixed": [
1214
[
1315
"@tanstack/angular-query-experimental",
16+
"@tanstack/angular-query-devtools",
1417
"@tanstack/angular-query-persist-client",
1518
"@tanstack/eslint-plugin-query",
1619
"@tanstack/preact-query",
@@ -43,4 +46,4 @@
4346
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
4447
"onlyUpdatePeerDependentsWhenOutOfRange": true
4548
}
46-
}
49+
}

.changeset/deep-crews-open.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/angular-query-experimental': minor
3+
---
4+
5+
require Angular v19+ and use Angular component effect scheduling

docs/config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@
158158
"label": "Devtools",
159159
"to": "framework/angular/devtools"
160160
},
161+
{
162+
"label": "SSR",
163+
"to": "framework/angular/guides/ssr"
164+
},
161165
{
162166
"label": "TypeScript",
163167
"to": "framework/angular/typescript"
@@ -1546,6 +1550,10 @@
15461550
{
15471551
"label": "Devtools embedded panel",
15481552
"to": "framework/angular/examples/devtools-panel"
1553+
},
1554+
{
1555+
"label": "SSR",
1556+
"to": "framework/angular/examples/ssr"
15491557
}
15501558
]
15511559
}

docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Because TanStack Query's fetching mechanisms are agnostically built on Promises,
1212
- Mock responses in unit tests using [provideHttpClientTesting](https://angular.dev/guide/http/testing).
1313
- [Interceptors](https://angular.dev/guide/http/interceptors) can be used for a wide range of functionality including adding authentication headers, performing logging, etc. While some data fetching libraries have their own interceptor system, `HttpClient` interceptors are integrated with Angular's dependency injection system.
1414
- `HttpClient` automatically informs [`PendingTasks`](https://angular.dev/api/core/PendingTasks#), which enables Angular to be aware of pending requests. Unit tests and SSR can use the resulting application _stableness_ information to wait for pending requests to finish. This makes unit testing much easier for [Zoneless](https://angular.dev/guide/zoneless) applications.
15-
- When using SSR, `HttpClient` will [cache requests](https://angular.dev/guide/ssr#caching-data-when-using-HttpClient) performed on the server. This will prevent unneeded requests on the client. `HttpClient` SSR caching works out of the box. TanStack Query has its own hydration functionality which may be more powerful but requires some setup. Which one fits your needs best depends on your use case.
15+
- When using SSR, `HttpClient` will [cache requests](https://angular.dev/guide/ssr#caching-data-when-using-HttpClient) performed on the server. TanStack Query will additionally [hydrate its cache](./guides/ssr.md) from the server-rendered HTML when you use `provideTanStackQuery`.
1616

1717
### Using observables in `queryFn`
1818

@@ -36,8 +36,6 @@ class ExampleComponent {
3636
```
3737

3838
> Since Angular is moving towards RxJS as an optional dependency, it's expected that `HttpClient` will also support promises in the future.
39-
>
40-
> Support for observables in TanStack Query for Angular is planned.
4139
4240
## Comparison table
4341

docs/framework/angular/devtools.md

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,23 @@ title: Devtools
1111
1212
## Enable devtools
1313

14+
Add the devtools package (in addition to `@tanstack/angular-query-experimental`):
15+
16+
```bash
17+
npm install @tanstack/angular-query-devtools
18+
```
19+
1420
The devtools help you debug and inspect your queries and mutations. You can enable the devtools by adding `withDevtools` to `provideTanStackQuery`.
1521

16-
By default, Angular Query Devtools are only included in development mode bundles, so you don't need to worry about excluding them during a production build.
22+
By default, Angular Query Devtools only load in development.
1723

1824
```ts
1925
import {
2026
QueryClient,
2127
provideTanStackQuery,
2228
} from '@tanstack/angular-query-experimental'
2329

24-
import { withDevtools } from '@tanstack/angular-query-experimental/devtools'
30+
import { withDevtools } from '@tanstack/angular-query-devtools'
2531

2632
export const appConfig: ApplicationConfig = {
2733
providers: [provideTanStackQuery(new QueryClient(), withDevtools())],
@@ -30,28 +36,28 @@ export const appConfig: ApplicationConfig = {
3036

3137
## Devtools in production
3238

33-
Devtools are automatically excluded from production builds. However, it might be desirable to lazy load the devtools in production.
34-
35-
To use `withDevtools` in production builds, import using the `production` sub-path. The function exported from the production subpath is identical to the main one, but won't be excluded from production builds.
39+
If you need the real implementation in production, import from the `production` entrypoint.
3640

3741
```ts
38-
import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production'
42+
import { withDevtools } from '@tanstack/angular-query-devtools/production'
3943
```
4044

41-
To control when devtools are loaded, you can use the `loadDevtools` option.
45+
To control when devtools are loaded, use the `loadDevtools` option.
4246

43-
When not setting the option or setting it to 'auto', the devtools will be loaded automatically only when Angular runs in development mode.
47+
When omitted or set to `'auto'`, devtools only load in development mode.
4448

4549
```ts
46-
import { withDevtools } from '@tanstack/angular-query-experimental/devtools'
50+
import { withDevtools } from '@tanstack/angular-query-devtools'
4751

48-
provideTanStackQuery(new QueryClient(), withDevtools())
52+
providers: [provideTanStackQuery(new QueryClient(), withDevtools())]
4953

5054
// which is equivalent to
51-
provideTanStackQuery(
52-
new QueryClient(),
53-
withDevtools(() => ({ loadDevtools: 'auto' })),
54-
)
55+
providers: [
56+
provideTanStackQuery(
57+
new QueryClient(),
58+
withDevtools(() => ({ loadDevtools: 'auto' })),
59+
),
60+
]
5561
```
5662

5763
When setting the option to true, the devtools will be loaded in both development and production mode.
@@ -61,29 +67,30 @@ This is useful if you want to load devtools based on [Angular environment config
6167
```ts
6268
import { environment } from './environments/environment'
6369
// Make sure to use the production sub-path to load devtools in production builds
64-
import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production'
65-
66-
provideTanStackQuery(
67-
new QueryClient(),
68-
withDevtools(() => ({ loadDevtools: environment.loadDevtools })),
69-
)
70+
import { withDevtools } from '@tanstack/angular-query-devtools/production'
71+
72+
providers: [
73+
provideTanStackQuery(
74+
new QueryClient(),
75+
withDevtools(() => ({ loadDevtools: environment.loadDevtools })),
76+
),
77+
]
7078
```
7179

7280
When setting the option to false, the devtools will not be loaded.
7381

7482
```ts
75-
provideTanStackQuery(
76-
new QueryClient(),
77-
withDevtools(() => ({ loadDevtools: false })),
78-
)
83+
providers: [
84+
provideTanStackQuery(
85+
new QueryClient(),
86+
withDevtools(() => ({ loadDevtools: false })),
87+
),
88+
]
7989
```
8090

8191
## Derive options through reactivity
8292

83-
Options are passed to `withDevtools` from a callback function to support reactivity through signals. In the following example
84-
a signal is created from a RxJS observable that emits on a keyboard shortcut. When the derived signal is set to true, the devtools are lazily loaded.
85-
86-
The example below always loads devtools in development mode and loads on-demand in production mode when a keyboard shortcut is pressed.
93+
Options can be returned from a callback so they can react to signals. For example, a signal derived from a keyboard shortcut can enable devtools on demand:
8794

8895
```ts
8996
import { Injectable, isDevMode } from '@angular/core'
@@ -107,14 +114,12 @@ export class DevtoolsOptionsManager {
107114
}
108115
```
109116

110-
If you want to use an injectable such as a service in the callback you can use `deps`. The injected value will be passed as parameter to the callback function.
111-
112-
This is similar to `deps` in Angular's [`useFactory`](https://angular.dev/guide/di/dependency-injection-providers#factory-providers-usefactory) provider.
117+
To use an injectable such as a service in the callback, pass it through `deps`:
113118

114119
```ts
115120
// ...
116121
// 👇 Note we import from the production sub-path to enable devtools lazy loading in production builds
117-
import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production'
122+
import { withDevtools } from '@tanstack/angular-query-devtools/production'
118123

119124
export const appConfig: ApplicationConfig = {
120125
providers: [
@@ -126,7 +131,6 @@ export const appConfig: ApplicationConfig = {
126131
loadDevtools: devToolsOptionsManager.loadDevtools(),
127132
}),
128133
{
129-
// `deps` is used to inject and pass `DevtoolsOptionsManager` to the `withDevtools` callback.
130134
deps: [DevtoolsOptionsManager],
131135
},
132136
),
@@ -140,8 +144,8 @@ export const appConfig: ApplicationConfig = {
140144
Of these options `loadDevtools`, `client`, `position`, `errorTypes`, `buttonPosition`, and `initialIsOpen` support reactivity through signals.
141145

142146
- `loadDevtools?: 'auto' | boolean`
143-
- Defaults to `auto`: lazily loads devtools when in development mode. Skips loading in production mode.
144-
- Use this to control if the devtools are loaded.
147+
- Omit or `'auto'`: load devtools only in development mode.
148+
- Use this to control whether devtools load when using the `/production` import.
145149
- `initialIsOpen?: Boolean`
146150
- Set this to `true` if you want the tools to default to being open
147151
- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative"`

docs/framework/angular/guides/caching.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ Let's assume we are using the default `gcTime` of **5 minutes** and the default
2727
- When the request completes successfully, the cache's data under the `['todos']` key is updated with the new data, and both instances are updated with the new data.
2828
- Both instances of the `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos }))` query are destroyed and no longer in use.
2929
- Since there are no more active instances of this query, a garbage collection timeout is set using `gcTime` to delete and garbage collect the query (defaults to **5 minutes**).
30-
- Before the cache timeout has completed, another instance of `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos }))` mounts. The query immediately returns the available cached data while the `fetchTodos` function is being run in the background. When it completes successfully, it will populate the cache with fresh data.
31-
- The final instance of `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos }))` gets destroyed.
32-
- No more instances of `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos }))` appear within **5 minutes**.
30+
- Before the cache timeout has completed, another instance of `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` mounts. The query immediately returns the available cached data while the `fetchTodos` function is being run in the background. When it completes successfully, it will populate the cache with fresh data.
31+
- The final instance of `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` gets destroyed.
32+
- No more instances of `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` appear within **5 minutes**.
3333
- The cached data under the `['todos']` key is deleted and garbage collected.
3434

3535
For more advanced use-cases, see [injectQuery](../reference/functions/injectQuery.md).

docs/framework/angular/guides/default-query-function.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,27 @@ bootstrapApplication(MyAppComponent, {
2828
providers: [provideTanStackQuery(queryClient)],
2929
})
3030

31-
export class PostsComponent {
31+
@Component({
32+
// ...
33+
})
34+
class PostsComponent {
3235
// All you have to do now is pass a key!
3336
postsQuery = injectQuery<Array<Post>>(() => ({
3437
queryKey: ['/posts'],
3538
}))
3639
// ...
3740
}
3841

39-
export class PostComponent {
42+
@Component({
43+
// ...
44+
})
45+
class PostComponent {
46+
postId = input(0)
47+
4048
// You can even leave out the queryFn and just go straight into options
4149
postQuery = injectQuery<Post>(() => ({
42-
enabled: this.postIdSignal() > 0,
43-
queryKey: [`/posts/${this.postIdSignal()}`],
50+
enabled: this.postId() > 0,
51+
queryKey: [`/posts/${this.postId()}`],
4452
}))
4553
// ...
4654
}

docs/framework/angular/guides/dependent-queries.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ replace: { 'useQuery': 'injectQuery', 'useQueries': 'injectQueries' }
1010
```ts
1111
// Get the user
1212
userQuery = injectQuery(() => ({
13-
queryKey: ['user', email],
14-
queryFn: getUserByEmail,
13+
queryKey: ['user', this.email()],
14+
queryFn: this.getUserByEmail,
1515
}))
1616

1717
// Then get the user's projects
1818
projectsQuery = injectQuery(() => ({
1919
queryKey: ['projects', this.userQuery.data()?.id],
20-
queryFn: getProjectsByUser,
20+
queryFn: this.getProjectsByUser,
2121
// The query will not execute until the user id exists
2222
enabled: !!this.userQuery.data()?.id,
2323
}))
@@ -26,8 +26,24 @@ projectsQuery = injectQuery(() => ({
2626
[//]: # 'Example'
2727
[//]: # 'Example2'
2828

29+
Dynamic parallel query - `injectQueries` can depend on a previous query also, here's how to achieve this:
30+
2931
```ts
30-
// injectQueries is under development for Angular Query
32+
// Get the users ids
33+
userIdsQuery = injectQuery(() => ({
34+
queryKey: ['users'],
35+
queryFn: getUsersData,
36+
select: (users) => users.map((user) => user.id),
37+
}))
38+
39+
// Then get the users' messages
40+
usersMessages = injectQueries(() => ({
41+
queries:
42+
this.userIdsQuery.data()?.map((id) => ({
43+
queryKey: ['messages', id],
44+
queryFn: () => getMessagesByUsers(id),
45+
})) ?? [],
46+
}))
3147
```
3248

3349
[//]: # 'Example2'

docs/framework/angular/guides/disabling-queries.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ replace: { 'useQuery': 'injectQuery' }
1313
template: `<div>
1414
<button (click)="query.refetch()">Fetch Todos</button>
1515
16-
@if (query.data()) {
16+
@if (query.data(); as data) {
1717
<ul>
18-
@for (todo of query.data(); track todo.id) {
18+
@for (todo of data; track todo.id) {
1919
<li>{{ todo.title }}</li>
2020
}
2121
</ul>
@@ -70,7 +70,7 @@ export class TodosComponent {
7070
[//]: # 'Example3'
7171

7272
```angular-ts
73-
import { skipToken, injectQuery } from '@tanstack/query-angular'
73+
import { skipToken, injectQuery } from '@tanstack/angular-query-experimental'
7474
7575
@Component({
7676
selector: 'todos',

docs/framework/angular/guides/does-this-replace-client-state.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@ replace:
77
'useQuery': 'injectQuery',
88
'useMutation': 'injectMutation',
99
'hook': 'function',
10+
'Redux, MobX or': 'NgRx Store or',
11+
'Redux, MobX, Zustand': 'NgRx Store, custom services with RxJS',
1012
}
1113
---

0 commit comments

Comments
 (0)