-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs(angular-query): add auto-refetching example (#8371)
* feat(examples): add angular auto-refetching example Implement an example showcasing auto-refetching in Angular using TanStack Query. Includes a mock API interceptor to simulate HTTP calls for tasks. * feat(examples): add angular auto-refetching example Implement an example showcasing auto-refetching in Angular using TanStack Query. Includes a mock API interceptor to simulate HTTP calls for tasks. * feat(examples): update lock file * Update lock file * ci: apply automated fixes * Update examples/angular/auto-refetching/.devcontainer/devcontainer.json Co-authored-by: Arnoud <6420061+arnoud-dv@users.noreply.github.com> * Update examples/angular/auto-refetching/package.json Co-authored-by: Arnoud <6420061+arnoud-dv@users.noreply.github.com> * Update examples/angular/auto-refetching/src/index.html Co-authored-by: Arnoud <6420061+arnoud-dv@users.noreply.github.com> * Update examples/angular/auto-refetching/tsconfig.json Co-authored-by: Arnoud <6420061+arnoud-dv@users.noreply.github.com> * Update examples/angular/auto-refetching/tsconfig.json Co-authored-by: Arnoud <6420061+arnoud-dv@users.noreply.github.com> * Update examples/angular/auto-refetching/tsconfig.json Co-authored-by: Arnoud <6420061+arnoud-dv@users.noreply.github.com> * Update examples/angular/auto-refetching/src/app/app.config.ts Co-authored-by: Arnoud <6420061+arnoud-dv@users.noreply.github.com> * Update MR change interceptor to function interceptor and update all tasks to quert options obj * Update Angular version * Update Angular version * ci: apply automated fixes * Add fetching indicator * add example to docs * fix build --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Arnoud <6420061+arnoud-dv@users.noreply.github.com>
- Loading branch information
1 parent
8ccc36c
commit d6621a8
Showing
18 changed files
with
489 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
examples/angular/auto-refetching/.devcontainer/devcontainer.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"name": "Node.js", | ||
"image": "mcr.microsoft.com/devcontainers/javascript-node:22" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// @ts-check | ||
|
||
/** @type {import('eslint').Linter.Config} */ | ||
const config = {} | ||
|
||
module.exports = config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# TanStack Query Angular auto-refetching example | ||
|
||
To run this example: | ||
|
||
- `npm install` or `yarn` or `pnpm i` or `bun i` | ||
- `npm run start` or `yarn start` or `pnpm start` or `bun start` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
{ | ||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", | ||
"version": 1, | ||
"cli": { | ||
"packageManager": "pnpm", | ||
"analytics": false, | ||
"cache": { | ||
"enabled": false | ||
} | ||
}, | ||
"newProjectRoot": "projects", | ||
"projects": { | ||
"auto-refetching": { | ||
"projectType": "application", | ||
"schematics": { | ||
"@schematics/angular:component": { | ||
"inlineTemplate": true, | ||
"inlineStyle": true, | ||
"skipTests": true | ||
}, | ||
"@schematics/angular:class": { | ||
"skipTests": true | ||
}, | ||
"@schematics/angular:directive": { | ||
"skipTests": true | ||
}, | ||
"@schematics/angular:guard": { | ||
"skipTests": true | ||
}, | ||
"@schematics/angular:interceptor": { | ||
"skipTests": true | ||
}, | ||
"@schematics/angular:pipe": { | ||
"skipTests": true | ||
}, | ||
"@schematics/angular:resolver": { | ||
"skipTests": true | ||
}, | ||
"@schematics/angular:service": { | ||
"skipTests": true | ||
} | ||
}, | ||
"root": "", | ||
"sourceRoot": "src", | ||
"prefix": "app", | ||
"architect": { | ||
"build": { | ||
"builder": "@angular/build:application", | ||
"options": { | ||
"outputPath": "dist/auto-refetching", | ||
"index": "src/index.html", | ||
"browser": "src/main.ts", | ||
"polyfills": ["zone.js"], | ||
"tsConfig": "tsconfig.app.json", | ||
"assets": ["src/favicon.ico", "src/assets"], | ||
"styles": [], | ||
"scripts": [] | ||
}, | ||
"configurations": { | ||
"production": { | ||
"budgets": [ | ||
{ | ||
"type": "initial", | ||
"maximumWarning": "500kb", | ||
"maximumError": "1mb" | ||
}, | ||
{ | ||
"type": "anyComponentStyle", | ||
"maximumWarning": "2kb", | ||
"maximumError": "4kb" | ||
} | ||
], | ||
"outputHashing": "all" | ||
}, | ||
"development": { | ||
"optimization": false, | ||
"extractLicenses": false, | ||
"sourceMap": true | ||
} | ||
}, | ||
"defaultConfiguration": "production" | ||
}, | ||
"serve": { | ||
"builder": "@angular/build:dev-server", | ||
"configurations": { | ||
"production": { | ||
"buildTarget": "auto-refetching:build:production" | ||
}, | ||
"development": { | ||
"buildTarget": "auto-refetching:build:development" | ||
} | ||
}, | ||
"defaultConfiguration": "development" | ||
}, | ||
"extract-i18n": { | ||
"builder": "@angular/build:extract-i18n", | ||
"options": { | ||
"buildTarget": "auto-refetching:build" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "@tanstack/query-example-angular-auto-refetching", | ||
"type": "module", | ||
"scripts": { | ||
"ng": "ng", | ||
"start": "ng serve", | ||
"build": "ng build", | ||
"watch": "ng build --watch --configuration development" | ||
}, | ||
"private": true, | ||
"dependencies": { | ||
"@angular/common": "^19.1.0-next.0", | ||
"@angular/compiler": "^19.1.0-next.0", | ||
"@angular/core": "^19.1.0-next.0", | ||
"@angular/platform-browser": "^19.1.0-next.0", | ||
"@angular/platform-browser-dynamic": "^19.1.0-next.0", | ||
"@tanstack/angular-query-experimental": "^5.62.2", | ||
"rxjs": "^7.8.1", | ||
"tslib": "^2.6.3", | ||
"zone.js": "^0.15.0" | ||
}, | ||
"devDependencies": { | ||
"@angular/build": "^19.0.2", | ||
"@angular/cli": "^19.0.2", | ||
"@angular/compiler-cli": "^19.1.0-next.0", | ||
"typescript": "5.7.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { ChangeDetectionStrategy, Component } from '@angular/core' | ||
import { AutoRefetchingExampleComponent } from './components/auto-refetching.component' | ||
|
||
@Component({ | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
selector: 'app-root', | ||
standalone: true, | ||
template: `<auto-refetching-example />`, | ||
imports: [AutoRefetchingExampleComponent], | ||
}) | ||
export class AppComponent {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { | ||
provideHttpClient, | ||
withFetch, | ||
withInterceptors, | ||
} from '@angular/common/http' | ||
import { | ||
QueryClient, | ||
provideTanStackQuery, | ||
withDevtools, | ||
} from '@tanstack/angular-query-experimental' | ||
import { mockInterceptor } from './interceptor/mock-api.interceptor' | ||
import type { ApplicationConfig } from '@angular/core' | ||
|
||
export const appConfig: ApplicationConfig = { | ||
providers: [ | ||
provideHttpClient(withFetch(), withInterceptors([mockInterceptor])), | ||
provideTanStackQuery( | ||
new QueryClient({ | ||
defaultOptions: { | ||
queries: { | ||
gcTime: 1000 * 60 * 60 * 24, // 24 hours | ||
}, | ||
}, | ||
}), | ||
withDevtools(), | ||
), | ||
], | ||
} |
35 changes: 35 additions & 0 deletions
35
examples/angular/auto-refetching/src/app/components/auto-refetching.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<div> | ||
<h1>Auto Refetch with stale-time set to {{ intervalMs() }}ms</h1> | ||
<p> | ||
This example is best experienced on your own machine, where you can open | ||
multiple tabs to the same localhost server and see your changes propagate | ||
between the two. | ||
</p> | ||
<label> | ||
Query Interval speed (ms): | ||
<input [value]="intervalMs()" (input)="inputChange($event)" /> | ||
<span | ||
[ngStyle]="{ | ||
display: 'inline-block', | ||
marginLeft: '.5rem', | ||
width: '10px', | ||
height: '10px', | ||
background: tasks.isFetching() ? 'green' : 'transparent', | ||
transition: !tasks.isFetching() ? 'all .3s ease' : 'none', | ||
borderRadius: '100%', | ||
transform: 'scale(2)', | ||
}" | ||
></span> | ||
</label> | ||
<h2>Todo List</h2> | ||
|
||
<input placeholder="Enter something" (keydown.enter)="addItem($event)" /> | ||
<ul> | ||
@for (item of tasks.data(); track item) { | ||
<li>{{ item }}</li> | ||
} | ||
</ul> | ||
<div> | ||
<button (click)="clearTasks()">Clear All</button> | ||
</div> | ||
</div> |
46 changes: 46 additions & 0 deletions
46
examples/angular/auto-refetching/src/app/components/auto-refetching.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { | ||
ChangeDetectionStrategy, | ||
Component, | ||
inject, | ||
signal, | ||
} from '@angular/core' | ||
import { | ||
injectMutation, | ||
injectQuery, | ||
} from '@tanstack/angular-query-experimental' | ||
import { NgStyle } from '@angular/common' | ||
import { TasksService } from '../services/tasks.service' | ||
|
||
@Component({ | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
selector: 'auto-refetching-example', | ||
standalone: true, | ||
templateUrl: './auto-refetching.component.html', | ||
imports: [NgStyle], | ||
}) | ||
export class AutoRefetchingExampleComponent { | ||
#tasksService = inject(TasksService) | ||
|
||
intervalMs = signal(1000) | ||
|
||
tasks = injectQuery(() => this.#tasksService.allTasks(this.intervalMs())) | ||
|
||
addMutation = injectMutation(() => this.#tasksService.addTask()) | ||
clearMutation = injectMutation(() => this.#tasksService.clearAllTasks()) | ||
|
||
clearTasks() { | ||
this.clearMutation.mutate() | ||
} | ||
|
||
inputChange($event: Event) { | ||
const target = $event.target as HTMLInputElement | ||
this.intervalMs.set(Number(target.value)) | ||
} | ||
|
||
addItem($event: Event) { | ||
const target = $event.target as HTMLInputElement | ||
const value = target.value | ||
this.addMutation.mutate(value) | ||
target.value = '' | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
examples/angular/auto-refetching/src/app/interceptor/mock-api.interceptor.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/** | ||
* MockApiInterceptor is used to simulate API responses for `/api/tasks` endpoints. | ||
* It handles the following operations: | ||
* - GET: Fetches all tasks from localStorage. | ||
* - POST: Adds a new task to localStorage. | ||
* - DELETE: Clears all tasks from localStorage. | ||
* Simulated responses include a delay to mimic network latency. | ||
*/ | ||
import { HttpResponse } from '@angular/common/http' | ||
import { delay, of } from 'rxjs' | ||
import type { | ||
HttpEvent, | ||
HttpHandlerFn, | ||
HttpInterceptorFn, | ||
HttpRequest, | ||
} from '@angular/common/http' | ||
import type { Observable } from 'rxjs' | ||
|
||
export const mockInterceptor: HttpInterceptorFn = ( | ||
req: HttpRequest<unknown>, | ||
next: HttpHandlerFn, | ||
): Observable<HttpEvent<any>> => { | ||
const respondWith = (status: number, body: any) => | ||
of(new HttpResponse({ status, body })).pipe(delay(100)) | ||
if (req.url === '/api/tasks') { | ||
switch (req.method) { | ||
case 'GET': | ||
return respondWith( | ||
200, | ||
JSON.parse(localStorage.getItem('tasks') || '[]'), | ||
) | ||
case 'POST': | ||
const tasks = JSON.parse(localStorage.getItem('tasks') || '[]') | ||
tasks.push(req.body) | ||
localStorage.setItem('tasks', JSON.stringify(tasks)) | ||
return respondWith(201, { | ||
status: 'success', | ||
task: req.body, | ||
}) | ||
case 'DELETE': | ||
localStorage.removeItem('tasks') | ||
return respondWith(200, { status: 'success' }) | ||
} | ||
} | ||
return next(req) | ||
} |
59 changes: 59 additions & 0 deletions
59
examples/angular/auto-refetching/src/app/services/tasks.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { HttpClient } from '@angular/common/http' | ||
import { Injectable, inject } from '@angular/core' | ||
import { | ||
QueryClient, | ||
mutationOptions, | ||
queryOptions, | ||
} from '@tanstack/angular-query-experimental' | ||
|
||
import { lastValueFrom } from 'rxjs' | ||
|
||
@Injectable({ | ||
providedIn: 'root', | ||
}) | ||
export class TasksService { | ||
#queryClient = inject(QueryClient) // Manages query state and caching | ||
#http = inject(HttpClient) // Handles HTTP requests | ||
|
||
/** | ||
* Fetches all tasks from the API. | ||
* Returns an observable containing an array of task strings. | ||
*/ | ||
allTasks = (intervalMs: number) => | ||
queryOptions({ | ||
queryKey: ['tasks'], | ||
queryFn: () => { | ||
return lastValueFrom(this.#http.get<Array<string>>('/api/tasks')) | ||
}, | ||
refetchInterval: intervalMs, | ||
}) | ||
|
||
/** | ||
* Creates a mutation for adding a task. | ||
* On success, invalidates and refetches the "tasks" query cache to update the task list. | ||
*/ | ||
addTask() { | ||
return mutationOptions({ | ||
mutationFn: (task: string) => | ||
lastValueFrom(this.#http.post('/api/tasks', task)), | ||
mutationKey: ['tasks'], | ||
onSuccess: () => { | ||
this.#queryClient.invalidateQueries({ queryKey: ['tasks'] }) | ||
}, | ||
}) | ||
} | ||
|
||
/** | ||
* Creates a mutation for clearing all tasks. | ||
* On success, invalidates and refetches the "tasks" query cache to ensure consistency. | ||
*/ | ||
clearAllTasks() { | ||
return mutationOptions({ | ||
mutationFn: () => lastValueFrom(this.#http.delete('/api/tasks')), | ||
mutationKey: ['clearTasks'], | ||
onSuccess: () => { | ||
this.#queryClient.invalidateQueries({ queryKey: ['tasks'] }) | ||
}, | ||
}) | ||
} | ||
} |
Binary file not shown.
Oops, something went wrong.