The recommended way to create a singleton service has changed. It is now recommended to specify in the @Injectable
decorator on the service that it should be provided in the 'root'. This makes a lot of sense to me and there's no need to list all the provided services in your modules at all anymore. You just import the services when you need them and they register themselves in the proper place. You can also specify a module so it will only be provided if the module is imported.
@Injectable({
providedIn: 'root',
})
export class ApiService {
}
With NgModule, the way to do it now I think is to create a 'CoreModule' with your service class in it, and list the service in the module's providers. Then you import the core module in your main app module which will provide the one instance to any children requesting that class in their constructors:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ApiService } from './api.service';
@NgModule({
imports: [
CommonModule
],
exports: [ // components that we want to make available
],
declarations: [ // components for use in THIS module
],
providers: [ // singleton services
ApiService,
]
})
export class CoreModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
@NgModule({
declarations: [ AppComponent ],
imports: [
CommonModule,
CoreModule // will provide ApiService
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }
If you list a provider in bootstrap()
, you don't need to list them in your component decorator:
import { ApiService } from '../core/api-service';
@Component({
selector: 'main-app',
templateUrl: '/views/main-app.html',
// DO NOT LIST PROVIDERS HERE IF THEY ARE IN bootstrap()!
// (unless you want a new instance)
//providers: [ApiService]
})
export class MainAppComponent {
constructor(private api: ApiService) {}
}
In fact listing your class in 'providers' creates a new instance of it, if any parent component already lists it then the children don't need to, and if they do they will get a new instance.
Jason is completely right! It's caused by the way dependency injection works. It's based on hierarchical injectors.
There are several injectors within an Angular2 application:
When Angular2 tries to inject something in the component constructor:
So if you want to have a singleton for the whole application, you need to have the provider defined either at the level of the root injector or the application component injector.
But Angular2 will look at the injector tree from the bottom. This means that the provider at the lowest level will be used and the scope of the associated instance will be this level.
See this question for more details:
I know angular has hierarchical injectors like Thierry said.
But I have another option here in case you find a use-case where you don't really want to inject it at the parent.
We can achieve that by creating an instance of the service, and on provide always return that.
import { provide, Injectable } from '@angular/core';
import { Http } from '@angular/core'; //Dummy example of dependencies
@Injectable()
export class YourService {
private static instance: YourService = null;
// Return the instance of the service
public static getInstance(http: Http): YourService {
if (YourService.instance === null) {
YourService.instance = new YourService(http);
}
return YourService.instance;
}
constructor(private http: Http) {}
}
export const YOUR_SERVICE_PROVIDER = [
provide(YourService, {
deps: [Http],
useFactory: (http: Http): YourService => {
return YourService.getInstance(http);
}
})
];
And then on your component you use your custom provide method.
@Component({
providers: [YOUR_SERVICE_PROVIDER]
})
And you should have a singleton service without depending on the hierarchical injectors.
I'm not saying this is a better way, is just in case someone has a problem where hierarchical injectors aren't possible.
Syntax has been changed. Check this link
Dependencies are singletons within the scope of an injector. In below example, a single HeroService instance is shared among the HeroesComponent and its HeroListComponent children.
Step 1. Create singleton class with @Injectable decorator
@Injectable()
export class HeroService {
getHeroes() { return HEROES; }
}
Step 2. Inject in constructor
export class HeroListComponent {
constructor(heroService: HeroService) {
this.heroes = heroService.getHeroes();
}
Step 3. Register provider
@NgModule({
imports: [
BrowserModule,
FormsModule,
routing,
HttpModule,
JsonpModule
],
declarations: [
AppComponent,
HeroesComponent,
routedComponents
],
providers: [
HeroService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
this seems to be working well for me
@Injectable()
export class MyStaticService {
static instance: MyStaticService;
constructor() {
return MyStaticService.instance = MyStaticService.instance || this;
}
}
Adding @Injectable
decorator to the Service, AND registering it as a provider in the Root Module will make it a singleton.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With