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

feat: update angular version, code DX, examples and tests #1

Merged
merged 13 commits into from
Nov 13, 2023
Merged
42 changes: 24 additions & 18 deletions docs/angular/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ We are in the process of getting to a stable API for Angular Query. If you have
The adapter works with signals, which means it only supports Angular 16+

## Example

```typescript
import { AngularQueryDevtoolsComponent } from '@tanstack/angular-query-devtools-experimental'
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { CommonModule } from '@angular/common'
import { injectQuery } from '@tanstack/angular-query-experimental'
import axios from 'axios'
import { HttpClient } from '@angular/common/http'
import { lastValueFrom } from 'rxjs/src'
eneajaho marked this conversation as resolved.
Show resolved Hide resolved

type Response = {
name: string
Expand All @@ -34,28 +35,33 @@ type Response = {
selector: 'simple-example',
standalone: true,
template: `
<ng-container *ngIf="query().isPending">Loading...</ng-container>
<ng-container *ngIf="query().error as error">
An error has occurred: {{ error?.message }}
</ng-container>
<div *ngIf="query().data as data">
<h1>{{ data.name }}</h1>
<p>{{ data.description }}</p>
<strong>👀 {{ data.subscribers_count }}</strong>
<strong>✨ {{ data.stargazers_count }}</strong>
<strong>🍴 {{ data.forks_count }}</strong>
</div>
@if (query().isPending) {
Loading...
} @else if (query().error) {
An error has occurred: {{ query().error?.message }}
} @else {
@if (query().data; as data) {
<h1>{{ data.name }}</h1>
<p>{{ data.description }}</p>
<strong>👀 {{ data.subscribers_count }}</strong>
<strong>✨ {{ data.stargazers_count }}</strong>
<strong>🍴 {{ data.forks_count }}</strong>
}
}

<angular-query-devtools initialIsOpen />
`,
imports: [AngularQueryDevtoolsComponent, CommonModule],
imports: [AngularQueryDevtoolsComponent],
})
export class SimpleExampleComponent {
private http = inject(HttpClient)

query = injectQuery(() => ({
queryKey: ['repoData'],
queryFn: () =>
axios
.get('https://api.github.com/repos/tannerlinsley/react-query')
.then((res) => res.data as Response),
queryFn: () => lastValueFrom(
this.http.get('https://api.github.com/repos/tannerlinsley/react-query')
.pipe(map((res) => res.data as Response),),
)
}))
}
```
20 changes: 9 additions & 11 deletions docs/angular/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,15 @@ import { getTodos, postTodo } from '../my-api'

@Component({
standalone: true,
imports: [CommonModule],
template: `
<div>
<ul>
<li *ngFor="let todo of query().data">
{{ todo.title }}
</li>
@for (todo of query().data) {
<li>{{ todo.title }}</li>
}
</ul>

<button
(click)="onAddTodo()"
>
Add Todo
</button>
<button (click)="onAddTodo()">Add Todo</button>
</div>
`,
})
Expand All @@ -54,10 +49,13 @@ export class TodosComponent {
queryFn: getTodos
}))

mutation = injectMutation(() => ({
mutation = injectMutation((client) => ({
mutationFn: postTodo,
onSuccess: () => {
// Invalidate and refetch
// Invalidate and refetch by using the client directly
client.invalidateQueries({ queryKey: ['todos'] })

// OR use the queryClient that is injected into the component
this.queryClient.invalidateQueries({ queryKey: ['todos'] })
}
}))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {
Input,
ViewChild,
booleanAttribute,
inject,
} from '@angular/core'
import {
injectQueryClient,
QUERY_CLIENT,
onlineManager,
} from '@tanstack/angular-query-experimental'
import type { QueryClient } from '@tanstack/angular-query-experimental'
Expand All @@ -31,16 +32,13 @@ type DevtoolsPosition = DevtoolsPositionOriginal
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AngularQueryDevtoolsComponent implements AfterViewInit, OnDestroy {
readonly #injectedClient: QueryClient | null
readonly #injectedClient = inject<QueryClient>(QUERY_CLIENT, {
optional: true,
})
eneajaho marked this conversation as resolved.
Show resolved Hide resolved

#clientFromAttribute: QueryClient | null = null
#devtools: TanstackQueryDevtools | undefined

constructor() {
this.#injectedClient = injectQueryClient({
optional: true,
})
}

