Introduced in Angular 14, the inject() function allows you to inject dependencies into services, components, directives, etc. instead of injecting them using the class constructor.
import { Component } from '@angular/core'; @Component({ /* ... */ }) export class MyComponent { constructor( @Inject(SOME_TOKEN) private readonly someToken: string, private readonly myService: MyService, private readonly httpClient: HttpClient, ) {} }
import { Component, inject } from '@angular/core'; @Component({ /* ... */ }) export class MyComponent { private readonly someToken = inject(SOME_TOKEN); private readonly myService = inject(MyService); private readonly httpClient = inject(HttpClient); }
As you can notice, there are quite some advantages to using inject()
instead of the constructor:
I find the inject()
function particularly useful when you deal with inheritance. Consider a scenario where you want to reuse code and have a ParentService
abstract class that will be extended by several child classes:
export abstract class ParentService { constructor( protected readonly configKey: string, protected readonly httpClient: HttpClient, protected readonly helperService: HelperService, ) {} // ... some code here that will be reused in the children of ParentService }
hildren of ParentService
will extend it this way:
@Injectable({ providedIn: 'root' }) export class ChildService extends ParentService { constructor( protected readonly httpClient: HttpClient, protected readonly helperService: HelperService, ) { super('my-config-key', httpClient, helperService); } // ... some child-specific code here }
As you can see, there is a lot of repetition: all children need to import the HttpClient
and the HelperService
just because the constructor of ParentService
requires them.
Thanks to the inject()
function, we can avoid this unnecessary repetition:
export abstract class ParentService { protected abstract readonly configKey: string; // using "abstract" to force children to initialise this field protected readonly httpClient = inject(HttpClient); protected readonly helperService = inject(HelperService); // ... some code here that will be reused in the children of ParentService ... } @Injectable({ providedIn: 'root' }) export class ChildService extends ParentService { protected readonly configKey = 'my-config-key'; // ... some child-specific code here }
The resulting code is much cleaner and avoids repetitions: the children of ParentService
do not need to import HttpClient
and HelperService
just to pass it to the parent, but they can still access to this.httpClient
and this.helperService
if needed.
Imagine a scenario where you have more dependencies and many children extending the base class, using inject()
will save many lines of code.
When refactoring an old project that I built with Angular back in 2019 using the inject()
function, I managed to remove around 1000~ lines of code simply by getting rid (or reducing the use) of constructors. You can see it in action in this, this, this, and this commits.
Yes, I know what you’re thinking. You can argue that I could have considered composition over inheritance, but discussing this goes beyond the scope of this article.
inject()
function provides a better and more modern way to inject independencies that should usually be preferred to using constructors;inject()
and it will be useful especially if you have to deal with inheritance;inject()
in legacy projects gradually, there is no need to migrate all your codebase at once.