DEV Community

Àlex Rodríguez Bacardit
Àlex Rodríguez Bacardit

Posted on • Originally published at marsbased.com

Forcing New Service Instance Creation. Overriding Dependency Injection in Angular

Hi all! First post here! I want to share a post written by a colleague of mine at MarsBased that I think should be posted here too, so my heartfelt thanks to David Garmendia for writing it. Let's start!

In Angular, injected services are determined by various factors that we cannot always directly control. Let's see some ways of overcome them.

Learning how to force the injector to create new instances instead of using existing ones can be a solution to typical problems that arise when using singleton objects. For example, when a service maintains state and is used from multiple places simultaneously.

The proper way to inject a service into our class is perfectly documented in one of the Angular documentation guides: Introduction to services and dependency injection. This is the recommended way for most cases.

Usually, we can have sufficient control over service instances by specifying the service in components or modules through the providers attribute of these objects. For example, if at a certain point in our component and module hierarchy we want to ensure that a new instance of a service is created, we just have to declare it in that attribute.

@Component({
  standalone: true,
  selector:    'app-hero-list',
  templateUrl: './hero-list.component.html',
  providers:  [ HeroService ]
})
export class HeroListComponent {
Enter fullscreen mode Exit fullscreen mode

In the above example, even though the HeroService service has been declared in a higher component or module, through the providers attribute of the component, we force the injector to create a new instance that will be used from this component downwards.

However, we cannot always or do not always want to use this method. An example is services that are instantiated at the root level and used from other services. Services do not have the providers attribute available, so it is not possible to force this instantiation from a service. It is possible that if the service is not at the root level, we can find a module or component that instantiates it, but it is not always possible.

In these cases, it is possible to use the dependency injector to force the creation of this new instance at the service level. For example, let's say we have the following two services.

@Injectable({providedIn: 'root'})
export class HeroService {
  private dataHeroService: DataHeroService = injectable(DataHeroService)
Enter fullscreen mode Exit fullscreen mode
@Injectable({providedIn: 'root'})
export class DataHeroService {
  myStateNumber = 0
Enter fullscreen mode Exit fullscreen mode

When using DataHeroService from HeroService we access the only instance (singleton) that exists of this service throughout the application. Since DataHeroService has internal state, there is a possibility that a third object modifies that state, thereby modifying the behavior of DataHeroService and consequently behaving unexpectedly from the point of view of HeroService.

If we wanted to ensure that HeroService gets a unique service instance that will not be shared with any other object in the application, we can do it like this.

@Injectable({providedIn: 'root'})
export class HeroService {
  private dataHeroService: DataHeroService;
  private injector = inject(Injector);

  constructor() {
    const injector = Injector.create({ providers: [DataHeroService], parent: this.injector });
    this.dataHeroService = injector.get(DataHeroService);
  }
Enter fullscreen mode Exit fullscreen mode

In this example, we are using the static method Injector.create to create a new injector, to which we pass the service, or list of services, that we want to create and a reference to the current injector in order to resolve the dependencies that DataHeroService may have.

Once the new injector is instantiated, we can ask it for the instance of DataHeroService that will be unique in this context.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.