I've read that injecting when bootstrapping should have all children share the same instance, but my main and header components (main app includes header component and router-outlet) are each getting a separate instance of my services.
I have a FacebookService which I use to make calls to the facebook javascript api and a UserService that uses the FacebookService. Here's my bootstrap:
bootstrap(MainAppComponent, [ROUTER_PROVIDERS, UserService, FacebookService]);
From my logging it looks like the bootstrap call finishes, then I see the FacebookService then UserService being created before the code in each of the constructors runs, the MainAppComponent, the HeaderComponent and the DefaultComponent:
https://i.stack.imgur.com/6q6tG.png
UserService
and FacebookService
to providers
anywhere else?
Update (Angular 6 +)
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 {
}
https://angular.io/guide/singleton-services
https://angular.io/guide/providers
Update (Angular 2)
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:
CoreModule.ts
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 { }
AppModule.ts
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 { }
Original Answer
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:
The root one you configure when bootstrapping your application
An injector per component. If you use a component inside another one. The component injector is a child of the parent component one. The application component (the one you specify when boostrapping your application) has the root injector as parent one).
When Angular2 tries to inject something in the component constructor:
It looks into the injector associated with the component. If there is matching one, it will use it to get the corresponding instance. This instance is lazily created and is a singleton for this injector.
If there is no provider at this level, it will look at the parent injector (and so on).
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:
What's the best way to inject one service into another in angular 2 (Beta)?
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.
SHARE_SERVICE_PROVIDER
be YOUR_SERVICE_PROVIDER
in the component? Also I'm assuming that importing the service file is needed like normal and the constructor will still have a parameter of type 'YourService', right? I like this I think, allows you to guarantee a singleton and not have to make sure the service is provided up the hierarchy. It also allows individual components to get their own copy by just listing the service in providers
instead of the singleton provider, right?
YOUR_SERVICE_PROVIDER
. Yes, all components will get the same instance by just adding it in the providers.
instance
property into a key-value map of instances
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 { }
Injectable
class isn't a service and just contains static
strings for global use?
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.
constructor
like this: import { SomeService } from '../../services/some/some'; @Component({ selector: 'page-selector', templateUrl: 'page.html', }) export class SomePage { constructor( public someService: SomeService ) { }
Here is a working example with Angular version 2.3. Just call the constructor of the service the stand way like this constructor(private _userService:UserService) . And it will create a singleton for the app.
user.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Subject } from 'rxjs/Subject';
import { User } from '../object/user';
@Injectable()
export class UserService {
private userChangedSource;
public observableEvents;
loggedUser:User;
constructor() {
this.userChangedSource = new Subject<any>();
this.observableEvents = this.userChangedSource.asObservable();
}
userLoggedIn(user:User) {
this.loggedUser = user;
this.userChangedSource.next(user);
}
...
}
app.component.ts
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { UserService } from '../service/user.service';
import { User } from '../object/user';
@Component({
selector: 'myApp',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
loggedUser:User;
constructor(private _userService:UserService) {
this._userService.observableEvents.subscribe(user => {
this.loggedUser = user;
console.log("event triggered");
});
}
...
}
You can use useValue
in providers
import { MyService } from './my.service';
@NgModule({
...
providers: [ { provide: MyService, useValue: new MyService() } ],
...
})
useValue
is not related to singleton. Usevalue is just to pass a value instead of a Type
(useClass
) that DI calls new
on or useFactory
where a function is passed that returns the value when called by DI. Angular DI maintains a single instance per provider automatically. Only provide it once and you have a singleton. Sorry, I have to downvote because it's just invalid information :-/
From Angular@6, you can have providedIn
in an Injectable
.
@Injectable({
providedIn: 'root'
})
export class UserService {
}
Check the docs here
There are two ways to make a service a singleton in Angular: Declare that the service should be provided in the application root. Include the service in the AppModule or in a module that is only imported by the AppModule. Beginning with Angular 6.0, the preferred way to create a singleton services is to specify on the service that it should be provided in the application root. This is done by setting providedIn to root on the service's @Injectable decorator:
public static
.
Just declare your service as provider in app.module.ts only.
It did the job for me.
providers: [Topic1Service,Topic2Service,...,TopicNService],
then either instanciate it using a constructor private parameter :
constructor(private topicService: TopicService) { }
or since if your service is used from html, the -prod option will claim:
Property 'topicService' is private and only accessible within class 'SomeComponent'.
add a member for your service and fill it with the instance recieved in the constructor:
export class SomeComponent {
topicService: TopicService;
constructor(private topicService: TopicService) {
this.topicService= topicService;
}
}
A singleton service
is a service for which only one instance exists in an app.
There are (2) ways to provide a singleton service for your application.
use the providedIn property, or provide the module directly in the AppModule of the application
Using providedIn
Beginning with Angular 6.0, the preferred way to create a singleton service is to set providedIn
to root on the service's @Injectable()
decorator. This tells Angular to provide the service in the application root.
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class UserService {
}
NgModule providers array
In apps built with Angular versions prior to 6.0, services are registered NgModule providers arrays as follows:
@NgModule({
...
providers: [UserService],
...
})
If this NgModule
were the root AppModule
, the UserService would be a singleton and available throughout the app. Though you may see it coded this way, using the providedIn
property of the @Injectable()
decorator on the service itself is preferable as of Angular 6.0 as it makes your services tree-shakable.
If you want to make service singleton at application level you should define it in app.module.ts providers: [ MyApplicationService ] (you can define the same in child module as well to make it that module specific) Do not add this service in provider which creates an instance for that component which breaks the singleton concept, just inject through constructor. If you want to define singleton service at component level create service, add that service in app.module.ts and add in providers array inside specific component as shown in below snipet. @Component({ selector: 'app-root', templateUrl: './test.component.html', styleUrls: ['./test.component.scss'], providers : [TestMyService] }) Angular 6 provide new way to add service at application level. Instead of adding a service class to the providers[] array in AppModule , you can set the following config in @Injectable() : @Injectable({providedIn: 'root'}) export class MyService { ... }
The "new syntax" does offer one advantage though: Services can be loaded lazily by Angular (behind the scenes) and redundant code can be removed automatically. This can lead to a better performance and loading speed - though this really only kicks in for bigger services and apps in general.
In addition to the above excellent answers, there may be something else that is missing if things in your singleton still aren't behaving as a singleton. I ran into the issue when calling a public function on the singleton and finding that it was using the wrong variables. It turns out that the problem was the this
isn't guaranteed to be bound to the singleton for any public functions in the singleton. This can be corrected by following the advice here, like so:
@Injectable({
providedIn: 'root',
})
export class SubscriptableService {
public serviceRequested: Subject<ServiceArgs>;
public onServiceRequested$: Observable<ServiceArgs>;
constructor() {
this.serviceRequested = new Subject<ServiceArgs>();
this.onServiceRequested$ = this.serviceRequested.asObservable();
// save context so the singleton pattern is respected
this.requestService = this.requestService.bind(this);
}
public requestService(arg: ServiceArgs) {
this.serviceRequested.next(arg);
}
}
Alternatively, you can simply declare the class members as public static
instead of public
, then the context won't matter, but you'll have to access them like SubscriptableService.onServiceRequested$
instead of using dependency injection and accessing them via this.subscriptableService.onServiceRequested$
.
Parent and child services
I was having trouble with a parent service and its child using different instances. To force one instance to be used, you can alias the parent with reference to the child in your app module providers. The parent will not be able to access the child's properties, but the same instance will be used for both services. https://angular.io/guide/dependency-injection-providers#aliased-class-providers
app.module.ts
providers: [
ChildService,
// Alias ParentService w/ reference to ChildService
{ provide: ParentService, useExisting: ChildService}
]
Services used by components outside of your app modules scope
When creating a library consisting of a component and a service, I ran into an issue where two instances would be created. One by my Angular project and one by the component inside of my library. The fix:
my-outside.component.ts
@Component({...})
export class MyOutsideComponent {
@Input() serviceInstance: MyOutsideService;
...
}
my-inside.component.ts
constructor(public myService: MyOutsideService) { }
my-inside.component.hmtl
<app-my-outside [serviceInstance]="myService"></app-my-outside>
Well, Scope of Angular service depends upon where you provide the service at root module, lazy loaded module or at component level.
Here is a video which describe it beutifully with real examples.
Success story sharing
[providers]
in your module file, not inbootstrap()
, right?ApiService
inAppModule
'sproviders
, and thus avoid the need forCoreModule
? (Not sure this is what @JasonSwett is suggesting.)ApiService
on the top but why then bother at all to put it in the providers array of the CoreModule and then import that one in app.module... it didn't click for me, yet.TestService
is specified in both core and app modules, instances aren't created for the modules because it is provided by the component so angular never gets that high up the injector tree. So if you provide a service in your module and never use it, an instance doesn't seem to be created.