Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject non-singleton service into non-singleton service in Angular

What would be the way to go if I wanted to inject a non-singleton service into another non-singleton service in Angular?

Let's say I have a ComponentA which uses a ServiceA like this

@component({
  selector: "componentA",
  providers: [ServiceA]
})
export class ComponentA {
  constructor(private serviceA: ServiceA) {}
}

and now, ServiceA also depends on ServiceB, so I need to inject it

@Injectable()
export class ServiceA {
   constructor(private serviceB: ServiceB) {}
}

How/where would I declare my ServiceB? In the module? In that case would that be a singleton?

If I add it to the providers in ComponentA it works fine, but then I need to know in ComponentA that ServiceA depends on ServiceB, which doesn't seem fine to me.

Thanks!

like image 567
David Avatar asked Feb 27 '18 17:02

David


People also ask

Can I inject a service into another service Angular?

Angular provides the ability for you to inject a service into a component to give that component access to the service. The @Injectable() decorator defines a class as a service in Angular and allows Angular to inject it into a component as a dependency.

What is non singleton service in Angular?

In this case, service is a non-singleton nature. It will create multiple instances of a service. Every time a new instance of provided service will be created when a component is used inside another component. Service is being destroyed along with the component.

How do you prevent to use a service as a singleton in Angular?

There are multiple ways to prevent this: Use the providedIn syntax instead of registering the service in the module. Separate your services into their own module. Define forRoot() and forChild() methods in the module.

What is the use of providedIn in Angular?

By default, this decorator has a providedIn property, which creates a provider for the service. In this case, providedIn: 'root' specifies that Angular should provide the service in the root injector.


1 Answers

Roughly speaking, unless you have the providedIn options in the @Injectable(), Angular will not treat the service as a singleton. You can refer to the docs for details on how that works: https://angular.io/api/core/Injectable#providedin

The issue that you will run into, is that if you simply do:

@Injectable()
export class ServiceB {
   constructor() {}
}

It will throw an error when trying to inject ServiceB into ServiceA because it has no provider. Now normally, to resolve this, you would add { providedIn: 'root' } to ServiceB's @Injectable(). This will create service B as an application-wide singleton service.

When you use @Component( { providers: [] } ) that will create an instance just for that component. But you can't do that for a service.

You may ask, why doesn't @Injectable() have a providers option like @Component() does? The Angular team doesn't want to do this for a number of reasons, see this github issue for more info https://github.com/angular/angular/issues/5622

TLDR:

To provide you with something to solve this problem, you can keep it simple and avoid using Angular's DI altogether if you want control over what instances you use, you can just do it yourself:

@Injectable()
export class ServiceA {
   constructor(private serviceB: ServiceB = new ServiceB()) {}
}

or

  • use Angular's DI with @NgModule() and/or @Component() providers arrays,
  • @Injectable( { providedIn: <something> } ) (https://angular.io/api/core/Injectable#providedin)

Edit: as suggested by @minigeek, using providedIn you can use any to make each lazy module get it's own instance of the service.

like image 189
RJM Avatar answered Oct 05 '22 15:10

RJM