|
1 | | -import { TanstackQueryDevtools } from '@tanstack/query-devtools' |
| 1 | +import * as queryDevtools from '@tanstack/query-devtools' |
| 2 | +import { |
| 3 | + injectQueryClient, |
| 4 | + onlineManager, |
| 5 | +} from '@tanstack/angular-query-experimental' |
2 | 6 | import { |
3 | 7 | ChangeDetectionStrategy, |
4 | 8 | Component, |
5 | 9 | ElementRef, |
6 | 10 | Input, |
7 | 11 | ViewChild, |
8 | 12 | booleanAttribute, |
9 | | - inject, |
10 | 13 | } from '@angular/core' |
11 | | -import { |
12 | | - QUERY_CLIENT, |
13 | | - onlineManager, |
14 | | -} from '@tanstack/angular-query-experimental' |
15 | | -import type { QueryClient } from '@tanstack/angular-query-experimental' |
16 | | -import type { AfterViewInit, OnDestroy } from '@angular/core' |
| 14 | +import { QueryClient } from '@tanstack/angular-query-experimental' |
| 15 | +import type { |
| 16 | + AfterViewInit, |
| 17 | + OnChanges, |
| 18 | + OnDestroy, |
| 19 | + SimpleChanges, |
| 20 | +} from '@angular/core' |
17 | 21 | import type { |
18 | | - // DevToolsErrorType as DevToolsErrorTypeOriginal, |
19 | | - DevtoolsButtonPosition as DevtoolsButtonPositionOriginal, |
20 | | - DevtoolsPosition as DevtoolsPositionOriginal, |
| 22 | + DevToolsErrorType, |
| 23 | + TanstackQueryDevtools, |
21 | 24 | } from '@tanstack/query-devtools' |
22 | 25 |
|
23 | | -// Alias types for decorators |
24 | | -type DevtoolsButtonPosition = DevtoolsButtonPositionOriginal |
25 | | -type DevtoolsPosition = DevtoolsPositionOriginal |
26 | | -// type DevToolsErrorType = DevToolsErrorTypeOriginal |
27 | | - |
28 | 26 | @Component({ |
29 | 27 | selector: 'angular-query-devtools', |
30 | 28 | standalone: true, |
31 | 29 | template: `<div class="tsqd-parent-container" #ref></div>`, |
32 | 30 | changeDetection: ChangeDetectionStrategy.OnPush, |
| 31 | + host: { ngSkipHydration: 'true' }, |
33 | 32 | }) |
34 | | -export class AngularQueryDevtools implements AfterViewInit, OnDestroy { |
35 | | - readonly #injectedClient = inject<QueryClient>(QUERY_CLIENT, { |
36 | | - optional: true, |
37 | | - }) |
| 33 | +export class AngularQueryDevtools |
| 34 | + implements AfterViewInit, OnChanges, OnDestroy |
| 35 | +{ |
| 36 | + /* |
| 37 | + * It is intentional that there are no default values on inputs. |
| 38 | + * Core devtools will set defaults when values are undefined. |
| 39 | + * */ |
38 | 40 |
|
39 | | - #clientFromAttribute: QueryClient | null = null |
40 | | - |
41 | | - #getAppliedQueryClient() { |
42 | | - if (!this.#clientFromAttribute && !this.#injectedClient) { |
43 | | - throw new Error( |
44 | | - `You must either provide a client via 'provideAngularQuery' or pass it to the 'client' attribute of angular-query-devtools.`, |
45 | | - ) |
46 | | - } |
47 | | - return this.#clientFromAttribute ?? this.#injectedClient |
48 | | - } |
49 | | - |
50 | | - @ViewChild('ref') ref!: ElementRef |
51 | | - |
52 | | - #devtools: TanstackQueryDevtools |
53 | | - |
54 | | - constructor() { |
55 | | - this.#devtools = new TanstackQueryDevtools({ |
56 | | - client: this.#getAppliedQueryClient()!, |
57 | | - queryFlavor: 'Angular Query', |
58 | | - version: '5', |
59 | | - onlineManager, |
60 | | - buttonPosition: this.buttonPosition, |
61 | | - position: this.position, |
62 | | - initialIsOpen: this.initialIsOpen, |
63 | | - // errorTypes, |
64 | | - }) |
65 | | - } |
66 | | - |
67 | | - #initialIsOpen = false |
68 | 41 | /** |
69 | 42 | * Add this attribute if you want the dev tools to default to being open |
| 43 | + * @example |
| 44 | + * <angular-query-devtools initialIsOpen /> |
70 | 45 | */ |
71 | | - @Input({ transform: booleanAttribute }) |
72 | | - set initialIsOpen(value: boolean) { |
73 | | - this.#initialIsOpen = value |
74 | | - this.#devtools.setInitialIsOpen(value) |
75 | | - } |
76 | | - get initialIsOpen() { |
77 | | - return this.#initialIsOpen |
78 | | - } |
| 46 | + @Input({ transform: booleanAttribute }) initialIsOpen?: boolean |
79 | 47 |
|
80 | | - #buttonPosition: DevtoolsButtonPosition = 'bottom-left' |
81 | 48 | /** |
82 | 49 | * The position of the TanStack logo to open and close the devtools panel. |
83 | 50 | * 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' |
84 | | - * Defaults to 'bottom-left'. |
| 51 | + * Defaults to 'bottom-right'. |
| 52 | + * @example |
| 53 | + * <angular-query-devtools buttonPosition="top-right" /> |
85 | 54 | */ |
86 | | - @Input() |
87 | | - set buttonPosition(value: DevtoolsButtonPosition) { |
88 | | - this.#buttonPosition = value |
89 | | - this.#devtools.setButtonPosition(value) |
90 | | - } |
91 | | - get buttonPosition() { |
92 | | - return this.#buttonPosition |
93 | | - } |
| 55 | + @Input() buttonPosition?: queryDevtools.DevtoolsButtonPosition |
94 | 56 |
|
95 | | - #position: DevtoolsPosition = 'bottom' |
96 | 57 | /** |
97 | 58 | * The position of the Angular Query devtools panel. |
98 | 59 | * 'top' | 'bottom' | 'left' | 'right' |
99 | | - * Defaults to 'bottom'. |
| 60 | + * @example |
| 61 | + * <angular-query-devtools position="bottom" /> |
100 | 62 | */ |
101 | | - @Input() |
102 | | - set position(value: DevtoolsPosition) { |
103 | | - this.#position = value |
104 | | - this.#devtools.setPosition(value) |
105 | | - } |
106 | | - get position() { |
107 | | - return this.#position |
108 | | - } |
| 63 | + @Input({ alias: 'position' }) position?: queryDevtools.DevtoolsPosition |
109 | 64 |
|
110 | 65 | /** |
111 | 66 | * Custom instance of QueryClient |
| 67 | + * @example |
| 68 | + * <angular-query-devtools [client]="queryClient" /> |
112 | 69 | */ |
113 | | - @Input() |
114 | | - set client(client: QueryClient | undefined) { |
115 | | - this.#clientFromAttribute = client ?? null |
116 | | - this.#devtools.setClient(this.#getAppliedQueryClient()!) |
117 | | - } |
| 70 | + @Input() client?: QueryClient |
| 71 | + |
| 72 | + /** |
| 73 | + * Use this to pass a nonce to the style tag that is added to the document head. This is useful if you are using a Content Security Policy (CSP) nonce to allow inline styles. |
| 74 | + * @example |
| 75 | + * <angular-query-devtools styleNonce="YourRandomNonceValue" /> |
| 76 | + */ |
| 77 | + @Input() styleNonce?: string |
118 | 78 |
|
119 | | - // TODO: needs to tested. When re-adding don't forget to re-add to devtools.md too |
120 | | - // #errorTypes: Array<DevToolsErrorType> = [] |
121 | 79 | /** |
122 | 80 | * Use this so you can define custom errors that can be shown in the devtools. |
123 | 81 | */ |
124 | | - // @Input() |
125 | | - // set errorTypes(value: Array<DevToolsErrorType>) { |
126 | | - // this.#errorTypes = value |
127 | | - // this.#devtools?.setErrorTypes(value) |
128 | | - // } |
129 | | - // get errorTypes(): Array<DevToolsErrorType> { |
130 | | - // return this.#errorTypes |
131 | | - // } |
| 82 | + @Input() errorTypes?: Array<DevToolsErrorType> |
| 83 | + |
| 84 | + @ViewChild('ref') ref!: ElementRef |
| 85 | + |
| 86 | + #devtools?: TanstackQueryDevtools |
| 87 | + |
| 88 | + readonly #injectedClient: QueryClient | null = injectQueryClient({ |
| 89 | + optional: true, |
| 90 | + }) |
| 91 | + |
| 92 | + ngOnChanges(changes: SimpleChanges) { |
| 93 | + if (!this.#devtools) return |
| 94 | + if (changes['client']) { |
| 95 | + this.#devtools.setClient(this.#getAppliedQueryClient()) |
| 96 | + } |
| 97 | + if (changes['buttonPosition'] && this.buttonPosition) { |
| 98 | + this.#devtools.setButtonPosition(this.buttonPosition) |
| 99 | + } |
| 100 | + if (changes['position'] && this.position) { |
| 101 | + this.#devtools.setPosition(this.position) |
| 102 | + } |
| 103 | + if (changes['initialIsOpen'] && this.initialIsOpen) { |
| 104 | + this.#devtools.setInitialIsOpen(this.initialIsOpen) |
| 105 | + } |
| 106 | + if (changes['errorTypes'] && this.errorTypes) { |
| 107 | + this.#devtools.setErrorTypes(this.errorTypes) |
| 108 | + } |
| 109 | + } |
132 | 110 |
|
133 | 111 | ngAfterViewInit() { |
134 | | - this.#devtools.mount(this.ref.nativeElement) |
| 112 | + const devtools = new queryDevtools.TanstackQueryDevtools({ |
| 113 | + client: this.#getAppliedQueryClient(), |
| 114 | + queryFlavor: 'Angular Query', |
| 115 | + version: '5', |
| 116 | + onlineManager, |
| 117 | + buttonPosition: this.buttonPosition, |
| 118 | + position: this.position, |
| 119 | + initialIsOpen: this.initialIsOpen, |
| 120 | + errorTypes: this.errorTypes, |
| 121 | + styleNonce: this.styleNonce, |
| 122 | + }) |
| 123 | + devtools.mount(this.ref.nativeElement) |
| 124 | + this.#devtools = devtools |
135 | 125 | } |
136 | 126 |
|
137 | 127 | ngOnDestroy() { |
138 | | - this.#devtools.unmount() |
| 128 | + this.#devtools?.unmount() |
| 129 | + } |
| 130 | + |
| 131 | + #getAppliedQueryClient() { |
| 132 | + const client = this.client ?? this.#injectedClient |
| 133 | + if (!client) { |
| 134 | + throw new Error( |
| 135 | + 'You must either provide a client via `provideAngularQuery` ' + |
| 136 | + 'or pass it to the `client` attribute of `<angular-query-devtools>`.', |
| 137 | + ) |
| 138 | + } |
| 139 | + return client |
139 | 140 | } |
140 | 141 | } |
0 commit comments