How Using Angular’s inject() Function Has......

How Using Angular’s inject() Function Has Saved Many Lines of Code

How Using Angular’s inject() Function Has Saved Many Lines of Code

How Using Angular’s inject() Function Has Saved Many Lines of Code


What is the inject() function?

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.



The old way of injecting dependencies using the 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,
  ) {}
}


The new way of injecting dependencies using the inject() function:


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);
}


Benefits of using inject() vs using the constructor


As you can notice, there are quite some advantages to using inject() instead of the constructor:


  • it makes the code cleaner, more readable and consistent (even when injecting tokens vs services)


  • the types will be automatically inferred and do not need to be manually specified


  • easier and less verbose inheritance (more details about this below)


Better inheritance


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.


Real use-case example

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 thisthisthis, 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.


Conclusions


  • the inject() function provides a better and more modern way to inject independencies that should usually be preferred to using constructors;
  • it is easy to migrate your code base to the usage of inject() and it will be useful especially if you have to deal with inheritance;
  • you can adopt inject() in legacy projects gradually, there is no need to migrate all your codebase at once.



Share Article:
  • Facebook
  • Instagram
  • LinkedIn
  • Twitter
  • Recent Posts