@ViewChild('ref') ref!: ElementRef

#initialIsOpen = false
Expand Down Expand Up @@ -136,6 +134,6 @@ export class AngularQueryDevtoolsComponent implements AfterViewInit, OnDestroy {
`You must either provide a client via 'provideAngularQuery' or pass it to the 'client' attribute of angular-query-devtools.`,
)
}
return this.#clientFromAttribute ?? this.#injectedClient!
return this.#clientFromAttribute ?? this.#injectedClient
}
}
16 changes: 8 additions & 8 deletions packages/angular-query-experimental/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@
},
"devDependencies": {
"@analogjs/vite-plugin-angular": "^0.2.18",
"@angular/animations": "^17.0.0-rc.2",
"@angular/cli": "^17.0.0-next.9",
"@angular/common": "^17.0.0-next.8",
"@angular/compiler": "^17.0.0-next.8",
"@angular/compiler-cli": "^17.0.0-rc.2",
"@angular/core": "^17.0.0-next.8",
"@angular/platform-browser": "^17.0.0-rc.2",
"@angular/platform-browser-dynamic": "^17.0.0-rc.2",
"@angular/animations": "^17.0.2",
"@angular/cli": "^17.0.0",
"@angular/common": "^17.0.2",
"@angular/compiler": "^17.0.2",
"@angular/compiler-cli": "^17.0.2",
"@angular/core": "^17.0.2",
"@angular/platform-browser": "^17.0.2",
"@angular/platform-browser-dynamic": "^17.0.2",
"ng-packagr": "^16.2.3",
"reflect-metadata": "^0.1.13",
"typescript": "5.2.2",
Expand Down
11 changes: 8 additions & 3 deletions packages/angular-query-experimental/src/createBaseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export function createBaseQuery<
TQueryData,
TQueryKey extends QueryKey,
>(
options: () => CreateBaseQueryOptions<
options: (
queryClient: QueryClient,
) => CreateBaseQueryOptions<
TQueryFnData,
TError,
TData,
Expand All @@ -32,10 +34,13 @@ export function createBaseQuery<
queryClient: QueryClient,
): CreateBaseQueryResult<TData, TError> {
assertInInjectionContext(createBaseQuery)
const destroyRef = inject(DestroyRef)

/** Creates a signal that has the default options applied */
const defaultedOptionsSignal = computed(() => {
const defaultedOptions = queryClient.defaultQueryOptions(options())
const defaultedOptions = queryClient.defaultQueryOptions(
options(queryClient),
)
defaultedOptions._optimisticResults = 'optimistic'
return defaultedOptions
})
Expand All @@ -61,7 +66,7 @@ export function createBaseQuery<
result.set(val)
}),
)
const destroyRef = inject(DestroyRef)

destroyRef.onDestroy(unsubscribe)

/** Subscribe to changes in result and defaultedOptionsStore */
Expand Down
6 changes: 5 additions & 1 deletion packages/angular-query-experimental/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,9 @@ export * from './injectIsMutating'
export * from './injectMutation'
export * from './injectQueries'
export * from './injectQuery'
export { injectQueryClient, provideQueryClient } from './injectQueryClient'
export {
injectQueryClient,
provideQueryClient,
QUERY_CLIENT,
} from './injectQueryClient'
export * from './providers'
22 changes: 8 additions & 14 deletions packages/angular-query-experimental/src/injectIsFetching.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,23 @@
import {
DestroyRef,
Injector,
assertInInjectionContext,
inject,
runInInjectionContext,
signal,
} from '@angular/core'
import { DestroyRef, Injector, inject, signal } from '@angular/core'
import { type QueryFilters, notifyManager } from '@tanstack/query-core'
import { injectQuery } from './injectQuery'
import { QUERY_CLIENT } from './injectQueryClient'
import { injectQueryClient } from './injectQueryClient'
import type { Signal } from '@angular/core'
import { assertInjector } from 'ngxtension/assert-injector'

