Open
Description
Describe the bug
Based on the documentation, you should be able to use inject within the loadDevtools
option in the withDevtools
function.
See https://github.com/TanStack/query/blob/main/docs/framework/angular/devtools.md
Now if you do this, you will get an NG0203
error.
Breaking PR: #8817
Your minimal, reproducible example
Steps to reproduce
- Initialize angular with tanstack query
- Write a simple service with a boolean signal that will determine if the devtool is active
- Try injecting it in the "loadDevtools" option.
Expected behavior
As a developer I expected the devtools not to throw an NG0203 error
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
Any browser
Tanstack Query adapter
angular-query
TanStack Query version
v5.68.1
TypeScript version
v5.8.2
Additional context
No response
Activity
[-][Angular] `withDevtools` option `loadDevtools` no longer supports reactive context[/-][+]Angular: `withDevtools` option `loadDevtools` no longer supports reactive context[/+]MichalKliment commentedon Mar 24, 2025
Would it be please possible to wrap call of optionsFn with runInInjectionContext? Like:
k3nsei commentedon Mar 24, 2025
I tried to upgrade my library (https://github.com/k3nsei/ngx-signal-store-query) to latest version of Angular and TanstackQuery. Then this line stopped working https://github.com/k3nsei/ngx-signal-store-query/blob/08ea07f9c6e30259734b6ee6231be339a5d100c4/apps/demo/src/app/counter/counter.store.ts#L15
So the injection context is also missing in
injectMutation
which is regression and breaks previously writen code.k3nsei commentedon Mar 24, 2025
Using
inject
is not anty-pattern and clearly @arnoud-dv make big mistake there. DI is basic building block in Angular and almost everything there is based on that feature. Please see documentation ofeffect
you can pass injector there https://angular.dev/api/core/CreateEffectOptions🚨 Rollback this PR it is breaking our codebase.
If it were an anti-pattern, then https://angular.dev/api/core/runInInjectionContext would not exist.
You can even see how NgRx Signal Store is allowing for that https://ngrx.io/guide/signals/signal-store#reactive-store-methods
Just rollback #8817 and don't introduce breaking changes like that as they are breaking production codebases.
jonath92 commentedon Mar 25, 2025
It also breaks our code. We have implemented a simple feature toggle mechanism which inter alia allows users to enable/disable the tanstack query devtools dynamically in our app. In the past we could do this very comfortable with:
Now this doesn't work anymore :-(. To do the same now, we would AFAICS need to create an extra component and copy parts of the logic from here in the component. I can therefore only agree with the request to revert the change.
BThomann commentedon Mar 28, 2025
I guess there are two points that are mixed here:
inject
inwithDevtools
that is the point of the other. I agree that this is not desirable and also diverges from how native Angularwith*
functions behave see e.g.: https://angular.dev/api/router/withNavigationErrorHandlerinjectMutation
(same forinjectQuery
). Here I have to strongly disagree:effect
is wrong. Altough you can pass ainjector
to theeffect
you still can not callinject
inside the effect. The injector is just needed for the creation of the effect.runInInjectionContext
is a different tool for a different jobinjectMutation
(same forinjectQuery
) more as acomputed
where you can also not useinject
so this makes sense to meSo TL;DR my 2 cents:
withDevtools
to be able to use inject thereinjectMutation
&injectQuery
k3nsei commentedon Mar 28, 2025
@BThomann then it would ruin all of our usage of TanStack Query and it becomes trash for us. Forcing us to move to
resource()
andhttpResource()
. Please look howNgRx Signals
works with their factory funtions where you able to inject anything that you need.Whole Angular is based on DI, not like trashy React or other JSX based frameworks. So if you want to support Angular properly you need to adapt it proper way having great support for DI.
Maybe you guys weren't never working with huge and complex codebases and don't understand how important this really is.
BThomann commentedon Mar 28, 2025
I don't get it why this would block TanStack Query I the typical patter any way is it to use like this (example from the docs):
Can you elaborate on where you would need to inject inside
injectMutation
?And please try to follow a respectful conversation here. We can disagree on points but there is no need to insult other persons or projects.
k3nsei commentedon Mar 28, 2025
But this is childish example. First of all it's anty-pattern to creating queries inside component and having any business logic there. For that you should have services. Components should just render stuff and handling user interactions then doing small orchestration to pass data to services where businesses logic should be handled (no tight coupling). Then with complex codebases where there are lots of generic services which are based for example on strategies/policies injected through DI. People are using composition for example by using providers to inject fn with given implementation. You can image for example you want to have generic image upload and you want to change endpoint which is used based on where it placed in component tree.
k3nsei commentedon Mar 28, 2025
@BThomann even in you example you can think about example where you need to get
todoId
as a signal from some store which is provided in router route or some parent/child component. In Angular not everything is global there are many contexts and Injection Trees.BThomann commentedon Mar 28, 2025
The query itself should be created in the InjectionContext of where it lives. So if you would use the
injectQuery
in a service (worst case providedIn: "root") it would "live" within that service not the component where its needed.I agree that this is a simple example. I like to use the query options service pattern to remove the buisness logic but the injectQuery should be call in the same context the component lives how ever one achieves that.
With that out of the way I still can't see why one would need to call inject inside. I agree with you points about DI and so but that not the thing in question.
Yeah in that case the
todoId
is needed imperatively when we call the mutate function so no issue there - it can come from store input etc.k3nsei commentedon Mar 28, 2025
@BThomann image simple concept as factory function. Then everything starting to make sense.
BThomann commentedon Mar 28, 2025
See also no issue there. Would you mind providing a code example?
4 remaining items
BThomann commentedon Mar 29, 2025
Happy to hear that you found a solution! :)
Regarding naming convention: as always in software engineering naming things is hard! But my mental model goes like
k3nsei commentedon Mar 29, 2025
No it isn't solution. I just solved it for consumers of my util library. Other than that we have real project using angular query.
I strongly believe that consumers of Angular Query shouldn't be bother to manually run low level helper functions like that. It should be done in libraries like Angular Query and optionally passing injector in options like in
effect
.About naming conventions I also cannot agree. Anything having
with
as a prefix should return Providers its convention created by Angular Team. But anything starting withinject
strongly suggests it would be running in Injection Context. SouseQuery
orcreateQuery
would be less confusing and better if for some stupid reason it would be not rolling back. I don't see any good reason why Angular Query shouldn't make sure Injection Context is present. It not making any performance issues as it called only once at callinginjectQuery
and takes burden from consumers to have ugly boilerplate or additional abstraction.BThomann commentedon Mar 29, 2025
I don't know why you keep bringing up the example with the effect. Yes you can pass a injector but still can not call inject inside!?
On the "with" naming convention you disagreed by making a point that I didn't mention? Yes sure it has to do with providers, thats not the point I was referring to. It was about the injection context.
Well at this point I guess it's best to "agree to disagree"... At least this is something to agree on.
k3nsei commentedon Mar 29, 2025
I'm doing this because I think there's a big ego on the part of the person who made the mistake of introducing this PR and these groundbreaking changes. Person who doesn't want to agree that it was a mistake. Just deal with it and don't be a weak snowflake.
Also, the purpose of libraries like Angular Query is to make things simpler and with less boilerplate. Choosing names like
injectQuery
andinjectMutation
strongly suggests that things should run in an injection context. I don't see why it's a big problem to still have them as in the past. It makes everyone's life easier and it's nicer to use.The last bit of Angular is famous for its opinionated patterns, attention to detail and backwards compatibility. It is the same with the whole ecosystem. Obviously, if you make changes like this, you break all that trust.
If building trust among people who have chosen to rely on this library and not breaking their code base is not a priority here. In that case, I can say goodbye and good luck. There are project that spend multi-miltion dollars to build them, which are used by millions of people every day. Than introducing breaking changes like that, without any announcement and plan how to handle them in reasonable period of time is not trust worthy and better to not rely on libraries with that approach. It's against philosophy of Angular ecosystem. Even React team when they changed how
Suspense
works, they discarded their changes because of that reason.@BThomann it is possible that you are just a young, naive student who has never yet been responsible for anything big. And you don't understand the importance of responsibility.
BThomann commentedon Mar 29, 2025
Relax mate. No need to be personal here. For me there is no point in continuing this discussion here on that level. Happy to continue if it gets back on a technical level.
I am sorry for the author of this issue that this got out of context.
Eirmas commentedon Mar 29, 2025
🍿
arnoud-dv commentedon Mar 29, 2025
@k3nsei There's no need to get personal. I'll fix the original issue as reported by @Eirmas. Clearly this was an oversight of me as it breaks my own code example.
TkDodo commentedon Mar 29, 2025
@k3nsei let me start by saying we generally appreciate feedback, and we also appreciate that you are using the angular adapter while it’s still experimental, because feedback in that phase that will help us getting to a good API.
That said, your tone is way out of line. At no point did you stop to consider that this was just an honest mistake by one of the maintainers - you assumed ill intent, tried to lecture everyone on “best practices” and insulted others who tried to chime on this issue, while at the same time bashing React 🤷♂️.
I strongly suggest you re-read your messages and try to reflect if this is really how you want to come across. This is not the kind of conversations and mindset I want to have in the TanStack community, and if this continues, I will lock this issue.
We are open source contributors and maintainers, offering something of value for free, for everyone in the world to use. We deserve better than being bashed by you.
k3nsei commentedon Mar 29, 2025
@TkDodo I'm sorry if somebody get offended. But we used to different standards in angular community. There is a thing called angular schematics used by angular cli. Usually when there are some breaking changes developers prepare automatic code fixes by applied by running
ng update lib-name
. They are also anounced to give projects time to adjust. This is super frustrating when after update things stops working and you don't even know why and reading changelogs do not helps here. Then you lossing trust to those dependencies. So if people in this thread writings nothing happend at those consumers of library are not important. Then you can lose some temper.arnoud-dv commentedon Mar 29, 2025
@k3nsei You're jumping from topic to topic. It's hard to follow and certainly insulting people doesn't help achieve your goal.
You're welcome to open a discussion thread if you want to discuss a certain topic such as adding schematics or naming of the functions, or running the options callback in the injection context.
Please open a separate discussion per topic you want to discuss so we can have a focused discussion while maintaining a basic level of respect for other people.
[-]Angular: `withDevtools` option `loadDevtools` no longer supports reactive context[/-][+]Angular: `withDevtools` option `loadDevtools` no longer supports dependency injection[/+]