156
votes

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:

enter image description here

14
You're sure you have not added UserService and FacebookService to providers anywhere else?Günter Zöchbauer

14 Answers

134
votes

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:

155
votes

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 {
}

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.

25
votes

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.

16
votes

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 { }
8
votes

this seems to be working well for me

@Injectable()
export class MyStaticService {
  static instance: MyStaticService;

  constructor() {
    return MyStaticService.instance = MyStaticService.instance || this;
  }
}
7
votes

Adding @Injectable decorator to the Service, AND registering it as a provider in the Root Module will make it a singleton.

5
votes

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");
        });
    }
    ...
}
3
votes

You can use useValue in providers

import { MyService } from './my.service';

@NgModule({
...
  providers: [ { provide: MyService, useValue: new MyService() } ],
...
})
3
votes

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:

  1. Declare that the service should be provided in the application root.
  2. 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:

2
votes

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;
  }
}
1
votes

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.

  1. use the providedIn property, or

  2. 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.

0
votes
  1. 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.
  2. 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] })

  3. 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.

0
votes

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$.

0
votes

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>