Replies: 27 comments 100 replies
-
Edit: changed it from
|
Beta Was this translation helpful? Give feedback.
-
Looks super cool, Is is something that can leverage https://github.com/ngneat/query by @NetanelBasal? |
Beta Was this translation helpful? Give feedback.
-
@arnoud-dv awesome amount of work! Definitely the amount of parenthesis might discourage people from trying that out, so 👍 for Question 1: Not sure if core tanstack/query is capable of supporting streams. Hardly anybody in angular area is using Question 2: would providing an example basing on NgModules-based components require extra-effort? Such example might attract more people who didn't move to standalone yet. Again, awesome work, IMO the community needs such port very much. |
Beta Was this translation helpful? Give feedback.
-
The |
Beta Was this translation helpful? Give feedback.
-
I thought this code from your sandbox was nifty. I frequently have a use case where i need to wait for some data (id, form, etc) before creating the query. it would be nice if you could somehow create a dummy query until you have what you need to make the request.
I don't think this code made it into the current release. I'm not sure what the purpose of
i tried to do something like this but it did not seem to work.
|
Beta Was this translation helpful? Give feedback.
-
I really love how simple this is to use. It would probably be useful to see an example that exists in a service as most Angular devs are going to be using that as a simple way to manage things like API calls and global state in their applications. Any updates on when stable is going to be released? |
Beta Was this translation helpful? Give feedback.
-
Example: query = injectQuery(() => ({
enabled: false,
queryKey: ["test"],
queryFn: () => 1,
}))
query.refetch() // Does not make sense, it's just a fetch, not a refetch. "Improved version": query = injectQuery(() => ({
enabled: false,
queryKey: ["test"],
queryFn: () => 1,
}))
query.fetch() // Makes more sense. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
What exactly is the different between this and ngneat/query? |
Beta Was this translation helpful? Give feedback.
-
I'm a developer shifting to angular from react world. It'll genuinely be a great time saver handling out data-fetching responsibilities to query. I checked the code examples in the documentation, seems interfaces are same as react-query. I'll definitely adopt it in real projects as soon as it comes out of the experimental stage! |
Beta Was this translation helpful? Give feedback.
-
Am trying AQ out now and really enjoying the reduction in boilerplate from NgRx (a library I love very much). This is great work! 🙏 |
Beta Was this translation helpful? Give feedback.
-
This looks pretty great, and has been experimental for a while now. With no non-chore, non-release commits in about a month, it seems stable enough to release to me. Is there anything keeping the library experimental besides inertia? I'd love to know what blockers exist to marking it as "ready to use in production." |
Beta Was this translation helpful? Give feedback.
-
I’m interested as well. We’ve used Angular Query for an experimental production app some months ago and it was “ready” (except for something like signal required input etc which was released in that time). I would like to introduce it in our bigger projects, but as it's experimental I'm not 100% sure |
Beta Was this translation helpful? Give feedback.
-
I have been using Angular query in medium sized projects for two months now and I am really enjoying it, especially that it is working with signals as opposed to observables. I am coming from a React background (including react query) and missed the ease of data fetching and handling that this library enables. My only major struggle so far is the typing of Even without a official plugin, persisting the cache with session storage using |
Beta Was this translation helpful? Give feedback.
-
@arnoud-dv is there anything published on the timeline and what's remaining to upgrade I'd like to avoid scrutiny from using something called "experimental" in production deployments. |
Beta Was this translation helpful? Give feedback.
-
Great work with the adapter!! I love how it works seamlessly with signals. As for the feedback:
click to see the example codeimport {Component, inject,} from '@angular/core'
import {injectQuery,} from '@tanstack/angular-query-experimental'
import {debounceTime, fromEvent, lastValueFrom, Subject, switchMap, takeUntil} from 'rxjs'
import {HttpClient} from "@angular/common/http";
@Component({
selector: 'address',
standalone: true,
template: `<input (input)="setInputValue($event)">`,
})
export class AddressComponent {
subject: Subject<string> = new Subject()
setInputValue($event: any) {
this.subject.next($event.target.value);
}
#http = inject(HttpClient)
addressQuery = injectQuery(() => ({
queryKey: ['address'], // <--- I want to add the search value here
queryFn: ({signal}) => {
return lastValueFrom(
this.subject.pipe(
debounceTime(250),
switchMap((value) => {
return this.#http
.get<any[]>(`https://some-url-that.returns/addresses`, {params: {search: value}})
.pipe(takeUntil(fromEvent(signal, 'abort')))
}),
)
)
},
}))
} |
Beta Was this translation helpful? Give feedback.
-
It's amazing! But SSR not being officially implemented is kind of a pain point. Posting a solution (this is for an @NgModule based app):
providers: [
{
provide: BEFORE_APP_SERIALIZED,
useFactory: (doc: Document) => {
return () => {
const script = doc.createElement('script');
script.innerText = `window.__QUERY_STATE__ = ${JSON.stringify(dehydrate(queryClient))}`;
doc.head.appendChild(script);
};
},
deps: [DOCUMENT],
multi: true,
},
], Imports are: import { BEFORE_APP_SERIALIZED } from '@angular/platform-server';
import { queryClient } from './app.module';
import { dehydrate } from '@tanstack/angular-query-experimental';
import { DOCUMENT } from '@angular/common';
export const queryClient = new QueryClient();
// ...
@NgModule({
// ...
providers: [
// ...
provideAngularQuery(queryClient)
],
// ...
})
export class AppModule {}
import { queryClient } from './app/app.module'
import { hydrate } from '@tanstack/angular-query-experimental';
// @ts-ignore
if (window.__QUERY_STATE__) {
// @ts-ignore
const state: any & {} = window.__QUERY_STATE__;
hydrate(queryClient, state);
console.log('hydrated QueryClient successfully', state);
} Also, why was hiding the actual injection token necessary? You could just define QueryClient.ɵprov = ɵɵdefineInjectable({
token: QueryClient,
providedIn: 'root',
factory: () => new QueryClient(),
}) |
Beta Was this translation helpful? Give feedback.
-
I've been trying to use this, just experimentally to learn, and I feel a typical use case is slightly less ergonomical/simple than I had hoped. It works, but I was hoping I could simplify it further. The situation is:
Number 3 means I will likely bind the id into the component either using I have found something that works, but is slightly less ergonomical/simple than I'd like. The query creation looks like this: export class SomeQueries {
static someQuery(someId: Signal<string | undefined>) { // I'd like to avoid "| undefined"
const backendService = inject(BackendService); // some code-generated service for calling the backend
return injectQuery(() => ({
queryKey: ['someQuery', someId()],
queryFn: () => lastValueFrom(backendService.getSomeResource(someId()!)), // I'd like to avoid the !
enabled: () => someId() !== undefined, // I'd like to avoid this check, if possible
}));
}
} And usage: @Component({...})
export class SomeComponent {
someId = input<string>();
someQuery = SomeQueries.someQuery(this.someId);
} Is this the recommended way of handling this case? |
Beta Was this translation helpful? Give feedback.
-
why does no one give production examples of use. since examples are described in the documentation, they are written only on the fence. In the real world, I do not want to take a tool and use it in components, make several levels of abstraction and see if your tool is good, if it integrates well into the framework. make at least an example of todoApiService, todoQueryService, todoService, |
Beta Was this translation helpful? Give feedback.
-
I have worked with Tanstack query for Angular for a little bit, and it is a great tool. Below I will add what I think should be added in the API to make easier and more natural to work with
resolvedData = ReturningType[] = []
resolvedDataSignal = computed(() => {
if (this.myInjectedQuery.data()) {
this.resolvedData = this.myInjectedQuery.data()!;
}
return this.resolvedData ;
}); Now I use the resolvedDataSignal instead of the data() signal. ** Proposal 1 **
I have a global service to notify loading ( For this reason I had to move them to the query level and that is fine. The problem now is that there is no signal to stop loading so I have to to the following (I can also add a finally clause to stopLoading, but that's fine) queryFn: async () => {
loadingService.startLoading('Loading customer bookings...');
try {
const bookings = await customerService.getCustomerBookings(
customer_id(),
limit(),
offset(),
isCancelled ? isCancelled() : undefined,
);
loadingService.stopLoading();
return bookings;
} catch (error) {
loadingService.stopLoading();
return Promise.reject([]);
}
}, This works ok but its not pretty. It would be much nicer to have separate hooks for I did not have any other issues so far. |
Beta Was this translation helpful? Give feedback.
-
I have 2 questions: First, how do I wait for a value to arrive and do something with it once the view has been rendered? For now, I have: injector = inject(Injector)
apis = injectQuery(...)
constructor() {
afterNextRender(() => {
toObservable(this.apis.data, { injector: this.injector })
.pipe(
filter((apis) => apis !== undefined),
first()
)
.subscribe((apis) => {
if (!apis) {
return
}
// ...
})
})
} Feels a bit too complicated. Second, say I have foo = injectQuery(() => ({
queryKey: ['foo'],
queryFn: () => 'foo',
})) and another value Option 1 (built in bar1 = computed(() => (!this.foo.isSuccess() ? undefined : 'bar')) The problem with this is that if Option 2 ( bar2 = derivedAsync(() =>
toObservable(this.foo.data).pipe(
filter((data) => data !== undefined),
map((data) => 'bar')
)
) I think this is functionally the same, and has the same issue with Option 3 (dependant query): bar3 = injectQuery(() => ({
queryKey: ['bar'],
queryFn: () => 'bar',
enabled: !!this.foo.data(),
})) I don't think queries are meant to be used with regular derived values, so this one feels like a hack, but at least it has the nice |
Beta Was this translation helpful? Give feedback.
-
Hi @arnoud-dv. I'm currently migrating from @ngneat/query to your library. Now that you're handling signal — which was the main reason I originally chose the ngneat library — I was wondering: have you considered reaching out to the ngneat/query maintainer, to see if he'd be interested in collaborating on your library instead, and potentially marking his as deprecated? Thanks again for all your efforts! |
Beta Was this translation helpful? Give feedback.
-
Hello @arnoud-dv, really really appreciate your hard work on this Angular adapter for Tanstack Query. I was lucky enough to start a new Angular project and immediately jumps onto this train the moment I saw the documentation. Happy to report that I have encountered 0 production bugs so far, and the app itself is a huge internal enterprise application. This library has dramatically changed the way I approached state management in general, and asynchronous state management in particular. Angular devs tend to over-burden ourselves with services injecting/co-ordinating other services, and worrying about at which layers should we load/fire our request to make sure our data is available. Also using a bunch of RxJs operators like distinctUntilChanged, debounceTime, switchMap, tap to perform repetitive tasks like debouncing, de-duping requests, setLoading, setError, etc. So happy that I no longer have to think so much about those things with Angular/Tanstack Query. The built-in query-key cache management, request de-dup/cancellation, active/inactive query management on component mount/unmount (via injection context), etc have been amazing to work with. I am honestly impressed with the perfect balance between declarative and imperative APIs available to developers. Big kudos to you and the team's work. Look forward to next stage of the library, graduation from experimental! |
Beta Was this translation helpful? Give feedback.
-
How do you test query data? In my unit tests (jest), my query is stuck in
The only thing that works is manually waiting for some arbitrary duration: fixture.detectChanges()
await new Promise((r) => setTimeout(r, 1000)) but obviously, that's super brittle (fails with 500ms for example). FWIW, as per https://tanstack.com/query/latest/docs/framework/react/guides/testing#our-first-test, in react you can simply do: await waitFor(() => result.current.isSuccess) |
Beta Was this translation helpful? Give feedback.
-
Is there any update on a stable version? I'm eager to use this in my application. |
Beta Was this translation helpful? Give feedback.
-
Angular 19 is introducing an experimental @Component(...)
export class UserProfile {
userId = input<number>();
userService = inject(UserService);
user = resource({
request: this.userId,
loader: async ({request: id}) => await userService.getUser(id),
});
}
This looks an awful lot like |
Beta Was this translation helpful? Give feedback.
-
todosQuery = injectQuery(() => ({
queryKey: ['todos', this.filter()],
queryFn: this.filter() ? () => fetchTodos(this.filter()) : skipToken,
})) The compiler doesn't know when the todosQuery = injectQuery(() => ({
queryKey: ['todos', this.filter()],
queryFn: !this.filter()
? skipToken
: () => {
const filter = this.filter()
if (!filter) {
throw new Error('yikes')
}
return fetchTodos(filter)
},
})) But since the argument given to todosQuery = injectQuery(() => {
const filter = this.filter()
return !filter
? { queryFn: skipToken } // Can I omit queryKey?
: {
queryKey: ['todos', filter],
queryFn: () => fetchTodos(filter),
}
}) At which point, if todosQuery = injectQuery(() => {
const filter = this.filter()
return !filter
? skipQuery
: {
queryKey: ['todos', filter],
queryFn: () => fetchTodos(filter),
}
}) |
Beta Was this translation helpful? Give feedback.
-
👋 Hi everyone,
I've been working on a pull request for adding an Angular adapter to TanStack Query, and it's now approaching completion.
To give you a better idea of its functionality, here are two sandboxes:
These sandboxes are interactive, allowing you to edit the code and see how it works in your own forked sandbox.
The adapter exclusively uses Angular signals, but integrating it with RxJS is quite straightforward thanks to the Angular RxJS interop. With Tanstack / Angular Query, you can potentially replace most, if not all, RxJS-based server-side state synchronization code. That said, I believe RxJS still has significant applications in modern Angular apps, even after the introduction of signals in the framework. For instance, signals derived from an RxJS stream can be easily integrated into query options for reactive behavior.
For the time being, I'm naming the adapter "experimental." This is primarily to allow for the possibility of making breaking API changes in the future. However, I am quite satisfied with its API, especially with
createQuery
.I'm looking forward to read your thoughts and feedback! If you have suggestions for API changes or any other improvements, now is the perfect time to share them.
Beta Was this translation helpful? Give feedback.
All reactions