Dependency Injection โ How Angular Resolves
Angular gives you dependencies โ you don't create them yourself.
Dependency Injection (DI) is a pattern where Angular provides dependencies to your classes instead of you creating them.
"DI = Swiggy delivery โ tum biryani nahi banate, Swiggy deliver karta hai."
Without DI: You create everything yourself:
const logger = new LoggerService();\nconst http = new HttpClient();\nconst router = new Router();\n// You manage creation, lifecycle, cleanup...
With DI: You ask, Angular provides:
private logger = inject(LoggerService);\nprivate http = inject(HttpClient);
Angular creates, manages, and provides the instances โ you just use them.
The Injector is the core of DI โ it's a container that holds service instances and knows how to create them.
"Injector = warehouse โ order do, milega."
When you call inject(ServiceName), Angular:
- Checks the injector if an instance already exists
- If yes โ returns that instance
- If no โ creates a new instance using the provider, stores it, and returns it
Angular doesn't have ONE injector โ it has a tree of injectors that mirrors your component tree.
Angular's injectors are organized in a hierarchy:
- Root injector (AppModule/app.config level) โ topmost, one per app
- Component injector โ each component has its own injector
"Root injector = Hyderabad main warehouse, Component injector = local branch."
When a component asks for a service, Angular:
- Checks the component's own injector first
- If not found, checks the parent component's injector
- Keeps going up the tree until it reaches the root injector
- If still not found โ throws an error
This is why providedIn: 'root' makes a service available everywhere โ it lives in the root injector that ALL components can reach.
Let's trace through a real resolution:
// GrandchildComponent injects BiryaniService\nprivate service = inject(BiryaniService);\n\n// Step 1: Check GrandchildComponent's injector\n// โ Not found (BiryaniService is at root)\n\n// Step 2: Check ParentComponent's injector\n// โ Not found\n\n// Step 3: Check AppComponent's injector\n// โ Not found\n\n// Step 4: Check Root Injector\n// โ Found! (because @Injectable({ providedIn: 'root' }))\n// โ Return the singleton instance
"Breadth-first search โ pehle apne level pe dhundho, phir upar jao."
This upward resolution means child components inherit their parent's services โ unless they override them by providing their own.
Why is DI such a powerful pattern?
- Testable โ Inject a mock service in unit tests. No need to modify component code.
- Flexible โ Swap implementations without changing component code. LoggerService โ ConsoleLoggerService.
- Singleton by default โ One instance shared everywhere. Consistent state across the app.
- Decoupled โ Component doesn't know HOW the service is created, just uses it.
"DI ka matlab โ component ko koi fikar nahi ki service kaise bani, bas use karo."
Key Takeaways
- โ DI lets Angular provide dependencies โ you don't create them (like Swiggy delivery).
- โ Injector is the container that holds and creates service instances.
- โ Injectors form a hierarchy matching the component tree โ child checks parent up to root.
- โ Resolution: component injector โ parent โ ... โ root injector.
- โ DI makes code testable, flexible, singleton-efficient, and decoupled.
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login