page change angular2 angular typescript angular2-routing angular2-services

change - ¿Cómo creo un servicio singleton en Angular 2?



singleton service angular 4 (12)

He leído que la inyección cuando el bootstrapping debería hacer que todos los niños compartan la misma instancia, pero mis componentes principales y de encabezado (la aplicación principal incluye el componente de encabezado y la salida del enrutador) obtienen cada uno una instancia separada de mis servicios.

Tengo un FacebookService que utilizo para hacer llamadas a la API de Facebook JavaScript y un UserService que utiliza el FacebookService. Aquí está mi bootstrap:

bootstrap(MainAppComponent, [ROUTER_PROVIDERS, UserService, FacebookService]);

Desde mi registro, parece que finaliza la llamada bootstrap, luego veo que se crea FacebookService y luego UserService antes de que se ejecute el código en cada uno de los constructores, MainAppComponent, HeaderComponent y DefaultComponent:


Actualización (angular 6)

La forma recomendada de crear un servicio singleton ha cambiado. Ahora se recomienda especificar en el decorador @Injectable en el servicio que se debe proporcionar en la ''raíz''. Esto tiene mucho sentido para mí y ya no es necesario enumerar todos los servicios proporcionados en sus módulos. Solo importa los servicios cuando los necesita y se registran en el lugar adecuado. También puede especificar un módulo para que solo se proporcione si se importa el módulo.

@Injectable({ providedIn: ''root'', }) export class ApiService { }

Actualización (angular 2)

Con NgModule, creo que la forma de hacerlo ahora es crear un ''CoreModule'' con su clase de servicio y enumerar el servicio en los proveedores del módulo. Luego, importa el módulo principal en el módulo de la aplicación principal que proporcionará una instancia a los niños que soliciten esa clase en sus constructores:

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

Respuesta original

Si incluye un proveedor en bootstrap() , no necesita incluirlo en su decorador de componentes:

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

De hecho, la inclusión de su clase en ''proveedores'' crea una nueva instancia de la misma, si algún componente principal ya lo incluye, los hijos no lo necesitan, y si lo hacen, obtendrán una nueva instancia.


  1. Si desea hacer que el servicio sea único a nivel de aplicación, debe definirlo en app.module.ts

    proveedores: [MyApplicationService] (también puede definir lo mismo en el módulo secundario para que sea específico de ese módulo)

    • No agregue este servicio en el proveedor que crea una instancia para ese componente que rompe el concepto singleton, solo inyecte a través del constructor.
  2. Si desea definir el servicio singleton a nivel de componente, cree el servicio, agregue ese servicio en app.module.ts y agregue la matriz de proveedores dentro del componente específico como se muestra en el siguiente fragmento.

    @Component ({selector: ''app-root'', templateUrl: ''./test.component.html'', styleUrls: [''./test.component.scss''], proveedores: [TestMyService]})

  3. Angular 6 proporciona una nueva forma de agregar servicio a nivel de aplicación. En lugar de agregar una clase de servicio a la matriz de proveedores [] en AppModule, puede establecer la siguiente configuración en @Injectable ():

    @Injectable ({shownIn: ''root''}) clase de exportación MyService {...}

Sin embargo, la "nueva sintaxis" ofrece una ventaja: Angular (detrás de escena) puede cargar los servicios con pereza y eliminar automáticamente el código redundante. Esto puede conducir a un mejor rendimiento y velocidad de carga, aunque esto realmente solo se aplica a servicios y aplicaciones más grandes en general.


Además de las excelentes respuestas anteriores, puede faltar algo más si las cosas en su singleton aún no se comportan como un singleton. Me encontré con el problema cuando llamé a una función pública en el singleton y descubrí que estaba usando las variables incorrectas. Resulta que el problema era que no se garantiza que esto esté vinculado al singleton para ninguna función pública en el singleton. Esto se puede corregir siguiendo los consejos here , así:

@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); } }

Alternativamente, puede simplemente declarar a los miembros de la clase como public static lugar de public , luego el contexto no importará, pero tendrá que acceder a ellos como SubscriptableService.onServiceRequested$ lugar de usar la inyección de dependencia y acceder a ellos a través de this.subscriptableService.onServiceRequested$ .


Agregar decorador @Injectable al Servicio, Y registrarlo como proveedor en el Módulo raíz lo convertirá en un singleton.


