J’ai lu que l’injection lors du démarrage devrait faire en sorte que tous les enfants partagent la même instance, mais mes composants principaux et d’en-tête (l’application principale inclut un composant d’en-tête et un routeur) reçoivent chacun une instance distincte de mes services.
J’ai un FacebookService que j’utilise pour passer des appels sur l’API javascript facebook et sur un UserService qui utilise le service Facebook. Voici mon bootstrap:
bootstrap(MainAppComponent, [ROUTER_PROVIDERS, UserService, FacebookService]);
D’après mon enregistrement, il semble que l’appel bootstrap se termine, puis je vois le service Facebook, puis UserService créé avant l’exécution du code dans chacun des constructeurs, le MainAppComponent, le HeaderComponent et le DefaultComponent:
Jason a tout à fait raison! Il est causé par le fonctionnement de l’dependency injection. C’est basé sur des injecteurs hiérarchiques.
Il y a plusieurs injecteurs dans une application Angular2:
Lorsque Angular2 tente d’injecter quelque chose dans le constructeur du composant:
Donc, si vous voulez avoir un singleton pour toute l’application, vous devez avoir défini le fournisseur au niveau de l’injecteur racine ou de l’injecteur du composant d’application.
Mais Angular2 examinera l’arbre d’injection depuis le bas. Cela signifie que le fournisseur au niveau le plus bas sera utilisé et l’étendue de l’instance associée sera ce niveau.
Voir cette question pour plus de détails:
La méthode recommandée pour créer un service singleton a été modifiée. Il est maintenant recommandé de spécifier dans le décorateur @Injectable
du service qu’il doit être fourni dans la racine. Cela fait beaucoup de sens pour moi et il n’est plus nécessaire de lister tous les services fournis dans vos modules. Vous importez simplement les services lorsque vous en avez besoin et ils s’enregistrent au bon endroit. Vous pouvez également spécifier un module afin qu’il ne soit fourni que si le module est importé.
@Injectable({ providedIn: 'root', }) export class ApiService { }
Avec NgModule, je pense qu’il faut créer un «CoreModule» avec votre classe de service et répertorier le service dans les fournisseurs du module. Ensuite, vous importez le module de base dans votre module d’application principal qui fournira une instance à tous les enfants demandant cette classe dans leurs constructeurs:
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 { }
Si vous répertoriez un fournisseur dans bootstrap()
, vous n’avez pas besoin de les répertorier dans votre décorateur de composants:
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) {} }
En fait, la liste de votre classe dans ‘providers’ en crée une nouvelle, si un composant parent le liste déjà, les enfants n’en ont pas besoin et s’ils le font, ils obtiendront une nouvelle instance.
Je sais qu’angular a des injecteurs hiérarchiques comme Thierry l’a dit.
Mais j’ai une autre option ici si vous trouvez un cas d’utilisation où vous ne voulez pas vraiment l’injecter chez le parent.
Nous pouvons y parvenir en créant une instance du service, et à condition de toujours la renvoyer.
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); } }) ];
Et puis, sur votre composant, vous utilisez votre méthode de fourniture personnalisée.
@Component({ providers: [YOUR_SERVICE_PROVIDER] })
Et vous devriez avoir un service unique sans dépendre des injecteurs hiérarchiques.
Je ne dis pas que c’est une meilleure façon, c’est juste au cas où quelqu’un aurait un problème où les injecteurs hiérarchiques ne sont pas possibles.
La syntaxe a été modifiée. Vérifiez ce lien
Les dépendances sont des singletons dans le cadre d’un injecteur. Dans l’exemple ci-dessous, une seule instance HeroService est partagée entre HeroesComponent et ses enfants HeroListComponent.
Étape 1. Créez une classe singleton avec le décorateur @Injectable
@Injectable() export class HeroService { getHeroes() { return HEROES; } }
Etape 2. Injecter dans le constructeur
export class HeroListComponent { constructor(heroService: HeroService) { this.heroes = heroService.getHeroes(); }
Étape 3. Enregistrer le fournisseur
@NgModule({ imports: [ BrowserModule, FormsModule, routing, HttpModule, JsonpModule ], declarations: [ AppComponent, HeroesComponent, routedComponents ], providers: [ HeroService ], bootstrap: [ AppComponent ] }) export class AppModule { }
L’ @Injectable
décorateur @Injectable
au service ET son enregistrement en tant que fournisseur dans le module racine en fera un singleton.
Voici un exemple de travail avec la version angular 2.3. Appelez simplement le constructeur du service de la même manière que ce constructeur (private _userService: UserService). Et il va créer un singleton pour l’application.
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 sortingggered"); }); } ... }
cela semble bien fonctionner pour moi
@Injectable() export class MyStaticService { static instance: MyStaticService; constructor() { return MyStaticService.instance = MyStaticService.instance || this; } }
Vous pouvez utiliser useValue
dans les fournisseurs
import { MyService } from './my.service'; @NgModule({ ... providers: [ { provide: MyService, useValue: new MyService() } ], ... })
De Angular @ 6, vous pouvez avoir providedIn
Injectable
in a Injectable
.
@Injectable({ providedIn: 'root' }) export class UserService { }
Vérifiez les documents ici
Il y a deux façons de rendre un service un singleton dans Angular:
- Déclarez que le service doit être fourni dans la racine de l’application.
- Incluez le service dans AppModule ou dans un module uniquement importé par AppModule.
À partir de Angular 6.0, la méthode préférée pour créer un service singleton consiste à spécifier sur le service qu’il doit être fourni dans la racine de l’application. Cela se fait en paramétrant providedIn à root sur le décorateur @Injectable du service:
Déclarez simplement votre service en tant que fournisseur dans app.module.ts uniquement.
Il a fait le travail pour moi.
providers: [Topic1Service,Topic2Service,...,TopicNService],
puis instanciez-le en utilisant un paramètre privé constructeur:
constructor(private topicService: TopicService) { }
ou puisque si votre service est utilisé à partir de HTML, l’option -prod réclamera:
Property 'topicService' is private and only accessible within class 'SomeComponent'.
ajoutez un membre pour votre service et remplissez-le avec l’instance reçue dans le constructeur:
export class SomeComponent { topicService: TopicService; constructor(private topicService: TopicService) { this.topicService= topicService; } }
Si vous souhaitez rendre le service singleton au niveau de l’application, vous devez le définir dans app.module.ts
fournisseurs: [MyApplicationService] (vous pouvez également définir le même dans le module enfant pour le rendre spécifique à ce module)
Si vous souhaitez définir le service singleton au niveau du service de création de composant, ajoutez ce service dans app.module.ts et ajoutez le tableau de fournisseurs dans un composant spécifique, comme indiqué dans le snipet ci-dessous.
@Component ({selector: ‘app-root’, templateUrl: ‘./test.component.html’, styleUrls: [‘./test.component.scss’], fournisseurs: [TestMyService]})
Angular 6 fournit un nouveau moyen d’append un service au niveau de l’application. Au lieu d’append une classe de service au tableau providers [] dans AppModule, vous pouvez définir la configuration suivante dans @Injectable ():
@Injectable ({providedIn: ‘root’}) export classe MyService {…}
La “nouvelle syntaxe” offre cependant un avantage: les services peuvent être chargés par Angular (en coulisse) et le code redondant peut être supprimé automatiquement. Cela peut conduire à une meilleure performance et à une vitesse de chargement plus élevée, même si cela ne donne que des applications et des services plus importants en général.