diff --git a/examples/angular/auto-refetching/src/app/components/auto-refetching.component.html b/examples/angular/auto-refetching/src/app/components/auto-refetching.component.html index f0359aae9bc..49b60cb75c3 100644 --- a/examples/angular/auto-refetching/src/app/components/auto-refetching.component.html +++ b/examples/angular/auto-refetching/src/app/components/auto-refetching.component.html @@ -14,8 +14,8 @@

Auto Refetch with stale-time set to {{ intervalMs() }}ms

marginLeft: '.5rem', width: '10px', height: '10px', - background: tasks.isFetching() ? 'green' : 'transparent', - transition: !tasks.isFetching() ? 'all .3s ease' : 'none', + background: tasks.isFetching ? 'green' : 'transparent', + transition: !tasks.isFetching ? 'all .3s ease' : 'none', borderRadius: '100%', transform: 'scale(2)', }" @@ -25,7 +25,7 @@

Todo List

diff --git a/examples/angular/basic-persister/src/app/components/post.component.html b/examples/angular/basic-persister/src/app/components/post.component.html index 34b36e94fcc..4808d5b4a5c 100644 --- a/examples/angular/basic-persister/src/app/components/post.component.html +++ b/examples/angular/basic-persister/src/app/components/post.component.html @@ -2,17 +2,17 @@
Back
- @if (postQuery.isPending()) { + @if (postQuery.isPending) { Loading... - } @else if (postQuery.isError()) { - Error: {{ postQuery.error().message }} + } @else if (postQuery.isError) { + Error: {{ postQuery.error.message }} } - @if (postQuery.data(); as post) { + @if (postQuery.data; as post) {

{{ post.title }}

{{ post.body }}

- @if (postQuery.isFetching()) { + @if (postQuery.isFetching) { Background Updating... } } diff --git a/examples/angular/basic-persister/src/app/components/posts.component.html b/examples/angular/basic-persister/src/app/components/posts.component.html index 92ba06ad498..d2e53d618fa 100644 --- a/examples/angular/basic-persister/src/app/components/posts.component.html +++ b/examples/angular/basic-persister/src/app/components/posts.component.html @@ -1,12 +1,12 @@

Posts

- @if (postsQuery.isPending()) { + @if (postsQuery.isPending) { Loading... - } @else if (postsQuery.isError()) { - Error: {{ postsQuery.error().message }} - } @else if (postsQuery.isSuccess()) { + } @else if (postsQuery.isError) { + Error: {{ postsQuery.error.message }} + } @else if (postsQuery.isSuccess) {
- @for (post of postsQuery.data(); track post.id) { + @for (post of postsQuery.data; track post.id) {

@@ -28,7 +28,7 @@

Posts

}
- @if (postsQuery.isFetching()) { + @if (postsQuery.isFetching) { Background Updating... }
diff --git a/examples/angular/basic/src/app/components/post.component.html b/examples/angular/basic/src/app/components/post.component.html index 34b36e94fcc..4808d5b4a5c 100644 --- a/examples/angular/basic/src/app/components/post.component.html +++ b/examples/angular/basic/src/app/components/post.component.html @@ -2,17 +2,17 @@
Back
- @if (postQuery.isPending()) { + @if (postQuery.isPending) { Loading... - } @else if (postQuery.isError()) { - Error: {{ postQuery.error().message }} + } @else if (postQuery.isError) { + Error: {{ postQuery.error.message }} } - @if (postQuery.data(); as post) { + @if (postQuery.data; as post) {

{{ post.title }}

{{ post.body }}

- @if (postQuery.isFetching()) { + @if (postQuery.isFetching) { Background Updating... } } diff --git a/examples/angular/basic/src/app/components/posts.component.html b/examples/angular/basic/src/app/components/posts.component.html index 92ba06ad498..d2e53d618fa 100644 --- a/examples/angular/basic/src/app/components/posts.component.html +++ b/examples/angular/basic/src/app/components/posts.component.html @@ -1,12 +1,12 @@

Posts

- @if (postsQuery.isPending()) { + @if (postsQuery.isPending) { Loading... - } @else if (postsQuery.isError()) { - Error: {{ postsQuery.error().message }} - } @else if (postsQuery.isSuccess()) { + } @else if (postsQuery.isError) { + Error: {{ postsQuery.error.message }} + } @else if (postsQuery.isSuccess) {
- @for (post of postsQuery.data(); track post.id) { + @for (post of postsQuery.data; track post.id) {

@@ -28,7 +28,7 @@

Posts

}
- @if (postsQuery.isFetching()) { + @if (postsQuery.isFetching) { Background Updating... }
diff --git a/examples/angular/devtools-panel/src/app/components/example-query.component.ts b/examples/angular/devtools-panel/src/app/components/example-query.component.ts index 1537167a948..b7a9d3e2cec 100644 --- a/examples/angular/devtools-panel/src/app/components/example-query.component.ts +++ b/examples/angular/devtools-panel/src/app/components/example-query.component.ts @@ -16,14 +16,14 @@ interface Response { selector: 'example-query', template: `
- @if (query.isPending()) { + @if (query.isPending) {
Loading...
} - @if (query.isError()) { -
An error has occurred: {{ query.error().message }}
+ @if (query.isError) { +
An error has occurred: {{ query.error.message }}
} - @if (query.isSuccess()) { - @let data = query.data(); + @if (query.isSuccess) { + @let data = query.data;

{{ data.name }}

{{ data.description }}

👀 {{ data.subscribers_count }} diff --git a/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.html b/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.html index 7afc85afe27..61c12633704 100644 --- a/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.html +++ b/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.html @@ -2,10 +2,10 @@

Infinite Query with max pages

4 projects per page

3 pages max

- @if (query.isPending()) { + @if (query.isPending) {

Loading...

- } @else if (query.isError()) { - Error: {{ query.error().message }} + } @else if (query.isError) { + Error: {{ query.error.message }} } @else {
- @for (page of query.data().pages; track $index) { + @for (page of query.data.pages; track $index) { @for (project of page.data; track project.id) {

{{ project.name }} {{ project.id }}

} @@ -27,7 +27,7 @@

3 pages max

{{ - query.isFetching() && !query.isFetchingNextPage() + query.isFetching && !query.isFetchingNextPage ? 'Background Updating...' : null }} diff --git a/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts b/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts index 3232f649429..14f64aad46f 100644 --- a/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts +++ b/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts @@ -30,24 +30,24 @@ export class ExampleComponent { })) readonly nextButtonDisabled = computed( - () => !this.query.hasNextPage() || this.query.isFetchingNextPage(), + () => !this.query.hasNextPage || this.query.isFetchingNextPage, ) readonly nextButtonText = computed(() => - this.query.isFetchingNextPage() + this.query.isFetchingNextPage ? 'Loading more...' - : this.query.hasNextPage() + : this.query.hasNextPage ? 'Load newer' : 'Nothing more to load', ) readonly previousButtonDisabled = computed( - () => !this.query.hasPreviousPage() || this.query.isFetchingPreviousPage(), + () => !this.query.hasPreviousPage || this.query.isFetchingPreviousPage, ) readonly previousButtonText = computed(() => - this.query.isFetchingPreviousPage() + this.query.isFetchingPreviousPage ? 'Loading more...' - : this.query.hasPreviousPage() + : this.query.hasPreviousPage ? 'Load Older' : 'Nothing more to load', ) diff --git a/examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts b/examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts index b32a0f50dc6..e49b13f2165 100644 --- a/examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts +++ b/examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts @@ -22,7 +22,7 @@ import { TasksService } from '../services/tasks.service'


- @if (tasks.isLoading()) { + @if (tasks.isLoading) {

Loading...

} @@ -36,16 +36,16 @@ import { TasksService } from '../services/tasks.service'
    - @for (task of tasks.data(); track $index) { + @for (task of tasks.data; track $index) {
  • {{ task }}
  • }
- Updated At: {{ tasks.dataUpdatedAt() | date: 'MMMM d, h:mm:ss a ' }} + Updated At: {{ tasks.dataUpdatedAt | date: 'MMMM d, h:mm:ss a ' }}
- @if (!tasks.isLoading() && tasks.isFetching()) { + @if (!tasks.isLoading && tasks.isFetching) {

Fetching in background

}
diff --git a/examples/angular/pagination/src/app/components/example.component.html b/examples/angular/pagination/src/app/components/example.component.html index b6ffc8c1289..d3f2dd862a0 100644 --- a/examples/angular/pagination/src/app/components/example.component.html +++ b/examples/angular/pagination/src/app/components/example.component.html @@ -7,13 +7,13 @@ instantaneously while they are also refetched invisibly in the background.

- @if (query.isPending()) { + @if (query.isPending) {
Loading...
- } @else if (query.isError()) { -
Error: {{ query.error().message }}
- } @else if (query.isSuccess()) { + } @else if (query.isError) { +
Error: {{ query.error.message }}
+ } @else if (query.isSuccess) {
- @for (project of query.data().projects; track project.id) { + @for (project of query.data.projects; track project.id) {

{{ project.name }}

}
@@ -26,14 +26,14 @@ - @if (query.isFetching()) { + @if (query.isFetching) { Loading... }
diff --git a/examples/angular/pagination/src/app/components/example.component.ts b/examples/angular/pagination/src/app/components/example.component.ts index 861ded2fa92..526481397b8 100644 --- a/examples/angular/pagination/src/app/components/example.component.ts +++ b/examples/angular/pagination/src/app/components/example.component.ts @@ -34,8 +34,8 @@ export class ExampleComponent { })) readonly prefetchEffect = effect(() => { - const data = this.query.data() - const isPlaceholderData = this.query.isPlaceholderData() + const data = this.query.data + const isPlaceholderData = this.query.isPlaceholderData const newPage = this.page() + 1 untracked(() => { @@ -57,7 +57,7 @@ export class ExampleComponent { nextPage() { this.page.update((currentPage) => { - return this.query.data()?.hasMore ? currentPage + 1 : currentPage + return this.query.data?.hasMore ? currentPage + 1 : currentPage }) } } diff --git a/examples/angular/query-options-from-a-service/src/app/components/post.component.html b/examples/angular/query-options-from-a-service/src/app/components/post.component.html index e1ec2ee2975..dd0998da80a 100644 --- a/examples/angular/query-options-from-a-service/src/app/components/post.component.html +++ b/examples/angular/query-options-from-a-service/src/app/components/post.component.html @@ -2,18 +2,18 @@
Back
- @if (postQuery.isPending()) { + @if (postQuery.isPending) { Loading... - } @else if (postQuery.isError()) { - Error: {{ postQuery.error().message }} + } @else if (postQuery.isError) { + Error: {{ postQuery.error.message }} } - @if (postQuery.isSuccess()) { - @let post = postQuery.data(); + @if (postQuery.isSuccess) { + @let post = postQuery.data;

{{ post.title }}

{{ post.body }}

- @if (postQuery.isFetching()) { + @if (postQuery.isFetching) { Background Updating... } } diff --git a/examples/angular/query-options-from-a-service/src/app/components/posts.component.html b/examples/angular/query-options-from-a-service/src/app/components/posts.component.html index 069263f27e1..9dcae2ac900 100644 --- a/examples/angular/query-options-from-a-service/src/app/components/posts.component.html +++ b/examples/angular/query-options-from-a-service/src/app/components/posts.component.html @@ -1,12 +1,12 @@

Posts

- @if (postsQuery.isPending()) { + @if (postsQuery.isPending) { Loading... - } @else if (postsQuery.isError()) { - Error: {{ postsQuery.error().message }} - } @else if (postsQuery.isSuccess()) { + } @else if (postsQuery.isError) { + Error: {{ postsQuery.error.message }} + } @else if (postsQuery.isSuccess) {
- @for (post of postsQuery.data(); track post.id) { + @for (post of postsQuery.data; track post.id) {

@@ -27,7 +27,7 @@

Posts

}
- @if (postsQuery.isFetching()) { + @if (postsQuery.isFetching) { Background Updating... }
diff --git a/examples/angular/router/src/app/components/post.component.html b/examples/angular/router/src/app/components/post.component.html index e1ec2ee2975..dd0998da80a 100644 --- a/examples/angular/router/src/app/components/post.component.html +++ b/examples/angular/router/src/app/components/post.component.html @@ -2,18 +2,18 @@
Back
- @if (postQuery.isPending()) { + @if (postQuery.isPending) { Loading... - } @else if (postQuery.isError()) { - Error: {{ postQuery.error().message }} + } @else if (postQuery.isError) { + Error: {{ postQuery.error.message }} } - @if (postQuery.isSuccess()) { - @let post = postQuery.data(); + @if (postQuery.isSuccess) { + @let post = postQuery.data;

{{ post.title }}

{{ post.body }}

- @if (postQuery.isFetching()) { + @if (postQuery.isFetching) { Background Updating... } } diff --git a/examples/angular/router/src/app/components/posts.component.html b/examples/angular/router/src/app/components/posts.component.html index 069263f27e1..9dcae2ac900 100644 --- a/examples/angular/router/src/app/components/posts.component.html +++ b/examples/angular/router/src/app/components/posts.component.html @@ -1,12 +1,12 @@

Posts

- @if (postsQuery.isPending()) { + @if (postsQuery.isPending) { Loading... - } @else if (postsQuery.isError()) { - Error: {{ postsQuery.error().message }} - } @else if (postsQuery.isSuccess()) { + } @else if (postsQuery.isError) { + Error: {{ postsQuery.error.message }} + } @else if (postsQuery.isSuccess) {
- @for (post of postsQuery.data(); track post.id) { + @for (post of postsQuery.data; track post.id) {

@@ -27,7 +27,7 @@

Posts

}
- @if (postsQuery.isFetching()) { + @if (postsQuery.isFetching) { Background Updating... }
diff --git a/examples/angular/rxjs/src/app/components/example.component.html b/examples/angular/rxjs/src/app/components/example.component.html index 67a6d0ea3db..05c9c3657a2 100644 --- a/examples/angular/rxjs/src/app/components/example.component.html +++ b/examples/angular/rxjs/src/app/components/example.component.html @@ -3,9 +3,9 @@

Search for a programming language

- @if (query.isSuccess() && query.data().suggestions.length) { + @if (query.isSuccess && query.data.suggestions.length) {
    - @for (suggestion of query.data().suggestions; track suggestion) { + @for (suggestion of query.data.suggestions; track suggestion) {
  • {{ suggestion }}
  • }
diff --git a/examples/angular/simple/src/app/components/simple-example.component.html b/examples/angular/simple/src/app/components/simple-example.component.html index d4a06728ff2..b24fa690eb8 100644 --- a/examples/angular/simple/src/app/components/simple-example.component.html +++ b/examples/angular/simple/src/app/components/simple-example.component.html @@ -1,10 +1,10 @@ -@if (query.isPending()) { +@if (query.isPending) {
Loading...
} -@if (query.isError()) { -
An error has occurred: {{ query.error().message }}
+@if (query.isError) { +
An error has occurred: {{ query.error.message }}
} -@if (query.data(); as data) { +@if (query.data; as data) {

{{ data.name }}

{{ data.description }}

👀 {{ data.subscribers_count }} diff --git a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts b/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts index 4a7ce532bc2..c70214b818b 100644 --- a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts @@ -45,7 +45,7 @@ describe('infiniteQueryOptions', () => { const { data } = injectInfiniteQuery(() => options) // known issue: type of pageParams is unknown when returned from useInfiniteQuery - expectTypeOf(data()).toEqualTypeOf< + expectTypeOf(data).toEqualTypeOf< InfiniteData | undefined >() }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts index 7ec133adfb1..2a536a0629f 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts @@ -34,8 +34,8 @@ describe('injectInfiniteQuery', () => { })) }) - if (query.isSuccess()) { - const data = query.data() + if (query.isSuccess) { + const data = query.data expectTypeOf(data).toEqualTypeOf>() } }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts index f587f6a2fe2..8ddad978924 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts @@ -3,7 +3,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import { ChangeDetectionStrategy, Component, Injector } from '@angular/core' import { sleep } from '@tanstack/query-test-utils' import { QueryClient, injectInfiniteQuery } from '..' -import { expectSignals, setupTanStackQueryTestBed } from './test-utils' +import { setupTanStackQueryTestBed } from './test-utils' describe('injectInfiniteQuery', () => { let queryClient: QueryClient @@ -38,14 +38,14 @@ describe('injectInfiniteQuery', () => { fixture.detectChanges() const query = fixture.componentInstance.query - expectSignals(query, { + expect(query).toMatchObject({ data: undefined, status: 'pending', }) await vi.advanceTimersByTimeAsync(11) - expectSignals(query, { + expect(query).toMatchObject({ data: { pageParams: [0], pages: ['data on page 0'], @@ -57,7 +57,7 @@ describe('injectInfiniteQuery', () => { await vi.advanceTimersByTimeAsync(11) - expectSignals(query, { + expect(query).toMatchObject({ data: { pageParams: [0, 12], pages: ['data on page 0', 'data on page 12'], @@ -98,7 +98,7 @@ describe('injectInfiniteQuery', () => { TestBed.tick() - expect(query.status()).toBe('pending') + expect(query.status).toBe('pending') }) }) }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-mutation.test-d.ts b/packages/angular-query-experimental/src/__tests__/inject-mutation.test-d.ts index f331bb02dea..605933aac5a 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-mutation.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-mutation.test-d.ts @@ -1,7 +1,6 @@ import { describe, expectTypeOf, test } from 'vitest' import { sleep } from '@tanstack/query-test-utils' import { injectMutation } from '..' -import type { Signal } from '@angular/core' describe('Discriminated union return type', () => { test('data should be possibly undefined by default', () => { @@ -9,7 +8,7 @@ describe('Discriminated union return type', () => { mutationFn: () => sleep(0).then(() => 'string'), })) - expectTypeOf(mutation.data).toEqualTypeOf>() + expectTypeOf(mutation.data).toEqualTypeOf() }) test('data should be defined when mutation is success', () => { @@ -17,8 +16,8 @@ describe('Discriminated union return type', () => { mutationFn: () => sleep(0).then(() => 'string'), })) - if (mutation.isSuccess()) { - expectTypeOf(mutation.data).toEqualTypeOf>() + if (mutation.isSuccess) { + expectTypeOf(mutation.data).toEqualTypeOf() } }) @@ -27,8 +26,8 @@ describe('Discriminated union return type', () => { mutationFn: () => sleep(0).then(() => 'string'), })) - if (mutation.isSuccess()) { - expectTypeOf(mutation.error).toEqualTypeOf>() + if (mutation.isSuccess) { + expectTypeOf(mutation.error).toEqualTypeOf() } }) @@ -37,8 +36,8 @@ describe('Discriminated union return type', () => { mutationFn: () => sleep(0).then(() => 'string'), })) - if (mutation.isPending()) { - expectTypeOf(mutation.data).toEqualTypeOf>() + if (mutation.isPending) { + expectTypeOf(mutation.data).toEqualTypeOf() } }) @@ -47,8 +46,8 @@ describe('Discriminated union return type', () => { mutationFn: () => sleep(0).then(() => 'string'), })) - if (mutation.isError()) { - expectTypeOf(mutation.error).toEqualTypeOf>() + if (mutation.isError) { + expectTypeOf(mutation.error).toEqualTypeOf() } }) @@ -57,15 +56,15 @@ describe('Discriminated union return type', () => { mutationFn: (_variables: string) => sleep(0).then(() => 'string'), })) - if (mutation.isIdle()) { - expectTypeOf(mutation.variables).toEqualTypeOf>() + if (mutation.isIdle) { + expectTypeOf(mutation.variables).toEqualTypeOf() } - if (mutation.isPending()) { - expectTypeOf(mutation.variables).toEqualTypeOf>() + if (mutation.isPending) { + expectTypeOf(mutation.variables).toEqualTypeOf() } - if (mutation.isSuccess()) { - expectTypeOf(mutation.variables).toEqualTypeOf>() + if (mutation.isSuccess) { + expectTypeOf(mutation.variables).toEqualTypeOf() } - expectTypeOf(mutation.variables).toEqualTypeOf>() + expectTypeOf(mutation.variables).toEqualTypeOf() }) }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts b/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts index c1548bc634f..7f8df20e4d0 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts @@ -11,7 +11,7 @@ import { TestBed } from '@angular/core/testing' import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import { sleep } from '@tanstack/query-test-utils' import { QueryClient, injectMutation, provideTanStackQuery } from '..' -import { expectSignals, registerSignalInput } from './test-utils' +import { registerSignalInput } from './test-utils' describe('injectMutation', () => { let queryClient: QueryClient @@ -38,7 +38,7 @@ describe('injectMutation', () => { })) }) - expectSignals(mutation, { + expect(mutation).toMatchObject({ isIdle: true, isPending: false, isError: false, @@ -60,7 +60,7 @@ describe('injectMutation', () => { mutation.mutate(result) await vi.advanceTimersByTimeAsync(0) - expectSignals(mutation, { + expect(mutation).toMatchObject({ isIdle: false, isPending: true, isError: false, @@ -82,7 +82,7 @@ describe('injectMutation', () => { await vi.advanceTimersByTimeAsync(11) - expectSignals(mutation, { + expect(mutation).toMatchObject({ isIdle: false, isPending: false, isError: true, @@ -104,7 +104,7 @@ describe('injectMutation', () => { await vi.advanceTimersByTimeAsync(11) - expectSignals(mutation, { + expect(mutation).toMatchObject({ isIdle: false, isPending: false, isError: false, @@ -147,13 +147,13 @@ describe('injectMutation', () => { await vi.advanceTimersByTimeAsync(11) - expect(mutation.isError()).toBe(true) + expect(mutation.isError).toBe(true) mutation.reset() await vi.advanceTimersByTimeAsync(0) - expectSignals(mutation, { + expect(mutation).toMatchObject({ isIdle: true, isPending: false, isError: false, @@ -305,7 +305,7 @@ describe('injectMutation', () => { selector: 'app-fake', template: ` - {{ mutation.data() }} + {{ mutation.data }} `, changeDetection: ChangeDetectionStrategy.OnPush, }) @@ -359,7 +359,7 @@ describe('injectMutation', () => { selector: 'app-fake', template: ` - {{ mutation.data() }} + {{ mutation.data }} `, changeDetection: ChangeDetectionStrategy.OnPush, }) @@ -518,7 +518,7 @@ describe('injectMutation', () => { ) // Initial state - expect(mutation.data()).toBeUndefined() + expect(mutation.data).toBeUndefined() expect(mutationStarted).toBe(false) // Start mutation @@ -532,8 +532,8 @@ describe('injectMutation', () => { // After whenStable(), mutation should be complete expect(mutationStarted).toBe(true) expect(mutationCompleted).toBe(true) - expect(mutation.isSuccess()).toBe(true) - expect(mutation.data()).toBe('processed: test') + expect(mutation.isSuccess).toBe(true) + expect(mutation.data).toBe('processed: test') }) test('should handle synchronous mutation with retry', async () => { @@ -581,8 +581,8 @@ describe('injectMutation', () => { await vi.advanceTimersByTimeAsync(10) await stablePromise - expect(mutation.isSuccess()).toBe(true) - expect(mutation.data()).toBe('processed: retry-test') + expect(mutation.isSuccess).toBe(true) + expect(mutation.data).toBe('processed: retry-test') expect(attemptCount).toBe(3) // Initial + 2 retries }) @@ -631,10 +631,10 @@ describe('injectMutation', () => { await vi.advanceTimersByTimeAsync(1) await stablePromise - expect(mutation1.isSuccess()).toBe(true) - expect(mutation1.data()).toBe('mutation1: test1') - expect(mutation2.isSuccess()).toBe(true) - expect(mutation2.data()).toBe('mutation2: test2') + expect(mutation1.isSuccess).toBe(true) + expect(mutation1.data).toBe('mutation1: test1') + expect(mutation2.isSuccess).toBe(true) + expect(mutation2.data).toBe('mutation2: test2') expect(callCount).toBe(2) }) @@ -685,8 +685,8 @@ describe('injectMutation', () => { expect(onMutateCalled).toBe(true) expect(onSuccessCalled).toBe(true) - expect(mutation.isSuccess()).toBe(true) - expect(mutation.data()).toBe('final: test') + expect(mutation.isSuccess).toBe(true) + expect(mutation.data).toBe('final: test') expect(queryClient.getQueryData(testQueryKey)).toBe('final: test') }) @@ -721,8 +721,8 @@ describe('injectMutation', () => { await stablePromise // Synchronous mutations complete immediately - expect(mutation.isSuccess()).toBe(true) - expect(mutation.data()).toBe('processed: test') + expect(mutation.isSuccess).toBe(true) + expect(mutation.data).toBe('processed: test') }) }) }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts b/packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts index 62547fd9e09..d12edf925b6 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts @@ -34,9 +34,9 @@ describe('InjectQueries config object overload', () => { queries: [query1, query2, query3], })) - const query1Data = queryResults()[0].data() - const query2Data = queryResults()[1].data() - const query3Data = queryResults()[2].data() + const query1Data = queryResults()[0].data + const query2Data = queryResults()[1].data + const query3Data = queryResults()[2].data expectTypeOf(query1Data).toEqualTypeOf<{ wow: boolean }>() expectTypeOf(query2Data).toEqualTypeOf() @@ -57,7 +57,7 @@ describe('InjectQueries config object overload', () => { }) const queryResults = injectQueries(() => ({ queries: [options] })) - const data = queryResults()[0].data() + const data = queryResults()[0].data expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() }) @@ -76,8 +76,8 @@ describe('InjectQueries config object overload', () => { } const queryResults = injectQueries(() => ({ queries: [query1, query2] })) - const query1Data = queryResults()[0].data() - const query2Data = queryResults()[1].data() + const query1Data = queryResults()[0].data + const query2Data = queryResults()[1].data expectTypeOf(query1Data).toEqualTypeOf() expectTypeOf(query2Data).toEqualTypeOf() @@ -98,7 +98,7 @@ describe('InjectQueries config object overload', () => { ], })) - const data = queryResults()[0].data() + const data = queryResults()[0].data expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>() }) @@ -122,7 +122,7 @@ describe('InjectQueries config object overload', () => { } const queryResults = injectCustomQueries() - const data = queryResults()[0].data() + const data = queryResults()[0].data expectTypeOf(data).toEqualTypeOf() }) @@ -141,7 +141,7 @@ describe('InjectQueries config object overload', () => { const firstResult = queryResults()[0] expectTypeOf(firstResult).toEqualTypeOf>() - expectTypeOf(firstResult.data()).toEqualTypeOf() + expectTypeOf(firstResult.data).toEqualTypeOf() }) it('should return correct data for dynamic queries with mixed result types', () => { diff --git a/packages/angular-query-experimental/src/__tests__/inject-queries.test.ts b/packages/angular-query-experimental/src/__tests__/inject-queries.test.ts index 44558aa7430..9dca3262b41 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-queries.test.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-queries.test.ts @@ -23,8 +23,8 @@ describe('injectQueries', () => { template: `
- data1: {{ result()[0].data() ?? 'null' }}, data2: - {{ result()[1].data() ?? 'null' }} + data1: {{ result()[0].data ?? 'null' }}, data2: + {{ result()[1].data ?? 'null' }}
`, @@ -54,7 +54,7 @@ describe('injectQueries', () => { })) _pushResults = effect(() => { - const snapshot = this.result().map((q) => ({ data: q.data() })) + const snapshot = this.result().map((q) => ({ data: q.data })) results.push(snapshot) }) } diff --git a/packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts b/packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts index 541ad65f148..93bfa8345c0 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts @@ -1,7 +1,6 @@ import { describe, expectTypeOf, it, test } from 'vitest' import { sleep } from '@tanstack/query-test-utils' import { injectQuery, queryOptions } from '..' -import type { Signal } from '@angular/core' describe('initialData', () => { describe('Config object overload', () => { @@ -12,7 +11,7 @@ describe('initialData', () => { initialData: { wow: true }, })) - expectTypeOf(data).toEqualTypeOf>() + expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() }) it('TData should be defined when passed through queryOptions', () => { @@ -30,7 +29,7 @@ describe('initialData', () => { }) const { data } = injectQuery(options) - expectTypeOf(data).toEqualTypeOf>() + expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() }) it('should be possible to define a different TData than TQueryFnData using select with queryOptions spread into useQuery', () => { @@ -44,7 +43,7 @@ describe('initialData', () => { select: (data) => data > 1, })) - expectTypeOf(query.data).toEqualTypeOf>() + expectTypeOf(query.data).toEqualTypeOf() }) it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => { @@ -60,7 +59,7 @@ describe('initialData', () => { }), })) - expectTypeOf(data).toEqualTypeOf>() + expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() }) it('TData should have undefined in the union when initialData is NOT provided', () => { @@ -73,7 +72,7 @@ describe('initialData', () => { }, })) - expectTypeOf(data).toEqualTypeOf>() + expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>() }) it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { @@ -87,7 +86,7 @@ describe('initialData', () => { initialData: () => undefined as { wow: boolean } | undefined, })) - expectTypeOf(data).toEqualTypeOf>() + expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>() }) it('TData should be narrowed after an isSuccess check when initialData is provided as a function which can return undefined', () => { @@ -101,8 +100,8 @@ describe('initialData', () => { initialData: () => undefined as { wow: boolean } | undefined, })) - if (query.isSuccess()) { - expectTypeOf(query.data).toEqualTypeOf>() + if (query.isSuccess) { + expectTypeOf(query.data).toEqualTypeOf<{ wow: boolean }>() } }) }) @@ -130,7 +129,7 @@ describe('Discriminated union return type', () => { queryFn: () => sleep(0).then(() => 'Some data'), })) - expectTypeOf(query.data).toEqualTypeOf>() + expectTypeOf(query.data).toEqualTypeOf() }) test('data should be defined when query is success', () => { @@ -139,8 +138,8 @@ describe('Discriminated union return type', () => { queryFn: () => sleep(0).then(() => 'Some data'), })) - if (query.isSuccess()) { - expectTypeOf(query.data).toEqualTypeOf>() + if (query.isSuccess) { + expectTypeOf(query.data).toEqualTypeOf() } }) @@ -150,8 +149,8 @@ describe('Discriminated union return type', () => { queryFn: () => sleep(0).then(() => 'Some data'), })) - if (query.isSuccess()) { - expectTypeOf(query.error).toEqualTypeOf>() + if (query.isSuccess) { + expectTypeOf(query.error).toEqualTypeOf() } }) @@ -161,8 +160,8 @@ describe('Discriminated union return type', () => { queryFn: () => sleep(0).then(() => 'Some data'), })) - if (query.isPending()) { - expectTypeOf(query.data).toEqualTypeOf>() + if (query.isPending) { + expectTypeOf(query.data).toEqualTypeOf() } }) @@ -172,8 +171,8 @@ describe('Discriminated union return type', () => { queryFn: () => sleep(0).then(() => 'Some data'), })) - if (query.isError()) { - expectTypeOf(query.error).toEqualTypeOf>() + if (query.isError) { + expectTypeOf(query.error).toEqualTypeOf() } }) }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-query.test.ts b/packages/angular-query-experimental/src/__tests__/inject-query.test.ts index d1befc202b1..a5347654016 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-query.test.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-query.test.ts @@ -214,46 +214,44 @@ describe('injectQuery', () => { fromWrappedFuncStyleQuery, } = fixture.componentInstance - expectTypeOf(noQueryFn.data()).toEqualTypeOf() - expectTypeOf(noQueryFn.error()).toEqualTypeOf() + expectTypeOf(noQueryFn.data).toEqualTypeOf() + expectTypeOf(noQueryFn.error).toEqualTypeOf() - expectTypeOf(fromQueryFn.data()).toEqualTypeOf() - expectTypeOf(fromQueryFn.error()).toEqualTypeOf() + expectTypeOf(fromQueryFn.data).toEqualTypeOf() + expectTypeOf(fromQueryFn.error).toEqualTypeOf() - expectTypeOf(withResult.data()).toEqualTypeOf() - expectTypeOf(withResult.error()).toEqualTypeOf() + expectTypeOf(withResult.data).toEqualTypeOf() + expectTypeOf(withResult.error).toEqualTypeOf() - expectTypeOf(withError.data()).toEqualTypeOf() - expectTypeOf(withError.error()).toEqualTypeOf<{ message: string } | null>() + expectTypeOf(withError.data).toEqualTypeOf() + expectTypeOf(withError.error).toEqualTypeOf<{ message: string } | null>() - expectTypeOf(withResultInfer.data()).toEqualTypeOf() - expectTypeOf(withResultInfer.error()).toEqualTypeOf() + expectTypeOf(withResultInfer.data).toEqualTypeOf() + expectTypeOf(withResultInfer.error).toEqualTypeOf() - expectTypeOf(unionTypeSync.data()).toEqualTypeOf<'a' | 'b' | undefined>() - expectTypeOf(unionTypeAsync.data()).toEqualTypeOf<'a' | 'b' | undefined>() + expectTypeOf(unionTypeSync.data).toEqualTypeOf<'a' | 'b' | undefined>() + expectTypeOf(unionTypeAsync.data).toEqualTypeOf<'a' | 'b' | undefined>() - expectTypeOf(fromGenericQueryFn.data()).toEqualTypeOf() - expectTypeOf(fromGenericQueryFn.error()).toEqualTypeOf() + expectTypeOf(fromGenericQueryFn.data).toEqualTypeOf() + expectTypeOf(fromGenericQueryFn.error).toEqualTypeOf() - expectTypeOf(fromGenericOptionsQueryFn.data()).toEqualTypeOf< + expectTypeOf(fromGenericOptionsQueryFn.data).toEqualTypeOf< string | undefined >() - expectTypeOf( - fromGenericOptionsQueryFn.error(), - ).toEqualTypeOf() + expectTypeOf(fromGenericOptionsQueryFn.error).toEqualTypeOf() - expectTypeOf(fromMyDataArrayKeyQueryFn.data()).toEqualTypeOf< + expectTypeOf(fromMyDataArrayKeyQueryFn.data).toEqualTypeOf< number | undefined >() - expectTypeOf(fromPromiseAnyQueryFn.data()).toEqualTypeOf() + expectTypeOf(fromPromiseAnyQueryFn.data).toEqualTypeOf() - expectTypeOf(fromGetMyDataStringKeyQueryFn.data()).toEqualTypeOf< + expectTypeOf(fromGetMyDataStringKeyQueryFn.data).toEqualTypeOf< number | undefined >() - expectTypeOf(fromWrappedQuery.data()).toEqualTypeOf() - expectTypeOf(fromWrappedFuncStyleQuery.data()).toEqualTypeOf< + expectTypeOf(fromWrappedQuery.data).toEqualTypeOf() + expectTypeOf(fromWrappedFuncStyleQuery.data).toEqualTypeOf< boolean | undefined >() }) @@ -275,11 +273,11 @@ describe('injectQuery', () => { fixture.detectChanges() const query = fixture.componentInstance.query - expect(query.status()).toBe('pending') - expect(query.isPending()).toBe(true) - expect(query.isFetching()).toBe(true) - expect(query.isStale()).toBe(true) - expect(query.isFetched()).toBe(false) + expect(query.status).toBe('pending') + expect(query.isPending).toBe(true) + expect(query.isFetching).toBe(true) + expect(query.isStale).toBe(true) + expect(query.isFetched).toBe(false) }) test('should resolve to success and update signal: injectQuery()', async () => { @@ -300,12 +298,12 @@ describe('injectQuery', () => { const query = fixture.componentInstance.query await vi.advanceTimersByTimeAsync(11) - expect(query.status()).toBe('success') - expect(query.data()).toBe('result2') - expect(query.isPending()).toBe(false) - expect(query.isFetching()).toBe(false) - expect(query.isFetched()).toBe(true) - expect(query.isSuccess()).toBe(true) + expect(query.status).toBe('success') + expect(query.data).toBe('result2') + expect(query.isPending).toBe(false) + expect(query.isFetching).toBe(false) + expect(query.isFetched).toBe(true) + expect(query.isSuccess).toBe(true) }) test('should reject and update signal', async () => { @@ -328,14 +326,14 @@ describe('injectQuery', () => { const query = fixture.componentInstance.query await vi.advanceTimersByTimeAsync(11) - expect(query.status()).toBe('error') - expect(query.data()).toBe(undefined) - expect(query.error()).toMatchObject({ message: 'Some error' }) - expect(query.isPending()).toBe(false) - expect(query.isFetching()).toBe(false) - expect(query.isError()).toBe(true) - expect(query.failureCount()).toBe(1) - expect(query.failureReason()).toMatchObject({ message: 'Some error' }) + expect(query.status).toBe('error') + expect(query.data).toBe(undefined) + expect(query.error).toMatchObject({ message: 'Some error' }) + expect(query.isPending).toBe(false) + expect(query.isFetching).toBe(false) + expect(query.isError).toBe(true) + expect(query.failureCount).toBe(1) + expect(query.failureReason).toMatchObject({ message: 'Some error' }) }) test('should update query on options contained signal change', async () => { @@ -364,7 +362,7 @@ describe('injectQuery', () => { expect(spy).toHaveBeenCalledTimes(1) await vi.advanceTimersByTimeAsync(11) - expect(query.status()).toBe('success') + expect(query.status).toBe('success') key.set(['key8']) fixture.detectChanges() @@ -403,13 +401,13 @@ describe('injectQuery', () => { const query = fixture.componentInstance.query expect(spy).not.toHaveBeenCalled() - expect(query.status()).toBe('pending') + expect(query.status).toBe('pending') enabled.set(true) await vi.advanceTimersByTimeAsync(11) expect(spy).toHaveBeenCalledTimes(1) - expect(query.status()).toBe('success') + expect(query.status).toBe('success') }) test('should properly execute dependant queries', async () => { @@ -432,7 +430,7 @@ describe('injectQuery', () => { computed(() => ({ queryKey: ['dependant2'], queryFn: dependentQueryFn, - enabled: !!this.query1.data(), + enabled: !!this.query1.data, })), ) } @@ -441,19 +439,19 @@ describe('injectQuery', () => { fixture.detectChanges() const { query1, query2 } = fixture.componentInstance - expect(query1.data()).toStrictEqual(undefined) - expect(query2.fetchStatus()).toStrictEqual('idle') + expect(query1.data).toStrictEqual(undefined) + expect(query2.fetchStatus).toStrictEqual('idle') expect(dependentQueryFn).not.toHaveBeenCalled() await vi.advanceTimersByTimeAsync(11) - expect(query1.data()).toStrictEqual('Some data') - expect(query2.fetchStatus()).toStrictEqual('fetching') + expect(query1.data).toStrictEqual('Some data') + expect(query2.fetchStatus).toStrictEqual('fetching') await vi.advanceTimersByTimeAsync(1002) - expect(query2.fetchStatus()).toStrictEqual('idle') - expect(query2.status()).toStrictEqual('success') + expect(query2.fetchStatus).toStrictEqual('idle') + expect(query2.status).toStrictEqual('success') expect(dependentQueryFn).toHaveBeenCalledTimes(1) expect(dependentQueryFn).toHaveBeenCalledWith( expect.objectContaining({ queryKey: ['dependant2'] }), @@ -652,17 +650,17 @@ describe('injectQuery', () => { fixture.detectChanges() const query = fixture.componentInstance.query - expect(query.status()).toBe('pending') + expect(query.status).toBe('pending') await vi.advanceTimersByTimeAsync(11) - expect(query.status()).toBe('error') + expect(query.status).toBe('error') }) test('should render with required signal inputs', async () => { @Component({ selector: 'app-fake', - template: `{{ query.data() }}`, + template: `{{ query.data }}`, changeDetection: ChangeDetectionStrategy.OnPush, }) class FakeComponent { @@ -726,7 +724,7 @@ describe('injectQuery', () => { fixture.detectChanges() const query = fixture.componentInstance.query - expect(query.status()).toBe('pending') + expect(query.status).toBe('pending') }) test('should complete queries before whenStable() resolves', async () => { @@ -751,14 +749,14 @@ describe('injectQuery', () => { fixture.detectChanges() const query = fixture.componentInstance.query - expect(query.status()).toBe('pending') - expect(query.data()).toBeUndefined() + expect(query.status).toBe('pending') + expect(query.data).toBeUndefined() await vi.advanceTimersByTimeAsync(60) await app.whenStable() - expect(query.status()).toBe('success') - expect(query.data()).toBe('test data') + expect(query.status).toBe('success') + expect(query.data).toBe('test data') }) test('should complete HttpClient-based queries before whenStable() resolves', async () => { @@ -803,15 +801,15 @@ describe('injectQuery', () => { }, 10) // Initial state - expect(query.status()).toBe('pending') + expect(query.status).toBe('pending') // Advance timers and wait for Angular to be "stable" await vi.advanceTimersByTimeAsync(20) await app.whenStable() // Query should be complete after whenStable() thanks to PendingTasks integration - expect(query.status()).toBe('success') - expect(query.data()).toEqual({ message: 'http test data' }) + expect(query.status).toBe('success') + expect(query.data).toEqual({ message: 'http test data' }) httpTestingController.verify() }) @@ -851,8 +849,8 @@ describe('injectQuery', () => { await app.whenStable() - expect(query.status()).toBe('success') - expect(query.data()).toBe('sync-data-1') + expect(query.status).toBe('success') + expect(query.data).toBe('sync-data-1') expect(component.callCount).toBe(1) await query.refetch() @@ -860,8 +858,8 @@ describe('injectQuery', () => { await vi.runAllTimersAsync() await app.whenStable() - expect(query.status()).toBe('success') - expect(query.data()).toBe('sync-data-2') + expect(query.status).toBe('success') + expect(query.data).toBe('sync-data-2') expect(component.callCount).toBe(2) }) @@ -903,8 +901,8 @@ describe('injectQuery', () => { // Initially disabled await vi.advanceTimersByTimeAsync(0) await app.whenStable() - expect(query.status()).toBe('pending') - expect(query.data()).toBeUndefined() + expect(query.status).toBe('pending') + expect(query.data).toBeUndefined() expect(component.callCount).toBe(0) // Enable the query @@ -913,8 +911,8 @@ describe('injectQuery', () => { await vi.advanceTimersByTimeAsync(0) await app.whenStable() - expect(query.status()).toBe('success') - expect(query.data()).toBe('sync-data-1') + expect(query.status).toBe('success') + expect(query.data).toBe('sync-data-1') expect(component.callCount).toBe(1) }) @@ -952,8 +950,8 @@ describe('injectQuery', () => { const query = component.query await app.whenStable() - expect(query.status()).toBe('success') - expect(query.data()).toBe('sync-data-1') + expect(query.status).toBe('success') + expect(query.data).toBe('sync-data-1') expect(component.callCount).toBe(1) // Invalidate the query @@ -964,8 +962,8 @@ describe('injectQuery', () => { await vi.advanceTimersByTimeAsync(10) await app.whenStable() - expect(query.status()).toBe('success') - expect(query.data()).toBe('sync-data-2') + expect(query.status).toBe('success') + expect(query.data).toBe('sync-data-2') expect(component.callCount).toBe(2) }) }) diff --git a/packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts b/packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts index 7e2c737474a..f1c66a14b6b 100644 --- a/packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts +++ b/packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts @@ -62,8 +62,8 @@ describe('PendingTasks Integration', () => { const query = fixture.componentInstance.query // Should start as pending even with synchronous data - expect(query.status()).toBe('pending') - expect(query.data()).toBeUndefined() + expect(query.status).toBe('pending') + expect(query.data).toBeUndefined() const stablePromise = app.whenStable() // Flush microtasks to allow TanStack Query's scheduled notifications to process @@ -72,8 +72,8 @@ describe('PendingTasks Integration', () => { await stablePromise // Should work correctly even though queryFn was synchronous - expect(query.status()).toBe('success') - expect(query.data()).toBe('instant-data') + expect(query.status).toBe('success') + expect(query.data).toBe('instant-data') }) test('should handle synchronous error with whenStable()', async () => { @@ -94,8 +94,8 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(10) await stablePromise - expect(query.status()).toBe('error') - expect(query.error()).toEqual(new Error('instant-error')) + expect(query.status).toBe('error') + expect(query.error).toEqual(new Error('instant-error')) }) test('should handle synchronous mutationFn with whenStable()', async () => { @@ -122,8 +122,8 @@ describe('PendingTasks Integration', () => { await stablePromise expect(mutationFnCalled).toBe(true) - expect(mutation.isSuccess()).toBe(true) - expect(mutation.data()).toBe('processed: test') + expect(mutation.isSuccess).toBe(true) + expect(mutation.data).toBe('processed: test') }) test('should handle synchronous mutation error with whenStable()', async () => { @@ -147,8 +147,8 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(10) await stablePromise - expect(mutation.isError()).toBe(true) - expect(mutation.error()).toEqual(new Error('sync-mutation-error')) + expect(mutation.isError).toBe(true) + expect(mutation.error).toEqual(new Error('sync-mutation-error')) }) }) @@ -176,8 +176,8 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(10) await stablePromise - expect(query.status()).toBe('success') - expect(query.data()).toBe('race-data') + expect(query.status).toBe('success') + expect(query.data).toBe('race-data') }) test('should handle rapid refetches without task leaks', async () => { @@ -213,8 +213,8 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(20) await stablePromise - expect(query.status()).toBe('success') - expect(query.data()).toMatch(/^data-\d+$/) + expect(query.status).toBe('success') + expect(query.data).toMatch(/^data-\d+$/) }) test('should keep PendingTasks active when query starts offline (never reaches fetching)', async () => { @@ -238,8 +238,8 @@ describe('PendingTasks Integration', () => { await flushQueryUpdates() // Query should initialize directly to 'paused' (never goes through 'fetching') - expect(query.status()).toBe('pending') - expect(query.fetchStatus()).toBe('paused') + expect(query.status).toBe('pending') + expect(query.fetchStatus).toBe('paused') const stablePromise = app.whenStable() let stableResolved = false @@ -261,8 +261,8 @@ describe('PendingTasks Integration', () => { await stablePromise expect(stableResolved).toBe(true) - expect(query.status()).toBe('success') - expect(query.data()).toBe('online-data') + expect(query.status).toBe('success') + expect(query.data).toBe('online-data') }) test('should keep PendingTasks active while query retry is paused offline', async () => { @@ -293,8 +293,8 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(10) await Promise.resolve() - expect(query.status()).toBe('pending') - expect(query.fetchStatus()).toBe('fetching') + expect(query.status).toBe('pending') + expect(query.fetchStatus).toBe('fetching') // Simulate the app going offline during retry delay onlineManager.setOnline(false) @@ -303,7 +303,7 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(50) await Promise.resolve() - expect(query.fetchStatus()).toBe('paused') + expect(query.fetchStatus).toBe('paused') const stablePromise = app.whenStable() let stableResolved = false @@ -315,7 +315,7 @@ describe('PendingTasks Integration', () => { // PendingTasks should continue blocking stability while the fetch is paused expect(stableResolved).toBe(false) - expect(query.status()).toBe('pending') + expect(query.status).toBe('pending') // Bring the app back online so the retry can continue onlineManager.setOnline(true) @@ -327,8 +327,8 @@ describe('PendingTasks Integration', () => { await stablePromise expect(stableResolved).toBe(true) - expect(query.status()).toBe('success') - expect(query.data()).toBe('final-data') + expect(query.status).toBe('success') + expect(query.data).toBe('final-data') }) }) @@ -360,7 +360,7 @@ describe('PendingTasks Integration', () => { fixture.detectChanges() // Start the query - expect(fixture.componentInstance.query.status()).toBe('pending') + expect(fixture.componentInstance.query.status).toBe('pending') // Destroy component while query is running fixture.destroy() @@ -427,21 +427,21 @@ describe('PendingTasks Integration', () => { const { query1, query2, query3 } = fixture.componentInstance // All queries should start - expect(query1.status()).toBe('pending') - expect(query2.status()).toBe('pending') - expect(query3.status()).toBe('pending') + expect(query1.status).toBe('pending') + expect(query2.status).toBe('pending') + expect(query3.status).toBe('pending') const stablePromise = app.whenStable() await vi.advanceTimersByTimeAsync(60) await stablePromise // All queries should be complete - expect(query1.status()).toBe('success') - expect(query1.data()).toBe('data-1') - expect(query2.status()).toBe('success') - expect(query2.data()).toBe('data-2') - expect(query3.status()).toBe('success') - expect(query3.data()).toBe('instant-data') + expect(query1.status).toBe('success') + expect(query1.data).toBe('data-1') + expect(query2.status).toBe('success') + expect(query2.data).toBe('data-2') + expect(query3.status).toBe('success') + expect(query3.data).toBe('instant-data') }) test('should handle multiple mutations running simultaneously', async () => { @@ -486,12 +486,12 @@ describe('PendingTasks Integration', () => { await stablePromise // All mutations should be complete - expect(mutation1.isSuccess()).toBe(true) - expect(mutation1.data()).toBe('processed-1: test1') - expect(mutation2.isSuccess()).toBe(true) - expect(mutation2.data()).toBe('processed-2: test2') - expect(mutation3.isSuccess()).toBe(true) - expect(mutation3.data()).toBe('processed-3: test3') + expect(mutation1.isSuccess).toBe(true) + expect(mutation1.data).toBe('processed-1: test1') + expect(mutation2.isSuccess).toBe(true) + expect(mutation2.data).toBe('processed-2: test2') + expect(mutation3.isSuccess).toBe(true) + expect(mutation3.data).toBe('processed-3: test3') }) test('should handle mixed queries and mutations', async () => { @@ -524,10 +524,10 @@ describe('PendingTasks Integration', () => { await stablePromise // Both should be complete - expect(query.status()).toBe('success') - expect(query.data()).toBe('query-data') - expect(mutation.isSuccess()).toBe(true) - expect(mutation.data()).toBe('mutation: test') + expect(query.status).toBe('success') + expect(query.data).toBe('query-data') + expect(mutation.isSuccess).toBe(true) + expect(mutation.data).toBe('mutation: test') }) }) @@ -572,10 +572,10 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(20) await stablePromise - expect(query1.status()).toBe('success') - expect(query1.data()).toEqual({ id: 1 }) - expect(query2.status()).toBe('success') - expect(query2.data()).toEqual({ id: 2 }) + expect(query1.status).toBe('success') + expect(query1.data).toEqual({ id: 1 }) + expect(query2.status).toBe('success') + expect(query2.data).toEqual({ id: 2 }) httpTestingController.verify() }) @@ -606,7 +606,7 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(20) await stablePromise - expect(query.status()).toBe('error') + expect(query.status).toBe('error') httpTestingController.verify() }) @@ -641,8 +641,8 @@ describe('PendingTasks Integration', () => { await stablePromise // Cancellation should restore the pre-fetch state - expect(query.status()).toBe('pending') - expect(query.fetchStatus()).toBe('idle') + expect(query.status).toBe('pending') + expect(query.fetchStatus).toBe('idle') }) test('should handle query retry and pending task tracking', async () => { @@ -668,8 +668,8 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(50) await stablePromise - expect(query.status()).toBe('success') - expect(query.data()).toBe('success-data') + expect(query.status).toBe('success') + expect(query.data).toBe('success-data') expect(attemptCount).toBe(3) // Initial + 2 retries }) @@ -711,8 +711,8 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(60) await stablePromise - expect(mutation.isSuccess()).toBe(true) - expect(mutation.data()).toBe('optimistic-data') + expect(mutation.isSuccess).toBe(true) + expect(mutation.data).toBe('optimistic-data') expect(queryClient.getQueryData(testQueryKey)).toBe('optimistic-data') }) }) diff --git a/packages/angular-query-experimental/src/__tests__/query-options.test-d.ts b/packages/angular-query-experimental/src/__tests__/query-options.test-d.ts index 350ab3dda28..b5d3eb39f47 100644 --- a/packages/angular-query-experimental/src/__tests__/query-options.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/query-options.test-d.ts @@ -1,6 +1,5 @@ import { assertType, describe, expectTypeOf, test } from 'vitest' import { QueryClient, dataTagSymbol, injectQuery, queryOptions } from '..' -import type { Signal } from '@angular/core' describe('queryOptions', () => { test('should not allow excess properties', () => { @@ -49,7 +48,7 @@ test('should work when passed to injectQuery', () => { }) const { data } = injectQuery(() => options) - expectTypeOf(data).toEqualTypeOf>() + expectTypeOf(data).toEqualTypeOf() }) test('should work when passed to fetchQuery', () => { diff --git a/packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts b/packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts index d06aef67230..79152428709 100644 --- a/packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts +++ b/packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts @@ -1,14 +1,24 @@ -import { isSignal, signal } from '@angular/core' -import { describe, expect, test } from 'vitest' +import { + ChangeDetectionStrategy, + Component, + computed, + input, + isSignal, + provideZonelessChangeDetection, + signal, +} from '@angular/core' +import { beforeEach, describe, expect, test } from 'vitest' import { signalProxy } from '../signal-proxy' +import { TestBed } from '@angular/core/testing' +import { registerSignalInput } from './test-utils' describe('signalProxy', () => { const inputSignal = signal({ fn: () => 'bar', baz: 'qux' }) const proxy = signalProxy(inputSignal) - test('should have computed fields', () => { - expect(proxy.baz()).toEqual('qux') - expect(isSignal(proxy.baz)).toBe(true) + test('should have reactive fields that return values directly', () => { + expect(proxy.baz).toEqual('qux') + expect(isSignal(proxy.baz)).toBe(false) }) test('should pass through functions as-is', () => { @@ -24,4 +34,52 @@ describe('signalProxy', () => { test('supports "Object.keys"', () => { expect(Object.keys(proxy)).toEqual(['fn', 'baz']) }) + + describe('with component input', () => { + beforeEach(() => { + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [provideZonelessChangeDetection()], + }) + }) + + test('should work with component input wraped on a function', () => { + @Component({ + standalone: true, + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + in = input.required() + derived = computed(() => ({ in: this.in(), fn: () => 'bar' })) + proxy = signalProxy(this.derived) + inProxied = computed(() => this.proxy.in) + fn = () => this.proxy.fn() + } + registerSignalInput(TestComponent, 'in') + const fixture = TestBed.createComponent(TestComponent) + fixture.componentRef.setInput('in', 'value') + fixture.detectChanges() + + expect(fixture.componentInstance.inProxied()).toBe('value') + expect(fixture.componentInstance.fn()).toBe('bar') + }) + + test('should not work with component input not wraped on a function', () => { + @Component({ + standalone: true, + template: '', + }) + class TestComponent { + in = input.required() + derived = computed(() => ({ in: this.in(), fn: () => 'bar' })) + proxy = signalProxy(this.derived) + inProxied = this.proxy.in + fn = this.proxy.fn + } + + registerSignalInput(TestComponent, 'in') + expect(() => TestBed.createComponent(TestComponent)).toThrow(/NG0950/) + }) + }) }) diff --git a/packages/angular-query-experimental/src/__tests__/test-utils.ts b/packages/angular-query-experimental/src/__tests__/test-utils.ts index 39884df261d..fab0ba93d1a 100644 --- a/packages/angular-query-experimental/src/__tests__/test-utils.ts +++ b/packages/angular-query-experimental/src/__tests__/test-utils.ts @@ -1,49 +1,9 @@ -import { - isSignal, - provideZonelessChangeDetection, - untracked, -} from '@angular/core' +import { provideZonelessChangeDetection } from '@angular/core' import { TestBed } from '@angular/core/testing' -import { expect, vi } from 'vitest' +import { vi } from 'vitest' import { provideTanStackQuery } from '..' import type { QueryClient } from '@tanstack/query-core' -import type { - EnvironmentProviders, - Provider, - Signal, - Type, -} from '@angular/core' - -// Evaluate all signals on an object and return the result -function evaluateSignals>( - obj: T, -): { [K in keyof T]: ReturnType } { - const result: Partial<{ [K in keyof T]: ReturnType }> = {} - - untracked(() => { - for (const key in obj) { - if ( - Object.prototype.hasOwnProperty.call(obj, key) && - // Only evaluate signals, not normal functions - isSignal(obj[key]) - ) { - const func = obj[key] - result[key] = func() - } - } - }) - - return result as { [K in keyof T]: ReturnType } -} - -export const expectSignals = >( - obj: T, - expected: Partial<{ - [K in keyof T]: T[K] extends Signal ? ReturnType : never - }>, -): void => { - expect(evaluateSignals(obj)).toMatchObject(expected) -} +import type { EnvironmentProviders, Provider, Type } from '@angular/core' /** * Reset Angular's TestBed and configure the standard TanStack Query providers for tests. diff --git a/packages/angular-query-experimental/src/inject-queries.ts b/packages/angular-query-experimental/src/inject-queries.ts index 2f201799e74..92f6353f98e 100644 --- a/packages/angular-query-experimental/src/inject-queries.ts +++ b/packages/angular-query-experimental/src/inject-queries.ts @@ -326,5 +326,5 @@ export function injectQueries< signalProxy(signal(query)), ) }) - }) as unknown as Signal + }) as Signal } diff --git a/packages/angular-query-experimental/src/inject-query.ts b/packages/angular-query-experimental/src/inject-query.ts index 1dac0ab6949..02debcd1ed2 100644 --- a/packages/angular-query-experimental/src/inject-query.ts +++ b/packages/angular-query-experimental/src/inject-query.ts @@ -222,5 +222,5 @@ export function injectQuery( !options?.injector && assertInInjectionContext(injectQuery) return runInInjectionContext(options?.injector ?? inject(Injector), () => createBaseQuery(injectQueryFn, QueryObserver), - ) as unknown as CreateQueryResult + ) as CreateQueryResult } diff --git a/packages/angular-query-experimental/src/signal-proxy.ts b/packages/angular-query-experimental/src/signal-proxy.ts index e2a9de345f6..c0b3963ec54 100644 --- a/packages/angular-query-experimental/src/signal-proxy.ts +++ b/packages/angular-query-experimental/src/signal-proxy.ts @@ -1,34 +1,52 @@ import { computed, untracked } from '@angular/core' import type { Signal } from '@angular/core' -export type MapToSignals = { - [K in keyof T]: T[K] extends Function ? T[K] : Signal -} +/** + * Reactive proxy type that wraps the input type. + * Note: This type cannot be destructured on component initialization as it would break reactivity. + * Always access properties directly on the proxy object (e.g., `proxy.field` not `const { field } = proxy`). + */ +export type ReactiveProxy = T /** - * Exposes fields of an object passed via an Angular `Signal` as `Computed` signals. - * Functions on the object are passed through as-is. + * Exposes fields of an object passed via an Angular `Signal` as reactive values. + * Functions on the object are passed through as-is, bound to the current reactive state. * @param inputSignal - `Signal` that must return an object. - * @returns A proxy object with the same fields as the input object, but with each field wrapped in a `Computed` signal. + * @returns A proxy object with the same fields as the input object, where properties return values directly (like Svelte/Solid/Vue). + * @remarks This proxy cannot be destructured on component initialization as it would break reactivity. + * Always access properties directly on the returned proxy object. */ export function signalProxy>( inputSignal: Signal, ) { - const internalState = {} as MapToSignals - - return new Proxy>(internalState, { - get(target, prop) { - // first check if we have it in our internal state and return it - const computedField = target[prop] - if (computedField) return computedField + const computedCache = new Map any>() - // then, check if it's a function on the resultState and return it - const targetField = untracked(inputSignal)[prop] - if (typeof targetField === 'function') return targetField + return new Proxy({} as ReactiveProxy, { + get(_target, prop) { + let targetField: any + try { + targetField = untracked(inputSignal)[prop] + } catch (error) { + if (error instanceof Error && /NG0950/.test(error.message)) { + throw new Error(EARLY_INPUT_ERROR, { cause: error }) + } + throw error + } + if (typeof targetField === 'function') { + return function (this: any, ...args: any[]) { + const currentState = inputSignal() + return targetField.apply(currentState, args) + } + } - // finally, create a computed field, store it and return it - // @ts-expect-error - return (target[prop] = computed(() => inputSignal()[prop])) + if (!computedCache.has(prop)) { + computedCache.set( + prop, + computed(() => inputSignal()[prop]), + ) + } + const computedSignal = computedCache.get(prop)! + return computedSignal() }, has(_, prop) { return !!untracked(inputSignal)[prop] @@ -44,3 +62,6 @@ export function signalProxy>( }, }) } + +const EARLY_INPUT_ERROR = + 'signalProxy: Dependant input of proxy state is not yet initialized (NG0950). Do not destructure the result before component or directive initialization.' diff --git a/packages/angular-query-experimental/src/types.ts b/packages/angular-query-experimental/src/types.ts index 1d4bf86487a..2436cfd3d4d 100644 --- a/packages/angular-query-experimental/src/types.ts +++ b/packages/angular-query-experimental/src/types.ts @@ -15,8 +15,7 @@ import type { QueryObserverOptions, QueryObserverResult, } from '@tanstack/query-core' -import type { Signal } from '@angular/core' -import type { MapToSignals } from './signal-proxy' +import type { ReactiveProxy } from './signal-proxy' export interface CreateBaseQueryOptions< TQueryFnData = unknown, @@ -48,36 +47,6 @@ export interface CreateQueryOptions< 'suspense' > {} -type CreateStatusBasedQueryResult< - TStatus extends QueryObserverResult['status'], - TData = unknown, - TError = DefaultError, -> = Extract, { status: TStatus }> - -export interface BaseQueryNarrowing { - isSuccess: ( - this: CreateBaseQueryResult, - ) => this is CreateBaseQueryResult< - TData, - TError, - CreateStatusBasedQueryResult<'success', TData, TError> - > - isError: ( - this: CreateBaseQueryResult, - ) => this is CreateBaseQueryResult< - TData, - TError, - CreateStatusBasedQueryResult<'error', TData, TError> - > - isPending: ( - this: CreateBaseQueryResult, - ) => this is CreateBaseQueryResult< - TData, - TError, - CreateStatusBasedQueryResult<'pending', TData, TError> - > -} - export interface CreateInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, @@ -99,8 +68,7 @@ export type CreateBaseQueryResult< TData = unknown, TError = DefaultError, TState = QueryObserverResult, -> = BaseQueryNarrowing & - MapToSignals> +> = ReactiveProxy export type CreateQueryResult< TData = unknown, @@ -111,14 +79,12 @@ export type DefinedCreateQueryResult< TData = unknown, TError = DefaultError, TState = DefinedQueryObserverResult, -> = BaseQueryNarrowing & - MapToSignals> +> = ReactiveProxy export type CreateInfiniteQueryResult< TData = unknown, TError = DefaultError, -> = BaseQueryNarrowing & - MapToSignals> +> = ReactiveProxy> export type DefinedCreateInfiniteQueryResult< TData = unknown, @@ -127,7 +93,7 @@ export type DefinedCreateInfiniteQueryResult< TData, TError >, -> = MapToSignals +> = ReactiveProxy export interface CreateMutationOptions< TData = unknown, @@ -185,84 +151,6 @@ type CreateStatusBasedMutationResult< { status: TStatus } > -type SignalFunction any> = T & Signal> - -export interface BaseMutationNarrowing< - TData = unknown, - TError = DefaultError, - TVariables = unknown, - TOnMutateResult = unknown, -> { - isSuccess: SignalFunction< - ( - this: CreateMutationResult, - ) => this is CreateMutationResult< - TData, - TError, - TVariables, - TOnMutateResult, - CreateStatusBasedMutationResult< - 'success', - TData, - TError, - TVariables, - TOnMutateResult - > - > - > - isError: SignalFunction< - ( - this: CreateMutationResult, - ) => this is CreateMutationResult< - TData, - TError, - TVariables, - TOnMutateResult, - CreateStatusBasedMutationResult< - 'error', - TData, - TError, - TVariables, - TOnMutateResult - > - > - > - isPending: SignalFunction< - ( - this: CreateMutationResult, - ) => this is CreateMutationResult< - TData, - TError, - TVariables, - TOnMutateResult, - CreateStatusBasedMutationResult< - 'pending', - TData, - TError, - TVariables, - TOnMutateResult - > - > - > - isIdle: SignalFunction< - ( - this: CreateMutationResult, - ) => this is CreateMutationResult< - TData, - TError, - TVariables, - TOnMutateResult, - CreateStatusBasedMutationResult< - 'idle', - TData, - TError, - TVariables, - TOnMutateResult - > - > - > -} - export type CreateMutationResult< TData = unknown, TError = DefaultError, @@ -275,5 +163,4 @@ export type CreateMutationResult< TVariables, TOnMutateResult >, -> = BaseMutationNarrowing & - MapToSignals> +> = ReactiveProxy