Come posso creare un servizio Singleton in Angular 2?

Ho letto che l’iniezione quando bootstrap dovrebbe avere tutti i bambini condividono la stessa istanza, ma i miei componenti principali e di intestazione (l’app principale include il componente di intestazione e il router-outlet) ottengono ciascuno un’istanza separata dei miei servizi.

Ho un servizio di Facebook che uso per effettuare chiamate su Facebook javascript api e un UserService che utilizza FacebookService. Ecco il mio bootstrap:

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

Dalla mia registrazione sembra che la chiamata bootstrap sia terminata, quindi vedo che FacebookService e UserService vengono creati prima del codice in ognuno dei costruttori, MainAppComponent, HeaderComponent e DefaultComponent:

inserisci la descrizione dell'immagine qui

Jason ha completamente ragione! È causato dal modo in cui funziona l’iniezione di dipendenza. È basato su iniettori gerarchici.

Esistono diversi iniettori all’interno di un’applicazione Angular2:

  • Il root che si configura quando si avvia l’applicazione
  • Un iniettore per componente. Se usi un componente all’interno di un altro. L’iniettore componente è un figlio del componente genitore. Il componente dell’applicazione (quello specificato durante il boating dell’applicazione) ha l’iniettore root come genitore).

Quando Angular2 tenta di iniettare qualcosa nel costruttore del componente:

  • Esamina l’iniettore associato al componente. Se ne trova uno corrispondente, lo userà per ottenere l’istanza corrispondente. Questa istanza viene creata pigramente ed è un singleton per questo iniettore.
  • Se non ci sono provider a questo livello, guarderà l’iniettore genitore (e così via).

Quindi, se si desidera avere un singleton per l’intera applicazione, è necessario che il provider sia definito a livello dell’iniettore root o dell’iniettore del componente dell’applicazione.

Ma Angular2 guarderà l’albero dell’iniettore dal basso. Ciò significa che verrà utilizzato il provider al livello più basso e l’ambito dell’istanza associata sarà questo livello.

Vedi questa domanda per maggiori dettagli:

  • Qual è il modo migliore per iniettare un servizio in un altro in angular 2 (Beta)?

Aggiornamento (Angolare 6)

Il modo consigliato per creare un servizio singleton è cambiato. Si consiglia ora di specificare nel decoratore @Injectable sul servizio che dovrebbe essere fornito nella ‘radice’. Questo ha molto senso per me e non c’è più bisogno di elencare tutti i servizi forniti nei tuoi moduli. Importi semplicemente i servizi quando ne hai bisogno e si registrano nel posto giusto. È inoltre ansible specificare un modulo in modo che venga fornito solo se il modulo viene importato.

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

Aggiornamento (Angolare 2)

Con NgModule, il modo per farlo ora è creare un ‘CoreModule’ con la class di servizio al suo interno ed elencare il servizio nei provider del modulo. Quindi importa il modulo principale nel modulo dell’app principale che fornirà l’istanza a tutti i bambini che richiedono quella class nei loro costruttori:

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

Risposta originale

Se si elenca un provider in bootstrap() , non è necessario elencarli nel decoratore del componente:

 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 effetti, l’elenco della class in “provider” crea una nuova istanza di esso, se un componente padre già lo elenca, quindi i bambini non ne hanno bisogno, e se lo fanno riceveranno una nuova istanza.

So che l’angular ha iniettori gerarchici come ha detto Thierry.

Ma ho un’altra opzione qui nel caso in cui trovi un caso d’uso in cui non vuoi realmente iniettarlo sul genitore.

Possiamo ottenere ciò creando un’istanza del servizio e restituendola sempre.

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

E poi sul tuo componente usi il tuo metodo di fornitura personalizzato.

 @Component({ providers: [YOUR_SERVICE_PROVIDER] }) 

E dovresti avere un servizio singleton senza dipendere dagli iniettori gerarchici.

Non sto dicendo che questo è un modo migliore, è nel caso in cui qualcuno ha un problema in cui gli iniettori gerarchici non sono possibili.

La syntax è stata cambiata. Controlla questo link

Le dipendenze sono singleton nell’ambito di un iniettore. Nell’esempio sottostante, una singola istanza di HeroService è condivisa tra HeroesComponent e i suoi figli HeroListComponent.

Passaggio 1. Creare una class singleton con il decoratore @Injectable

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

Passaggio 2. Iniezione nel costruttore

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

Passaggio 3. Registrazione provider

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

Aggiungendo @Injectable decoratore @Injectable al Servizio, E registrandolo come un fornitore nel Modulo di Root diventerà un singleton.

Ecco un esempio funzionante con Angular versione 2.3. Basta chiamare il costruttore del servizio il modo stand come questo costruttore (_userService privato: UserService). E creerà un singleton per l’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(); 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"); }); } ... } 

questo sembra funzionare bene per me

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

È ansible utilizzare useValue nei provider

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

Da Angular @ 6, puoi aver Injectable in un Injectable .

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

Controlla i documenti qui

Esistono due modi per rendere un servizio un singleton in Angular:

  1. Dichiarare che il servizio dovrebbe essere fornito nella root dell’applicazione.
  2. Includere il servizio in AppModule o in un modulo importato solo da AppModule.

A partire da Angular 6.0, il modo preferito per creare un servizio singleton è specificare sul servizio che dovrebbe essere fornito nella root dell’applicazione. Questo viene fatto impostando providedIn come root sul decoratore @Injectable del servizio:

Dichiara solo il tuo servizio come fornitore solo in app.module.ts.

Ha fatto il lavoro per me.

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

quindi installarlo utilizzando un parametro privato del costruttore:

 constructor(private topicService: TopicService) { } 

o dal momento che se il tuo servizio viene utilizzato da html, l’opzione -prod chiederà:

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

aggiungi un membro per il tuo servizio e compilalo con l’istanza ricevuta nel costruttore:

 export class SomeComponent { topicService: TopicService; constructor(private topicService: TopicService) { this.topicService= topicService; } } 
  1. Se si desidera rendere singleton di servizio a livello di applicazione, è necessario definirlo in app.module.ts

    provider: [MyApplicationService] (puoi definire lo stesso anche nel modulo figlio per renderlo specifico del modulo)

    • Non aggiungere questo servizio nel provider che crea un’istanza per quel componente che interrompe il concetto di singleton, semplicemente iniettare tramite il costruttore.
  2. Se si desidera definire il servizio Singleton a livello di componente, creare il servizio, aggiungere tale servizio in app.module.ts e aggiungere l’array di provider all’interno di un componente specifico come mostrato in sotto snipet.

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

  3. Angular 6 fornisce un nuovo modo per aggiungere il servizio a livello di applicazione. Invece di aggiungere una class di servizio all’array provider [] in AppModule, puoi impostare la seguente configurazione in @Injectable ():

    @Injectable ({providedIn: ‘root’}) export class MyService {…}

La “nuova syntax” offre però un vantaggio: i servizi possono essere caricati pigramente da Angular (dietro le quinte) e il codice ridondante può essere rimosso automaticamente. Ciò può portare a prestazioni e velocità di caricamento migliori, anche se in realtà questo è solo un problema per servizi e app più grandi in generale.