-
Notifications
You must be signed in to change notification settings - Fork 12k
Circular dependency error with service generated with --module in 6.0.0-rc.0 #10170
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
Comments
That's a good point. I'm asking around if this is by design. It's worth noting that the circular dependency detection was introduced in the past because there were cases where it broke bundling. Perhaps it is not actually needed anymore. |
Please, "don't shoot the messenger". In fact it seems to me the circular dependency warning is a life-saver here, pointing out on an otherwise very hard to detect problem. Dropping the message won't solve the problem:
The service is obviously not being injected in case of a circular dependency. Actually, that sounds like a problem with the angular compiler who reasons about decorator metadata for dependency injection and who seems to have issues to properly resolve those circular dependencies - it could also be a runtime problem in the code the compiler generates (you guys probably know better than me). I would expect much more people to run into this once tree-shakable providers will be released for two reasons:
Unless there's a solution to this problem |
Please stop teaching or telling pepole to use this pattern. With tree shakeable providers, you now have zero reasons to provide a service from a module (there is a rare case on some lazy loaded services with a component using the service from a separate lazy module). The
What will likely happen is; we will either
People using |
@hansl Thanks for the explanation. I think deleting the |
@hansl I think we think pretty much the same about the issue. I didn't intent to teach people to use this pattern nor should above "pattern" be understood as a recommendation. Maybe I should have called it anti-pattern
In fact that's what I argued for, too. My comment was meant as a response to the thoughts of @filipesilva who thought whether it were possible to drop the circular dep. detection alltogether. I think it provides an invaluable hint on the cause of @cyrilletuzi's issue and points at code smells which are unpleasant and unintended most of the time. By the current typings and docs it is at least technically possible to use Therefore I am afraid there will be many people who migrate their code and end up with the (anti-)pattern I described. Without any warning they would only find out that "for some reason" the service simply won't be provided to some components. They would face a problem which is very hard to debug. Seeing the circ. dep. from just the code is not any easier. And therefore I came to the same conclusion as you did. The circular dependency warning might not tell people directly that they've used
That was my impression, too. Given the docs |
You might wanna strip the following from the docs, to not give people any good ideas:
Source: https://angular.io/guide/dependency-injection#injectable-providers Or keep it, and put a very big fat warning box next to it. |
I'm experiencing this problem as well, but am unconvinced that Since |
Encountered this as well. Api service imports module, module imports ngrx/effect, ngrx/effects imports api service. So I should just use |
Whoa Nellie! I have a large number of services that are scoped to modules (lazy modules) or even to components within those (lazy modules). This pattern has worked GREAT for a very long time. I understand the desire to make LIBRARIES treeshakable. A library often includes tons of code that many consumers will not need or want. However, APPLICATIONS typically contain very few treeshakable services. If I write a service for my app, it's because I expect the app to need it. I don't write app services that someone might NOT need. Therefore, treeshaking is of very little benefit to application code. If you think I'm wrong, show me (us) please. FWIW, I know we can use the new syntax to declare that a service is provided in a non-root module (and I think it works in a component too). That's grand, but do I need it? In an application, I don't think that I do. Moreover, this style is a mixed blessing. On the plus side, it's easy to tell by reading the service where it is provided and to ensure that it is provided there. On the negative side, I've pinned the service to that particular usage. I can't safely provide it at another level because I'm at risk of service shadowing (a serious problem for a stateful service). It feels premature to make such bold statements about a feature that none of us could have used for more than a few months. |
Well you totally busted me right now.
+1 for this idea. @NgModule({ selector: 'heroes' })
export class HeroModule {}
@Injectable({ providedIn: 'heroes' })
export class HeroService {} I think that's a nice way to do it. This should be possible, right? |
Tree shaking providers are opt-in. The classic method of providing the services via |
As an update to my last post: it does appear that even when |
I think re-export @NgModule({
imports: [HttpClientModule],
exports: [HttpClientModule]
})
export class LazyLoadedModule {
}
@Injectable({ providedIn: 'root' })
export class LazyService {
constructor(private http: HttpClient) {
}
} |
@StevenLiekens no, that didn't help :-/ |
@luchsamapparat I can not provide some "official" recommendation but I think the best practice in cases where tree-shakable providers don't work is to continue providing things as they have been provided prior to tree-shakable providers. The v5 mechanics haven't been deprecated. Nevertheless, did you try the pattern shown by @manfredsteyer in angular/angular#23715. It's far from pretty but might help with tree-shakable providers. |
After playing with this for the last hour, I have come to the following conclusion that for services used across the application, then it is only useful to have However, if you want to provide a service in any feature module (not the root), then you are better off using the providers array in the feature module's decorators, otherwise you will be plagued with circular dependency warnings. |
Then my other recommendation is importing
Because your app module is the composition root, which is where you should register all services that you will eventually use. Think of Or to put it in NPM terms: HttpClient is a peer dependency between your app and your lazy modules. |
Here's why that breaks
https://angular.io/guide/providers#limiting-provider-scope-by-lazy-loading-modules Okay so what this means is that your lazy module gets its own private copy of HttpClient. The root module doesn't know about your private HttpClient so the root injector shits all over the console when you try to get an instance of a service that depends on HttpClient. I do feel like re-exporting the HttpClientModule should have added HttpClient to the root injector but maybe my expectations are wrong. |
For me it feels like this feature, while nice in theory, isn't thought through… |
From reading lots of things about the topic, what I understand is the following: Like @hansl wrote: "Always use Like @swimmadude66 wrote: it will still be bundled in the right bundle (so they the build does the job for you if it's only used in the lazy loaded bundle) And just like in this issue 23715 - If you really want to limit the scope, you can do it with a separate module for services which gets loaded by the lazy loaded module. The only thing missing is to update the docs and I think it'll be better to remove the section that shows you can use I think that will reduce the confusion and will promote the better practice. (FYI @jenniferfell ) CONCLUSION:Always use the
|
So back on topic: what should the |
@StevenLiekens I have a feature module and I was trying to specify the module name in the Sounds like we need a feature request to tokenize a module name to be used by services preventing the circular dependency warning. If this was implemented THEN |
It seems like if the obvious answer is that everything must be |
If it should be |
@cedvdb I'm with you, if that was the only way to define providers. |
add here for notation purpose. I take 3 hours to invest about this, and this blog: The New Treeshakable Providers API In Angular: Why, How And Cycles @Injectable({
providedIn: FlightApiModule
})
export abstract class AbstractFlightService {
} In addition, the lazy module also needs to import the new service module: @NgModule({
imports: [
[...]
FlightApiModule
],
[...]
})
export class FlightBookingModule {
} |
Has everyone read the updated documentation yet? Also there is the FAQ providing tons of informations how it works (probably better than the article on softwarearchitect.io ?) If i get this right:
Can someone confirm? |
I think this is not completely correct. If you use This is from my POV a problem because you can't guarantee that something is not used somewhere where you don't want it to be used. The only way to guarantee that is to use |
With "5." i mean provide with the providers array. Do not use |
@MickL I see that, so then it is a true statement to say that these two are equivalent? Use providers: []
Use providedIn
|
This has just happend to me using this page: https://angular.io/guide/providers#providing-a-service Please guys could you remove it from de docs? Or add something that warning the people. I saw the problem after implement it. |
Please correct me if I'm missing something, but it seems to me that adding the The service doesn't know about the module, and the module doesn't know about the service anymore, so the service is no longer really "in" the module. Instead, the service will just get bundled with whatever references it? The result of this seems to be that we can no longer guarantee that the service's dependencies (as defined by the module it "lives in") will be loaded when the service is injected, hence - I think - the problem described by @luchsamapparat? It seems to me this could be fixed using the Alternatively though, wouldn't it have been simpler not to have introduced the |
Most probably you wanted to have a service in 1 particular component/module. You can do it by doing the following:
And in your component:
Do not make it this way:
It will show warnings about circular dependencies. Also do not import this in the
Angular will then detect automatically in which component it has been used. If it wouldn't be used it will be tree shaked. |
Angular contributor says make all your service instances Root scoped Singletons? Regardless of the programming language or framework this can't be a serious mindset. If I wanted to do silly things with code I would switch to React and become a JSX fan. Being able to control the scope and lifetime of an injected dependency instance is very important, I hope the Angular Team is not planning on taking this capability away for tree shaking improvements. What you end up with is a small file bundle that quickly becomes bloated in memory at run time. I don't use 'providedIn' because I like being able to control scope and lifetime of my service instances, and when a developer has this mindset then there is no need for tree shaking since I intend for my services to be used else I would not go to the trouble of providing them only where they are needed. Since the CLI defaults to providedIn when generating a service class I see teams of Angular newbies using it everywhere in their apps for services that are written to work only with a single component instance. I would like to see the providedIn technique be Opt In and remove the {providedIn: 'root'} snippet from CLI generated services. |
Why would you handle the scope of your services when providedIn already choses the perfect usage? If the service is used only with a single component then it is bundled with this component. If it is used as a app wide singleton then it is. The CLI always handles it perfectly no need to think about it again. I think this is great for newbies and for professionals. Still it is optional and you can handle it manually too so i dont see the problem. |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Versions
Repro steps
Observed behavior
Desired behavior
CLI v6
ng g service
command now provides the service in a tree-shakeable way, using theprovidedIn
option ofInjectable
decorator.Without the
--module
option, it usesprovidedIn: 'root'
, which seems OK.But with the
--module
option, CLI v1 was providing the service directly inside theNgModule
. Now in CLI v6, the module is configured viaprovidedIn
. But then the service is not usable in components, which is a serious problem.I see 2 options :
providedIn
, then the CLI should keep its old behavior of providing the service directly in theNgModule
when using--module
.The text was updated successfully, but these errors were encountered: