A detailed look at Angular's 'root' and 'any' provider scopes
Angular 9 Ivy provides us with a few more options when defining Provider Scope. In this article we will see why 'root' is not sufficient and how the new value 'any' can help.

If you are following Angular 9 release, you may have heard how providedIn
has few more properties, module scope has been possible since version 2, tree-shakable module scope became available in version 6 with providedIn: MyServiceModule
and now we have 'any'
and 'platform'
.
Did you know, #Angular 9 introduces providedIn:'any':
— Manfred Steyer (@ManfredSteyer) January 20, 2020
?Gives you a separate service instance per lazy module (+ one for 'root')
?Can replace the need to force consumer to call forRoot and forChild in some cases pic.twitter.com/b9vOHv7qH6
In this blog post we will see the example why we need 'any'
and how it is useful. We will leave 'platform'
for our next blog post.
Tree-Shakable Providers
In Angular 6 providedIn
property was added to providers, to make services tree-shakable. If you are new to Angular, let me give you an simple explanation what we mean by tree shaking - it is process to remove, the unused code from our application. It means if you create a service but do not use it, in your application the code will not be part of final production build. To learn more you can refer this blog-post from Lars.
Why the any scope?
So we now know why 'root'
was introduced, the idea was to make services tree-shakable. To understand providedIn: 'any'
we have to talk a little bit about implementation of forRoot
and forChild
and lazy loading. If you have used Angular Router or NgRx then you know about these methods.
The problem of working with lazy loaded module is that if we use providedIn: 'root'
even though we think we should get a new instance of a service, it gives the same instance and that might not be the behavior we expect. When working with a lazy-loaded module, a new instance should be created when we load the module.
Let’s write some code to see, what was the issue earlier and how 'any'
(make sure you don’t miss the quotes to avoid confusion with any type from typescript) resolves that for us.
What we are going to achieve
- A config service which will take some config parameter
apiEndpoint
andtimeout
. - 2 lazy loaded modules employee and department, they want to use the config service, but with different values.
The problem with using the root scope
Create a new Angular 9 app using the below command if you don't want to install Angular 9 globally
npx -p @angular/cli ng new providerdemo
If you have Angular CLI 9 installed globally skip npx -p @angular/cli
from all commands.
Now let’s create 2 new lazy loaded modules with components, run the below commands:
npx -p @angular/cli ng g module employee --routing --route employee --module app
npx -p @angular/cli ng g module department --routing --route department --module app
Create a new value provider, and an interface, you can add it in a new shared
folder, as this code will be shared between multiple modules:
export interface Config {
apiEndPoint: string;
timeout: number;
}
import { InjectionToken } from '@angular/core';
import { Config } from './demo.config';
export const configToken = new InjectionToken<Config>('demo token');
Next, create a new service we will call it ConfigService
which will read the value from token and use it to perform some operation. Use the below command to create it:
npx -p @angular/cli ng g service shared/config
Once created, add the below code to your service:
import { Injectable, Inject } from '@angular/core';
import { configToken } from './demo.token';
import { Config } from './demo.config';
@Injectable({
providedIn: 'root'
})
export class ConfigService {
constructor(@Inject(configToken) private config: Config) {
console.log('new instance is created');
}
getValue() {
return this.config;
}
}
Now let’s use this service in Employee and Department Component we created with their respective modules. We are just printing the values received from Token:
import { Component, OnInit } from '@angular/core';
import { ConfigService } from '../shared/config.service';
@Component({
selector: 'app-employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.css']
})
export class EmployeeComponent implements OnInit {
constructor(private configService: ConfigService) { }
ngOnInit(): void {
console.log(this.configService.getValue());
}
}
import { Component, OnInit } from '@angular/core';
import { ConfigService } from '../shared/config.service';
@Component({
selector: 'app-department',
templateUrl: './department.component.html',
styleUrls: ['./department.component.css']
})
export class DepartmentComponent implements OnInit {
constructor(private configService: ConfigService) { }
ngOnInit(): void {
console.log(this.configService.getValue());
}
}
Next, let’s try to pass 2 different configuration for Employee and Department, add the below code into both the modules:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { EmployeeRoutingModule } from './employee-routing.module';
import { EmployeeComponent } from './employee.component';
import { Config } from '../shared/demo.config';
import { configToken } from '../shared/demo.token';
export const configValue: Config = {
apiEndPoint: 'abc.com',
timeout: 3000
};
@NgModule({
declarations: [EmployeeComponent],
imports: [
CommonModule,
EmployeeRoutingModule
],
providers: [{
provide: configToken, useValue: configValue
}]
})
export class EmployeeModule {
constructor() { }
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DepartmentRoutingModule } from './department-routing.module';
import { DepartmentComponent } from './department.component';
import { Config } from '../shared/demo.config';
import { configToken } from '../shared/demo.token';
export const configValue: Config = {
apiEndPoint: 'xyz.com',
timeout: 4000
};
@NgModule({
declarations: [DepartmentComponent],
imports: [
CommonModule,
DepartmentRoutingModule
],
providers: [{
provide: configToken, useValue: configValue
}]
})
export class DepartmentModule { }
The difference as visible is the config values. Lets run the application and see what we get, remember the providedIn
value is still 'root'
.To test the application in current state let’s add the routes to app.component.html, add the below snippet in app.component.html
:
<a routerLink="employee">Employee</a>
<br>
<a routerLink="department">Department</a>
<router-outlet></router-outlet>
Next, run the application using the below command:
npx -p @angular/cli ng serve -o
The application works. But click on one of the routes, and boom, we have an error. Our expectation was to see the different config values, but we have the error:
So what went wrong here?
We did everything right, our expectation was to get different config values for both employee and department component, but we ended up getting an error. This is due to providedIn: 'root'
value. This diagram demonstrates what happened:
When we provide the service with providedIn: 'root'
, it is registered with our AppModule
. As soon as we tried to activate one of the routes, the service expected the config value which was not provided. So let’s make it work by making changes to our AppModule.
Add the below code to your app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { Condfig } from './shared/demo.config';
import { configToken } from './shared/demo.token';
export const configValue: Config = {
apiEndPoint: 'def.com',
timeout: 5000
};
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [{
provide: configToken, useValue: configValue
}],
bootstrap: [AppComponent]
})
export class AppModule { }
Now the application works, and click on routes, we will just see below 2 values for both employee and department component. Which is expected as per Image 1.
apiEndPoint: 'def.com'
timeout: 5000
What we wanted to achieve
In reality we wanted something like Image 2, where each module has their own instance, but with providedIn: 'root'
this was not possible. To resolve this issues, the previous solution was implementing forRoot
and forChild
static methods, so each component can have their own instances.
The other way to achieve this was to provide the ConfigService
in each and every module, but the problem is service is not tree shakable any more.
@NgModule({
providers: [
ConfigService,
{
provide: CONFIG_TOKEN, useValue: CONFIG_VALUE
}]
}
export class EmployeeModule { }
Now let’s change the providedIn
property for ConfigService
to below:
@Injectable({
providedIn: 'any'
})
Run the application again and notice the console now:

Bingo! We got the separate instances, without implementing forRoot
or forChild
static methods, or compromising with tree shakable providers.
So what happened after changing the providedIn: 'any'
, as shown by the image below, now all the eager loaded module will share one common instance and all lazy loaded module will have their own instance of ConfigService
.
Conclusion
Earlier it was a challenge to ensure that you'll get a new instance of a service for lazy loaded modules. Now with the new value 'any'
it’s easy to achieve. We can provide any tokens and developers can provided the values per lazy loaded module and the service will always create a new instance per lazy loaded module.
You can download the code for this project from GitHub.