Aquí hay un ejemplo de trabajo con Angular versión 2.3. Simplemente llame al constructor del servicio de manera similar a este constructor (private _userService: UserService). Y creará un singleton para la aplicación.

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


Desde Angular @ 6, puede haber providedIn Injectable en un Injectable .

@Injectable({ providedIn: ''root'' }) export class UserService { }

Consulta los documentos aquí

Hay dos formas de hacer que un servicio sea un singleton en Angular:

  1. Declare que el servicio debe proporcionarse en la raíz de la aplicación.
  2. Incluya el servicio en AppModule o en un módulo que solo importa AppModule.

A partir de Angular 6.0, la forma preferida de crear un servicio singleton es especificar en el servicio que se debe proporcionar en la raíz de la aplicación. Esto se hace configurando provideIn para rootear en el decorador @Injectable del servicio:


Jason tiene toda la razón! Es causado por la forma en que funciona la inyección de dependencia. Se basa en inyectores jerárquicos.

Hay varios inyectores dentro de una aplicación Angular2:

  • La raíz que configura al iniciar su aplicación
  • Un inyector por componente. Si usa un componente dentro de otro. El inyector de componentes es hijo del componente principal. El componente de la aplicación (el que especifique al reiniciar su aplicación) tiene el inyector raíz como primario.

Cuando Angular2 intenta inyectar algo en el constructor de componentes:

  • Se ve en el inyector asociado con el componente. Si hay una coincidente, la usará para obtener la instancia correspondiente. Esta instancia se crea perezosamente y es un singleton para este inyector.
  • Si no hay un proveedor en este nivel, mirará el inyector principal (y así sucesivamente).

Por lo tanto, si desea tener un singleton para toda la aplicación, debe tener el proveedor definido a nivel del inyector raíz o del inyector del componente de la aplicación.

Pero Angular2 mirará el árbol del inyector desde la parte inferior. Esto significa que se utilizará el proveedor en el nivel más bajo y el alcance de la instancia asociada será este nivel.

Vea esta pregunta para más detalles:


La sintaxis ha sido cambiada. Mira este link

Las dependencias son singletons dentro del alcance de un inyector. En el siguiente ejemplo, se comparte una sola instancia de HeroService entre HeroesComponent y sus hijos HeroListComponent.

Paso 1. Crear una clase singleton con el decorador @Injectable

@Injectable() export class HeroService { getHeroes() { return HEROES; } }

Paso 2. Inyectar en el constructor

export class HeroListComponent { constructor(heroService: HeroService) { this.heroes = heroService.getHeroes(); }

Paso 3. Registrar proveedor

@NgModule({ imports: [ BrowserModule, FormsModule, routing, HttpModule, JsonpModule ], declarations: [ AppComponent, HeroesComponent, routedComponents ], providers: [ HeroService ], bootstrap: [ AppComponent ] }) export class AppModule { }


Puede usar useValue en proveedores

import { MyService } from ''./my.service''; @NgModule({ ... providers: [ { provide: MyService, useValue: new MyService() } ], ... })


Sé que angular tiene inyectores jerárquicos, como dijo Thierry.

Pero tengo otra opción aquí en caso de que encuentre un caso de uso donde realmente no quiera inyectarlo en el padre.

Podemos lograr eso creando una instancia del servicio, y en proporcionar siempre devolver eso.

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); } }) ];

Y luego, en su componente, utiliza su método de suministro personalizado.

@Component({ providers: [YOUR_SERVICE_PROVIDER] })

Y debe tener un servicio singleton sin depender de los inyectores jerárquicos.

No digo que esta sea una mejor manera, es solo en caso de que alguien tenga un problema donde los inyectores jerárquicos no sean posibles.


Simplemente declare su servicio como proveedor solo en app.module.ts.

Hizo el trabajo por mí.

providers: [Topic1Service,Topic2Service,...,TopicNService],

luego instanciarlo usando un parámetro privado del constructor:

constructor(private topicService: TopicService) { }

o ya que si su servicio se usa desde html, la opción -prod reclamará:

Property ''topicService'' is private and only accessible within class ''SomeComponent''.

agregue un miembro para su servicio y llénelo con la instancia recibida en el constructor:

export class SomeComponent { topicService: TopicService; constructor(private topicService: TopicService) { this.topicService= topicService; } }


esto parece estar funcionando bien para mí

@Injectable() export class MyStaticService { static instance: MyStaticService; constructor() { return MyStaticService.instance = MyStaticService.instance || this; } }