Does a service that is provided in \"root\" within the @Injectable() decorator still have to be in the providers array of the module?
The Angular documentation doesn
The bullet points in the link you provided are all different methods of registering services, from the least specific to most specific.
App-specific - use @Injectable({ providedIn: 'root' })
When you provide the service at the root level, Angular creates a single, shared instance of HeroService and injects it into any class that asks for it. Registering the provider in the @Injectable() metadata also allows Angular to optimize an app by removing the service from the compiled app if it isn't used.
Module-specific - register in module providers
When you register a provider with a specific NgModule, the same instance of a service is available to all components in that NgModule. To register at this level, use the providers property of the @NgModule() decorator,
Component-specific - register in component
When you register a provider at the component level, you get a new instance of the service with each new instance of that component. At the component level, register a service provider in the providers property of the @Component() metadata.
All quotes above are from the official Introduction to services and dependency injection page
@Injectable
- the default CLI approach.It is my opinion that most use cases would fall into the first two approaches.
Tip: just use providedIn: 'root'
. Unused services won't be compiled for a module if it isn't used due to tree shaking. Declaring module-specific services seems redundant and, as we shall see, can cause problems.
There are two ways to register module-specific services - either from the module or from the service.
module
@NgModule({
providers: [MyService]
})
export class MyModule {}
service
@Injectable({ providedIn: MyModule })
The latter is the officially recommended approach. Declaring the providers array is a hangover from the earlier days.
From the docs:
The example above shows the preferred way to provide a service in a module. This method is preferred because it enables tree-shaking of the service if nothing injects it. If it's not possible to specify in the service which module should provide it, you can also declare a provider for the service within the module
providedIn: 'root'
So we see that this approach is tree-shakeable. So far so good. But you will end up with circular references if you simply try to import the same module that the components consuming the clients are declared in.
Take this setup:
my-module
declarations: [
MyComponent
]
my-service
@Injectable({ providedIn: MyModule })
my-component
constructor(private myService: MyService) {}
There is a circular dependency.
The workaround for this is to create a service module and import that into your module.
my-module
imports: [
MyModuleServices
],
declarations: [
MyComponent
]
my-module-services
my-service
@Injectable({ providedIn: MyModuleServices })
my-component
constructor(private myService: MyService) {}
This is a very long-winded alternative to simply using providedIn: 'root'
and letting the tree shaking do the work.