Angular DI: @Injectable(), providedIn, Provider, and Injector
These four concepts are closely related:
@Injectable()
↓
Makes the service available for DI configuration
↓
providedIn: 'root'
↓
Registers a provider with the root environment injector
↓
Injector resolves/creates the service
↓
Component receives the service
1. What is @Injectable()?
What is it?
@Injectable() is an Angular decorator that marks a class as participating in Angular's Dependency Injection (DI) system and can also configure how that class is provided.
It is most commonly used with services.
@Injectable({
providedIn: 'root'
})
export class ProductService {
}
In simple terms:
@Injectable()tells Angular that this class can be managed through Angular's dependency injection system.
Why do we need it?
A service may itself depend on other services.
For example:
ProductService
↓
HttpClient
Angular DI needs the appropriate metadata/provider configuration to resolve those dependencies.
For example:
@Injectable({
providedIn: 'root'
})
export class ProductService {
constructor(private http: HttpClient) {
}
}
Angular can resolve HttpClient and inject it into ProductService.
2. What does providedIn: 'root' mean?
What is it?
Consider:
@Injectable({
providedIn: 'root'
})
export class ProductService {
}
providedIn: 'root' tells Angular to make ProductService available through the application's root environment injector.
In a typical application, this means the service is:
- Available throughout the application
- Created lazily when first requested
- Shared as one instance within that injector
Conceptually:
Application
│
Root Environment Injector
│
└── ProductService
↑
shared instance
/ \
Component A Component B
Why use providedIn: 'root'?
Without it, you may need to explicitly configure a provider elsewhere.
For example:
@Component({
selector: 'app-product',
providers: [ProductService]
})
But this makes the provider local to that component's injector hierarchy.
For application-wide services, this is usually preferred:
@Injectable({
providedIn: 'root'
})
Advantage: Tree-Shaking
providedIn: 'root' works well with Angular's tree-shaking.
If a service is never actually referenced, build tooling can often remove the unused service code from the final bundle.
3. What is a Provider?
What is it?
A provider is configuration that tells Angular's DI system:
When someone requests this dependency/token, how should I supply it?
Simple example:
providers: [ProductService]
This is shorthand for a provider configuration similar to:
providers: [
{
provide: ProductService,
useClass: ProductService
}
]
Here:
provide → What dependency/token is being requested?
useClass → Which class should Angular instantiate?
Common Provider Types
Angular supports several provider configurations.
useClass
{
provide: ProductService,
useClass: ProductService
}
Create an instance of a class.
useValue
{
provide: 'API_URL',
useValue: 'https://api.example.com'
}
Provide an existing value.
In real Angular applications, an InjectionToken is generally preferable to a plain string token.
useFactory
{
provide: ProductService,
useFactory: () => new ProductService()
}
Create the dependency using a factory function.
useExisting
{
provide: OldProductService,
useExisting: ProductService
}
Make one token resolve to an already provided dependency.
4. What is an Injector?
What is it?
An injector is the Angular DI mechanism responsible for resolving dependencies from providers and returning the appropriate instances.
Suppose a component requests:
constructor(private productService: ProductService) {
}
Angular's injector effectively asks:
What does Product need?
↓
ProductService
↓
Do I have a provider for ProductService?
↓
Yes
↓
Create/retrieve ProductService
↓
Inject it into Product
Full Simple Sample Program
product.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ProductService {
getProducts(): string[] {
return [
'Laptop',
'Mobile',
'Keyboard'
];
}
}
product.ts
import { Component } from '@angular/core';
import { ProductService } from './product.service';
@Component({
selector: 'app-product',
standalone: true,
templateUrl: './product.html'
})
export class Product {
products: string[] = [];
constructor(private productService: ProductService) {
this.products = this.productService.getProducts();
}
}
product.html
<h2>Products</h2>
<ul>
@for (product of products; track product) {
<li>{{ product }}</li>
}
</ul>
How the Program Works
Step 1 — @Injectable()
@Injectable({
providedIn: 'root'
})
ProductService participates in Angular DI and is configured to be provided from the root environment injector.
Step 2 — Provider
Because of:
providedIn: 'root'
Angular has provider information for:
ProductService
Conceptually:
ProductService token
↓
ProductService implementation
Step 3 — Component requests the service
constructor(private productService: ProductService)
The component says:
I require
ProductService.
The component does not manually create it.
Step 4 — Injector resolves it
The injector searches for a provider for:
ProductService
It finds the root provider.
Step 5 — Service instance is supplied
Angular creates the service if necessary and injects it into the component.
Product Component
│
│ requests
▼
ProductService
│
▼
Injector
│
│ finds
▼
Provider
│
│ creates/resolves
▼
ProductService instance
│
▼
Product Component
How All Four Concepts Are Connected
This distinction is very important for interviews:
| Concept | Meaning |
|---|---|
@Injectable() |
Decorator that enables/configures a class for Angular DI |
providedIn: 'root' |
Registers the service with the root environment injector |
| Provider | Recipe/configuration describing how to supply a dependency |
| Injector | DI mechanism that resolves and supplies dependencies |
A simple way to remember:
@Injectable()
↓
"This class participates in DI"
providedIn: 'root'
↓
"Provide it application-wide"
Provider
↓
"How should I supply it?"
Injector
↓
"Find it, create/reuse it, and give it to the consumer"
Advantages
@Injectable()
- Supports Angular DI.
- Allows services to receive other dependencies.
- Keeps dependency configuration declarative.
providedIn: 'root'
- Simple application-wide registration.
- Usually one shared instance.
- Supports tree-shaking.
- No need to manually add the service to component providers.
Providers
- Allow implementations to be replaced.
- Support
useClass,useValue,useFactory, anduseExisting. - Useful for testing and configuration.
Injectors
- Automatically resolve dependencies.
- Manage dependency instances.
- Support hierarchical DI.
- Reduce manual object creation.
Disadvantages / Things to Watch
Wrong provider location
If you write:
@Component({
providers: [ProductService]
})
the component can receive a separate service instance rather than the root instance.
This matters when the service stores shared state.
Too many dependencies
A constructor like:
constructor(
private a: ServiceA,
private b: ServiceB,
private c: ServiceC,
private d: ServiceD,
private e: ServiceE,
private f: ServiceF
) {}
can indicate that the component/service has too many responsibilities.
Key Points
@Injectable()is strongly associated with services and Angular DI.providedIn: 'root'provides the service through the root environment injector.- A provider is configuration.
- An injector is the resolver/container mechanism.
- Providers tell injectors how dependencies should be supplied.
- The injector creates a service when required or returns an existing appropriate instance.
- Angular DI is hierarchical.
- Providing a service at different levels can create different instances.
providedIn: 'root'is the standard choice for many application-wide services.
Interview Questions and Answers
1. What is @Injectable()?
@Injectable() is an Angular decorator that marks/configures a class for Angular's dependency injection system. It is commonly used with services.
2. What does providedIn: 'root' mean?
It tells Angular to provide the service from the application's root environment injector, normally making one shared instance available application-wide.
3. What is a provider?
A provider is configuration that tells Angular how to create or obtain a dependency for a particular DI token.
4. What is an injector?
An injector is Angular's DI mechanism that looks up providers, creates or retrieves dependency instances, and supplies them to consumers.
5. What is the difference between a provider and an injector?
This is an important distinction:
Provider = recipe/configuration.
Injector = mechanism/container that uses that recipe to resolve the dependency.
6. Is providedIn: 'root' the same as providers: [ProductService]?
Not exactly.
@Injectable({
providedIn: 'root'
})
provides the service at the application root level.
Whereas:
@Component({
providers: [ProductService]
})
provides it from that component's injector and can result in a separate instance for that component subtree.
Interview Shortcut
@Injectable() → Makes/configures class for DI
providedIn: 'root' → Root-level provider registration
Provider → HOW to supply dependency
Injector → RESOLVES and supplies dependency
Dependency → WHAT the consumer needs
The most important distinction: a provider describes how to obtain an object; the injector uses providers to resolve and inject that object.