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

docs(angular-query): add query options from a service example #8220

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,10 @@
{
"label": "RxJS autocomplete",
"to": "framework/angular/examples/rxjs"
},
{
"label": "Query options from a service",
"to": "framework/angular/examples/query-options-from-a-service"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "Node.js",
"image": "mcr.microsoft.com/devcontainers/javascript-node:18"
}
6 changes: 6 additions & 0 deletions examples/angular/query-options-from-a-service/.eslintrc.cjs
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
6 changes: 6 additions & 0 deletions examples/angular/query-options-from-a-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# TanStack Query Angular query options from a service 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`
104 changes: 104 additions & 0 deletions examples/angular/query-options-from-a-service/angular.json
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": {
"query-options-from-a-service": {
"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-devkit/build-angular:application",
"options": {
"outputPath": "dist/query-options-from-a-service",
"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-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "query-options-from-a-service:build:production"
},
"development": {
"buildTarget": "query-options-from-a-service:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "query-options-from-a-service:build"
}
}
}
}
}
}
30 changes: 30 additions & 0 deletions examples/angular/query-options-from-a-service/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@tanstack/query-example-angular-query-options-from-a-service",
"type": "module",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development"
},
"private": true,
"dependencies": {
"@angular/common": "^17.3.12",
"@angular/compiler": "^17.3.12",
"@angular/core": "^17.3.12",
"@angular/platform-browser": "^17.3.12",
"@angular/platform-browser-dynamic": "^17.3.12",
"@angular/router": "^17.3.12",
"@tanstack/angular-query-experimental": "^5.59.16",
"rxjs": "^7.8.1",
"tslib": "^2.6.3",
"zone.js": "^0.14.8"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.8",
"@angular/cli": "^17.3.8",
"@angular/compiler-cli": "^17.3.12",
"@tanstack/angular-query-devtools-experimental": "^5.59.16",
"typescript": "5.3.3"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<p>
As you visit the posts below, you will notice them in a loading state the
first time you load them. However, after you return to this list and click on
any posts you have already visited again, you will see them load instantly and
background refresh right before your eyes!
<strong>
(You may need to throttle your network speed to simulate longer loading
sequences)
</strong>
</p>
<angular-query-devtools initialIsOpen />
<router-outlet />
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component } from '@angular/core'
import { RouterOutlet } from '@angular/router'
import { AngularQueryDevtools } from '@tanstack/angular-query-devtools-experimental'

@Component({
selector: 'app-root',
standalone: true,
imports: [AngularQueryDevtools, RouterOutlet],
templateUrl: './app.component.html',
styles: [],
})
export class AppComponent {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { provideHttpClient, withFetch } from '@angular/common/http'
import { provideRouter, withComponentInputBinding } from '@angular/router'
import {
QueryClient,
provideAngularQuery,
} from '@tanstack/angular-query-experimental'

import { routes } from './app.routes'
import type { ApplicationConfig } from '@angular/core'

export const appConfig: ApplicationConfig = {
providers: [
provideAngularQuery(new QueryClient()),
provideHttpClient(withFetch()),
provideRouter(routes, withComponentInputBinding()),
],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Route } from '@angular/router'

// loadComponent lazily loads the component
// when the component is the default export, there is no need to handle the promise

export const routes: Array<Route> = [
{
path: '',
loadComponent: () => import('./components/posts.component'),
},
{
path: 'post/:postId',
loadComponent: () => import('./components/post.component'),
},
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<div>
<div>
<a routerLink="/" href="#">Back</a>
</div>
@if (postQuery.isPending()) {
Loading...
} @else if (postQuery.isError()) {
Error: {{ postQuery.error().message }}
}
@if (postQuery.data(); as post) {
<h1>{{ post.title }}</h1>
<div>
<p>{{ post.body }}</p>
</div>
@if (postQuery.isFetching()) {
Background Updating...
}
}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
ChangeDetectionStrategy,
Component,
inject,
input,
numberAttribute,
} from '@angular/core'
import { RouterLink } from '@angular/router'
import { injectQuery } from '@tanstack/angular-query-experimental'
import { QueriesService } from '../services/queries-service'

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'post',
standalone: true,
templateUrl: './post.component.html',
imports: [RouterLink],
})
export default class PostComponent {
private queries = inject(QueriesService)

postId = input.required({
transform: numberAttribute,
})

postQuery = injectQuery(() => this.queries.post(this.postId()))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<div>
<h1>Posts</h1>
@switch (postsQuery.status()) {
@case ('pending') {
Loading...
}
@case ('error') {
Error: {{ postsQuery.error()?.message }}
}
@default {
<div class="todo-container">
@for (post of postsQuery.data(); track post.id) {
<p>
<!-- We can access the query data here to show bold links for-->
<!-- ones that are cached-->
<a
routerLink="post/{{ post.id }}"
[style]="
queryClient.getQueryData(['post', post.id])
? {
fontWeight: 'bold',
color: 'green',
}
: {}
"
>{{ post.title }}</a
>
</p>
}
</div>
}
}
<div>
@if (postsQuery.isFetching()) {
Background Updating...
}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { RouterLink } from '@angular/router'
import {
injectQuery,
injectQueryClient,
} from '@tanstack/angular-query-experimental'
import { QueriesService } from '../services/queries-service'

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'posts',
standalone: true,
templateUrl: './posts.component.html',
imports: [RouterLink],
})
export default class PostsComponent {
private queries = inject(QueriesService)

postsQuery = injectQuery(() => this.queries.posts())
queryClient = injectQueryClient()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { HttpClient } from '@angular/common/http'
import { Injectable, inject } from '@angular/core'

@Injectable({
providedIn: 'root',
})
export class PostsService {
#http = inject(HttpClient)

postById$ = (postId: number) =>
this.#http.get<Post>(`https://jsonplaceholder.typicode.com/posts/${postId}`)

allPosts$ = () =>
this.#http.get<Array<Post>>('https://jsonplaceholder.typicode.com/posts')
}

export interface Post {
id: number
title: string
body: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Injectable, inject } from '@angular/core'
import { lastValueFrom } from 'rxjs'
import { queryOptions } from '@tanstack/angular-query-experimental'
import { PostsService } from './posts-service'

@Injectable({
providedIn: 'root',
})
export class QueriesService {
private postsService = inject(PostsService)

post(postId: number) {
return queryOptions({
queryKey: ['post', postId],
queryFn: () => {
return lastValueFrom(this.postsService.postById$(postId))
},
})
}

posts() {
return queryOptions({
queryKey: ['posts'],
queryFn: () => lastValueFrom(this.postsService.allPosts$()),
})
}
}
Empty file.
Binary file not shown.
13 changes: 13 additions & 0 deletions examples/angular/query-options-from-a-service/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>TanStack Query Angular query options from a service example</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<app-root></app-root>
</body>
</html>
Loading
Loading