-
Notifications
You must be signed in to change notification settings - Fork 25.3k
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
[Feature] Scoped Services (Injectables) #23770
Comments
@lucasbasquerotto ... did you think already about new |
@mlc-mlapis I had seen some features of Angular 6 before, hadn't seen that tough. Now I read about it in: If I understood correctly, if the lazy loaded modules
All of this without having to import it in any module. Is that it? If it is, than that's awesome :) It will work using injector too? (without including in the constructor) I think I will make some test project when I have more time to see the behaviour (I still can't update my current project to Angular 6 due to some dependencies (Ionic) that require Angular 5). |
@lucasbasquerotto ... you described it exactly. Using |
Yes |
already implemented... |
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. |
I'm submitting a...
Hi!
I would like to ask for a feature to allow services (
@Injectable
) scoped to a component/(other) service/directive/etc that have it injected.Situation
I consider the best approach to create one module per component, this way I need only to reference direct dependencies (because the indirect ones will be imported by the submodules).
This makes maintenance much more easier when I change a component from a feature module to another (I will remove only the component module from the feature module, and the dependencies are already handled).
This is also better for performance/app size reasons, because each lazy loaded module will have only what its needs.
I can use this approach for components, directives and pipes.
The problem is that I can’t use it for services/providers, because Angular behaves differently and it might cause problems if I forget to import it in one dependent module, but a parent component imports it, giving the impression that everything is alright (that is, it might work in some cases and don’t work in others, making maintenance much harder as the app grows, and the worse is that the errors are runtime errors that may or may not happen depending if a parent component imports the service).
It's important to note that what I'm asking is not the same as injecting in the providers array of the component, because I still need to import indirect dependencies in this case, having the same problems as above.
Also, all services in a module are created when the module is loaded and stay alive during the lifetime of the application.
Based on almost every case I see in the internet, people import all (or almost all) services in
AppModule
so that the services are created in the startup and are singletons. It becames even worse in the case of third party libraries (having to import all their services inAppModule
so that I can use it, even if I use only one service, and that service is only used in lazy loaded modules).I don't blame them, though. In my own project I do that to avoid the problems above (maintenance nightmare). It has 60 services and their size is not insignificant.
To have a perfomant app I would need to lazy load the ones I don't need in the initial load, and the best approach would be the same one that I use with components, referencing only direct dependencies, so that when I change one service
A
that injects some serviceB
to now inject serviceC
and not inject serviceB
anymore, than I would change onlyServiceAModule
to importServiceCModule
and removeServiceBModule
from it, and all components that injects serviceA
would still be working fine.Proposal
I hope the Angular team creates a feature to make providers module scoped (without being seen in modules that do not import them directly), just like components, otherwise I see the way that is now as an impediment to create medium and large performant apps and at the same time easy to maintain.
For backwards compatibility, it could be some flag in the
@Injectable
annotation, like@Injectable({ scoped: true })
, or a new annotation like@Scoped()
, that would be like@Injectable()
, but it would be scoped only to the module that imports it directly.Example
CA
injects servicesSA
,SB
andSC
SB
injects serviceSD
SC
injects serviceSA
Then
CA
will be created like:The destruction would be:
(With
SA#1
andSA#2
being different instances of the same type of service (SA
))Their modules would be:
CAModule
declares and exportsCA
and importsSAModule
,SBModule
andSCModule
.SAModule
declares and exportsSA
.SBModule
declares and exportsSB
and importsSDModule
.SCModule
declares and exportsSC
and importsSAModule
.SDModule
declares and exportsSD
.(If
CAModule
doesn't importSAModule
it will give an error always, even if it importsSCModule
andSCModule
importsSAModule
, avoiding the maintenance problems I talked about previously)Pros
1) Much better performance (compared to import all of them in
AppModule
)2) Much easier maintenance (compared to import the services in feature modules, or in the component providers array)
Cons
1) To use services to behave as singletons I would make one, and only one service, to be global, and inject this service to preserve the shared state of services of the same type, and listen to events of the service . I would just need to create a map (each key belonging to a different type of service) to have the (serializable) object with the state and a rxjs subject to emit and subscribe to events (could be a map of subjects, similar to the map with the states). This is just an idea and I could create it myself, my point is that I could achieve services to share state using the approach above. Although this can be achieved, this would require some effort, and the services should be changed accordingly, but nothing that can't be done.
2) Possible performance problems after the app is loaded, because it would be created one new service instance in each injection (but only when needed, and destroyed when not needed anymore, so in some cases it could actually improve performance).
3) I need to import all direct dependencies modules in each component/service that needs them.
More details
More information of why I consider one module per component the best approach:
https://stackoverflow.com/questions/46434830/should-i-create-a-module-per-component-in-angular-4-app/49902078#49902078
I can still use feature modules, but they would import the component modules instead of the components themselves (so, no problems with indirect dependencies).
The reason that makes me want one module per service is the same, but I can't do that because Angular doesn't support it :/
TL;DR
Because of the way services are currently imported in modules (inside the providers array), scoped to that module and all child modules, I need to import all of them in
AppModule
to avoid indirect dependencies that need to be imported explicitly and errors that don't happen in every case depeding on the dependencies of a parent module (maintenance nightmare).The problem is that as the app grows, it will make the initial load size huge and the startup slower (bad UX).
So it would be really useful a scoped injectable that is created when the component/service that injects it is created and destroyed when the component/service is destroyed.
It would need to be imported explicitly in the module that imports the component/service (would not work even if a parent imported it, just like what happens with components/directives/pipes), to avoid cases where the same service works in some cases and not in other cases because of its parents dependencies (this is one of the worst kind of problems there is in software/web/app development IMO, and it is what happens now).
For medium and large apps, I don't see a way to make Angular apps both performant and easy to maintain, because of the reasons above.
Can this feature be implemented? Is there another way to achieve both performance and easy maintenace for medium and large sized Angular apps currently (without fighting against the framework and having in mind the problems I talked about)?
The text was updated successfully, but these errors were encountered: