Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(angular-query): change query functions to accept an options object instead of an injector #8776

Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ export default class LazyLoadDevtoolsPanelExampleComponent {
this.devtools.set(
import('@tanstack/angular-query-devtools-experimental').then(
({ injectDevtoolsPanel }) =>
injectDevtoolsPanel(this.devToolsOptions, this.injector),
injectDevtoolsPanel(this.devToolsOptions, {
injector: this.injector,
}),
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,40 @@ import { isPlatformBrowser } from '@angular/common'
import type { ElementRef } from '@angular/core'
import type { DevtoolsErrorType } from '@tanstack/query-devtools'

export interface InjectDevtoolsPanelOptions {
/**
* The `Injector` in which to create the devtools panel.
*
* If this is not provided, the current injection context will be used instead (via `inject`).
*/
injector?: Injector
}

/**
* Inject a TanStack Query devtools panel and render it in the DOM.
*
* Devtools panel allows programmatic control over the devtools, for example if you want to render
* the devtools as part of your own devtools.
*
* Consider `withDevtools` instead if you don't need this.
* @param optionsFn - A function that returns devtools panel options.
* @param injector - The Angular injector to use.
* @param injectDevtoolsPanelFn - A function that returns devtools panel options.
* @param options - Additional configuration
* @returns DevtoolsPanelRef
* @see https://tanstack.com/query/v5/docs/framework/angular/devtools
*/
export function injectDevtoolsPanel(
optionsFn: () => DevtoolsPanelOptions,
injector?: Injector,
injectDevtoolsPanelFn: () => DevtoolsPanelOptions,
options?: InjectDevtoolsPanelOptions,
): DevtoolsPanelRef {
!injector && assertInInjectionContext(injectDevtoolsPanel)
const currentInjector = injector ?? inject(Injector)
!options?.injector && assertInInjectionContext(injectDevtoolsPanel)
const currentInjector = options?.injector ?? inject(Injector)

return runInInjectionContext(currentInjector, () => {
const destroyRef = inject(DestroyRef)
const isBrowser = isPlatformBrowser(inject(PLATFORM_ID))
const injectedClient = inject(QueryClient, { optional: true })

const options = computed(optionsFn)
const queryOptions = computed(injectDevtoolsPanelFn)
let devtools: TanstackQueryDevtoolsPanel | null = null

const destroy = () => {
Expand All @@ -63,7 +72,7 @@ export function injectDevtoolsPanel(
shadowDOMTarget,
onClose,
hostElement,
} = options()
} = queryOptions()

untracked(() => {
if (!client) throw new Error('No QueryClient found')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ describe('injectInfiniteQuery', () => {
initialPageParam: 0,
getNextPageParam: () => 12,
}),
TestBed.inject(Injector),
{
injector: TestBed.inject(Injector),
},
)

expect(query.status()).toBe('pending')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ describe('injectIsFetching', () => {

test('can be used outside injection context when passing an injector', () => {
expect(
injectIsFetching(undefined, TestBed.inject(Injector)),
injectIsFetching(undefined, {
injector: TestBed.inject(Injector),
}),
).not.toThrow()
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ describe('injectIsMutating', () => {

test('can be used outside injection context when passing an injector', () => {
expect(
injectIsMutating(undefined, TestBed.inject(Injector)),
injectIsMutating(undefined, {
injector: TestBed.inject(Injector),
}),
).not.toThrow()
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,9 @@ describe('injectMutation', () => {
mutationKey: ['injectionContextError'],
mutationFn: () => Promise.resolve(),
}),
TestBed.inject(Injector),
{
injector: TestBed.inject(Injector),
},
)
}).not.toThrow()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,9 @@ describe('injectQuery', () => {
queryKey: ['manualInjector'],
queryFn: simpleFetcher,
}),
TestBed.inject(Injector),
{
injector: TestBed.inject(Injector),
},
)

expect(query.status()).toBe('pending')
Expand Down
54 changes: 35 additions & 19 deletions packages/angular-query-experimental/src/inject-infinite-query.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { InfiniteQueryObserver } from '@tanstack/query-core'
import { createBaseQuery } from './create-base-query'
import { assertInjector } from './util/assert-injector/assert-injector'
import type { Injector } from '@angular/core'
import type {
DefaultError,
InfiniteData,
Expand All @@ -17,12 +16,22 @@ import type {
DefinedInitialDataInfiniteOptions,
UndefinedInitialDataInfiniteOptions,
} from './infinite-query-options'
import type { Injector } from '@angular/core'

export interface InjectInfiniteQueryOptions {
/**
* The `Injector` in which to create the infinite query.
*
* If this is not provided, the current injection context will be used instead (via `inject`).
*/
injector?: Injector
}

/**
* Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key.
* Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll"
* @param optionsFn - A function that returns infinite query options.
* @param injector - The Angular injector to use.
* @param injectInfiniteQueryFn - A function that returns infinite query options.
* @param options - Additional configuration.
* @returns The infinite query result.
* @public
*/
Expand All @@ -33,21 +42,21 @@ export function injectInfiniteQuery<
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
optionsFn: () => DefinedInitialDataInfiniteOptions<
injectInfiniteQueryFn: () => DefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
injector?: Injector,
options?: InjectInfiniteQueryOptions,
): DefinedCreateInfiniteQueryResult<TData, TError>

/**
* Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key.
* Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll"
* @param optionsFn - A function that returns infinite query options.
* @param injector - The Angular injector to use.
* @param injectInfiniteQueryFn - A function that returns infinite query options.
* @param options - Additional configuration.
* @returns The infinite query result.
* @public
*/
Expand All @@ -58,21 +67,21 @@ export function injectInfiniteQuery<
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
optionsFn: () => UndefinedInitialDataInfiniteOptions<
injectInfiniteQueryFn: () => UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
injector?: Injector,
options?: InjectInfiniteQueryOptions,
): CreateInfiniteQueryResult<TData, TError>

/**
* Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key.
* Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll"
* @param optionsFn - A function that returns infinite query options.
* @param injector - The Angular injector to use.
* @param injectInfiniteQueryFn - A function that returns infinite query options.
* @param options - Additional configuration.
* @returns The infinite query result.
* @public
*/
Expand All @@ -83,30 +92,37 @@ export function injectInfiniteQuery<
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
optionsFn: () => CreateInfiniteQueryOptions<
injectInfiniteQueryFn: () => CreateInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryFnData,
TQueryKey,
TPageParam
>,
injector?: Injector,
options?: InjectInfiniteQueryOptions,
): CreateInfiniteQueryResult<TData, TError>

/**
* Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key.
* Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll"
* @param optionsFn - A function that returns infinite query options.
* @param injector - The Angular injector to use.
* @param injectInfiniteQueryFn - A function that returns infinite query options.
* @param options - Additional configuration.
* @returns The infinite query result.
* @public
*/
export function injectInfiniteQuery(
optionsFn: () => CreateInfiniteQueryOptions,
injector?: Injector,
injectInfiniteQueryFn: () => CreateInfiniteQueryOptions,
options?: InjectInfiniteQueryOptions,
) {
return assertInjector(injectInfiniteQuery, injector, () =>
createBaseQuery(optionsFn, InfiniteQueryObserver as typeof QueryObserver),
return assertInjector(injectInfiniteQuery, options?.injector, () =>
createBaseQuery(
injectInfiniteQueryFn,
InfiniteQueryObserver as typeof QueryObserver,
),
)
}

export interface InjectInfiniteQueryOptions {
injector?: Injector
}
15 changes: 12 additions & 3 deletions packages/angular-query-experimental/src/inject-is-fetching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@ import { assertInjector } from './util/assert-injector/assert-injector'
import type { QueryFilters } from '@tanstack/query-core'
import type { Injector, Signal } from '@angular/core'

export interface InjectIsFetchingOptions {
/**
* The `Injector` in which to create the isFetching signal.
*
* If this is not provided, the current injection context will be used instead (via `inject`).
*/
injector?: Injector
}

/**
* Injects a signal that tracks the number of queries that your application is loading or
* fetching in the background.
*
* Can be used for app-wide loading indicators
* @param filters - The filters to apply to the query.
* @param injector - The Angular injector to use.
* @param options - Additional configuration
* @returns signal with number of loading or fetching queries.
* @public
*/
export function injectIsFetching(
filters?: QueryFilters,
injector?: Injector,
options?: InjectIsFetchingOptions,
): Signal<number> {
return assertInjector(injectIsFetching, injector, () => {
return assertInjector(injectIsFetching, options?.injector, () => {
const destroyRef = inject(DestroyRef)
const ngZone = inject(NgZone)
const queryClient = inject(QueryClient)
Expand Down
15 changes: 12 additions & 3 deletions packages/angular-query-experimental/src/inject-is-mutating.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,29 @@ import { assertInjector } from './util/assert-injector/assert-injector'
import type { MutationFilters } from '@tanstack/query-core'
import type { Injector, Signal } from '@angular/core'

export interface InjectIsMutatingOptions {
/**
* The `Injector` in which to create the isMutating signal.
*
* If this is not provided, the current injection context will be used instead (via `inject`).
*/
injector?: Injector
}

/**
* Injects a signal that tracks the number of mutations that your application is fetching.
*
* Can be used for app-wide loading indicators
* @param filters - The filters to apply to the query.
* @param injector - The Angular injector to use.
* @param options - Additional configuration
* @returns signal with number of fetching mutations.
* @public
*/
export function injectIsMutating(
filters?: MutationFilters,
injector?: Injector,
options?: InjectIsMutatingOptions,
): Signal<number> {
return assertInjector(injectIsMutating, injector, () => {
return assertInjector(injectIsMutating, options?.injector, () => {
const destroyRef = inject(DestroyRef)
const ngZone = inject(NgZone)
const queryClient = inject(QueryClient)
Expand Down
18 changes: 14 additions & 4 deletions packages/angular-query-experimental/src/inject-mutation-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ type MutationStateOptions<TResult = MutationState> = {
select?: (mutation: Mutation) => TResult
}

/**
*
* @param mutationCache
* @param options
*/
function getResult<TResult = MutationState>(
mutationCache: MutationCache,
options: MutationStateOptions<TResult>,
Expand All @@ -34,18 +39,23 @@ function getResult<TResult = MutationState>(
* @public
*/
export interface InjectMutationStateOptions {
/**
* The `Injector` in which to create the mutation state signal.
*
* If this is not provided, the current injection context will be used instead (via `inject`).
*/
injector?: Injector
}

/**
* Injects a signal that tracks the state of all mutations.
* @param mutationStateOptionsFn - A function that returns mutation state options.
* @param injectMutationStateFn - A function that returns mutation state options.
* @param options - The Angular injector to use.
* @returns The signal that tracks the state of all mutations.
* @public
*/
export function injectMutationState<TResult = MutationState>(
mutationStateOptionsFn: () => MutationStateOptions<TResult> = () => ({}),
injectMutationStateFn: () => MutationStateOptions<TResult> = () => ({}),
options?: InjectMutationStateOptions,
): Signal<Array<TResult>> {
return assertInjector(injectMutationState, options?.injector, () => {
Expand All @@ -61,7 +71,7 @@ export function injectMutationState<TResult = MutationState>(
*/
const resultFromOptionsSignal = computed(() => {
return [
getResult(mutationCache, mutationStateOptionsFn()),
getResult(mutationCache, injectMutationStateFn()),
performance.now(),
] as const
})
Expand Down Expand Up @@ -91,7 +101,7 @@ export function injectMutationState<TResult = MutationState>(
const [lastResult] = effectiveResultSignal()
const nextResult = replaceEqualDeep(
lastResult,
getResult(mutationCache, mutationStateOptionsFn()),
getResult(mutationCache, injectMutationStateFn()),
)
if (lastResult !== nextResult) {
ngZone.run(() => {
Expand Down
Loading
Loading