Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2: how to force an injectable to be a singleton in the app

I have an @Injectable service in my application that is added as a provider in AppModule. I want to make sure no one in my development team injects it in any other module. A single instance is enough and it has some complicated logic that I don't want to be run twice. Any ideas?

I know how DI works in angular 2, so answers like 'Make sure it is added as a provider only in App Module' won't help. :(

PLEASE NOTE, that I want it to produce some sort of error at build or run time if the service is provided to any other but AppModule.

like image 934
eddyP23 Avatar asked Mar 11 '23 02:03

eddyP23


1 Answers

Angular maintains a single instance per provider.

Ensure you provide a service only once and DI will ensure that there is only one single instance in your application.

If you provide a service on a comonent @Component({ ..., providers: [...]}), then there will be as many instances as component instances.

If you provide a service only in providers of AppModule or providers of modules imported to AppModule, then there will only be a single instance for your whole application:

@NgModule({
  providers: [...],
  imports: [...]
})
export class AppModule {}

A pitfall are lazy loaded modules. If a module is lazy loaded then providers provided there will cause another instance to be created, because lazy loaded modules have their own DI root scope. For lazy loaded modules implement forRoot() and add providers that should be application-wide singletons only in forRoot(), but not in providers and import it in AppModule

@NgModule({
  providers: [...],
  imports: [LazyLoadedModuleWithSingltonProvider.forRoot()]
})
export class AppModule {}

update

To prevent instantiation of multiple instances of a service you can use a workaround like

@Injectable()
export class MyService {
  private static instanceCounter = 0;
  private instanceNumber = instanceCounter++;

  constructor() {
    if(this.instanceNumber > 0) {
      throw 'MyService must be kept a singleton but more than one instance was created';
    }
  }
}

Another way is to move singleton services to a CoreModule and prevent this module being imported anywhere else than in the AppModule

https://angular.io/docs/ts/latest/guide/ngmodule.html#!#prevent-reimport

Only the root AppModule should import the CoreModule. Bad things happen if a lazy loaded module imports it.

like image 190
Günter Zöchbauer Avatar answered Apr 27 '23 08:04

Günter Zöchbauer