inject() vs Constructor Injection
Two ways to inject dependencies in Angular. Understand both and know when to use which.
Constructor injection is the traditional way Angular provides dependencies to a class. You list all dependencies as constructor parameters, and Angular reads the TypeScript parameter types at runtime to know what to inject. This has been the standard since Angular 2.
While it works perfectly fine, it has limitations โ you can only use it inside class constructors, and the constructor gets cluttered when you have many dependencies.
The inject() function, introduced in Angular 14, lets you declare dependencies as class field initializers instead of constructor parameters. Each call to inject() retrieves one dependency.
This keeps the constructor clean and makes each dependency declaration independent.
The inject() function has several advantages over constructor injection:
- Works in more places: field initializers, factory functions, functional guards, interceptors, and standalone component factories
- Cleaner constructor: constructor stays focused on initialization logic, not dependency declarations
- Conditional injection: you can conditionally inject dependencies using
ifstatements - Better with signals: works naturally alongside signal declarations in class fields
- Tree-shakeable: unused dependencies can be more easily identified by the compiler
Here is a comparison of where each injection method works:
| Location | Constructor Injection | inject() |
|---|---|---|
| Component class constructor | โ | โ |
| Service class constructor | โ | โ |
| Class field initializer | โ | โ |
| Functional guard | โ | โ |
| Functional interceptor | โ | โ |
| Factory function | โ | โ |
| Pipe class | โ | โ |
| setTimeout / event handler | โ | โ |
Note: inject() must be called in an injection context. You cannot call it inside event handlers, setTimeout, or plain functions outside of Angular's DI context.
Migrating from constructor injection to inject() is straightforward. Here is a step-by-step guide:
Step 1: Replace each constructor parameter with a field initializer
Step 2: If constructor has non-DI logic, keep the constructor for that
Step 3: Move any initialization that depends on injections to the field or ngOnInit
Migrate one service at a time. There is no rush โ both patterns work perfectly together.
Key Takeaways
- โ Constructor injection lists dependencies in constructor parameters (traditional way)
- โ inject() declares dependencies as class field initializers (modern way)
- โ inject() works in more places: guards, interceptors, factories, field initializers
- โ inject() keeps the constructor clean for non-DI initialization logic
- โ Either pattern works โ pick one and be consistent across your codebase
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