export function injectIsFetching(
filters?: QueryFilters,
injector?: Injector,
): Signal<number> {
!injector && assertInInjectionContext(injectQuery)
const assertedInjector = injector ?? inject(Injector)
return runInInjectionContext(assertedInjector, () => {
const queryClient = inject(QUERY_CLIENT)
return assertInjector(injectIsFetching, injector, () => {
const queryClient = injectQueryClient()
const destroyRef = inject(DestroyRef)

const cache = queryClient.getQueryCache()
// isFetching is the prev value initialized on mount *
let isFetching = queryClient.isFetching(filters)

const result = signal(isFetching)

const unsubscribe = cache.subscribe(
notifyManager.batchCalls(() => {
const newIsFetching = queryClient.isFetching(filters)
Expand All @@ -37,6 +30,7 @@ export function injectIsFetching(
)

destroyRef.onDestroy(unsubscribe)

return result
})
}
22 changes: 8 additions & 14 deletions packages/angular-query-experimental/src/injectIsMutating.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,23 @@
import {
DestroyRef,
assertInInjectionContext,
inject,
runInInjectionContext,
signal,
} from '@angular/core'
import { DestroyRef, inject, signal } from '@angular/core'
import { type MutationFilters, notifyManager } from '@tanstack/query-core'
import { assertInjector } from 'ngxtension/assert-injector'
import { injectQuery } from './injectQuery'
import { QUERY_CLIENT } from './injectQueryClient'
import { injectQueryClient } from './injectQueryClient'
import type { Injector, Signal } from '@angular/core'

export function injectIsMutating(
filters?: MutationFilters,
injector?: Injector,
): Signal<number> {
injector = assertInjector(injectQuery, injector)
return runInInjectionContext(injector, () => {
const queryClient = inject(QUERY_CLIENT)
return assertInjector(injectIsMutating, injector, () => {
const queryClient = injectQueryClient()
const destroyRef = inject(DestroyRef)

assertInInjectionContext(injectIsMutating)
const cache = queryClient.getMutationCache()
// isMutating is the prev value initialized on mount *
let isMutating = queryClient.isMutating(filters)

const result = signal(isMutating)

const unsubscribe = cache.subscribe(
notifyManager.batchCalls(() => {
const newIsMutating = queryClient.isMutating(filters)
Expand All @@ -35,8 +28,9 @@ export function injectIsMutating(
}
}),
)
const destroyRef = inject(DestroyRef)

destroyRef.onDestroy(unsubscribe)

return result
})
}
33 changes: 16 additions & 17 deletions packages/angular-query-experimental/src/injectMutation.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { DestroyRef, computed, effect, inject, signal } from '@angular/core'
import {
DestroyRef,
computed,
effect,
inject,
runInInjectionContext,
signal,
} from '@angular/core'
import { MutationObserver, notifyManager } from '@tanstack/query-core'
MutationObserver,
notifyManager,
QueryClient,
} from '@tanstack/query-core'
import { assertInjector } from 'ngxtension/assert-injector'
import { injectQuery } from './injectQuery'
import { QUERY_CLIENT } from './injectQueryClient'
import { injectQueryClient } from './injectQueryClient'
import type { DefaultError } from '@tanstack/query-core'
import type { Injector } from '@angular/core'

Expand All @@ -25,16 +21,18 @@ export function injectMutation<
TVariables = void,
TContext = unknown,
>(
options: () => CreateMutationOptions<TData, TError, TVariables, TContext>,
options: (
client: QueryClient,
) => CreateMutationOptions<TData, TError, TVariables, TContext>,
injector?: Injector,
): CreateMutationResult<TData, TError, TVariables, TContext> {
injector = assertInjector(injectQuery, injector)
return runInInjectionContext(injector, () => {
const queryClient = inject(QUERY_CLIENT)
return assertInjector(injectMutation, injector, () => {
const queryClient = injectQueryClient()
const destroyRef = inject(DestroyRef)

const observer = new MutationObserver<TData, TError, TVariables, TContext>(
queryClient,
options(),
options(queryClient),
)
const mutate: CreateMutateFunction<TData, TError, TVariables, TContext> = (
variables,
Expand All @@ -44,16 +42,17 @@ export function injectMutation<
}

effect(() => {
observer.setOptions(options())
observer.setOptions(options(queryClient))
})

const result = signal(observer.getCurrentResult())

const unsubscribe = observer.subscribe(
notifyManager.batchCalls((val) => {
result.set(val)
}),
)
const destroyRef = inject(DestroyRef)

destroyRef.onDestroy(unsubscribe)

return computed(() => ({
Expand Down
Loading