Quelle est la meilleure façon d'injecter un service dans un autre dans angular 2 (Beta)?

je sais comment injecter un service dans un composant (via @Component), mais comment utiliser L'ai pour faire circuler des services en dehors des composants?

en d'autres termes, Je ne veux pas faire cela:

export class MyFirstSvc {

}

export class MySecondSvc {
    constructor() {
        this.helpfulService = new MyFirstSvc();
    }
}

export class MyThirdSvc {
    constructor() {
        this.helpfulService = new MyFirstSvc();
    }
}
37
demandé sur Antikhippe 2016-01-15 07:14:35

7 réponses

Oui, la première chose est d'ajouter le décorateur @Injectable sur chaque service que vous voulez injecter. En fait, le nom Injectable est un peu insidieux. Cela ne signifie pas que la classe sera "injectable" mais il ornera donc les paramètres du constructeur peut être injecté. Voir ce numéro de github pour plus de détails: https://github.com/angular/angular/issues/4404 .

voici ma compréhension du mécanisme d'injection. Lors de la configuration d'un @Injectable décorateur pour une classe, angulaire va essayer de créer ou d'obtenir des instances pour les types correspondants dans l'injecteur pour la chaîne d'exécution courante. En fait, il n'y a pas qu'un seul injecteur pour une application Angular2, mais un arbre d'injecteurs. Ils sont implicitement associés à l'ensemble de l'application et des composants. Une caractéristique clé à ce niveau est qu'ils sont liés d'une manière hiérarchique. Cet arbre d'injecteurs cartographie l'arbre des composants. Aucun injecteur n'est défini pour les "services".

prenons un échantillon. J'ai l'application suivante:

  • Component AppComponent : le principal composant de mon application qui est fourni lors de la création de L'application Angular2 dans la bootstrap fonction

    @Component({
      selector: 'my-app', 
        template: `
          <child></child>
        `,
        (...)
        directives: [ ChildComponent ]
    })
    export class AppComponent {
    }
    
  • Composant ChildComponent : un sous-élément qui sera utilisé au sein de la AppComponent composant

    @Component({
        selector: 'child', 
        template: `
          {{data | json}}<br/>
          <a href="#" (click)="getData()">Get data</a>
        `,
        (...)
    })
    export class ChildComponent {
      constructor(service1:Service1) {
        this.service1 = service1;
      }
    
      getData() {
        this.data = this.service1.getData();
          return false; 
      }
    }
    
  • deux services, Service1 et Service2 : Service1 est utilisé par ChildComponent et Service2 par Service1

    @Injectable()
    export class Service1 {
      constructor(service2:Service2) {
        this.service2 = service2;
      }
    
      getData() {
        return this.service2.getData();
      }
    }
    

    @Injectable()
    export class Service2 {
    
      getData() {
        return [
          { message: 'message1' },
          { message: 'message2' }
        ];
      }
    }
    

voici un aperçu de tous ces éléments et de leurs relations:

Application
     |
AppComponent
     |
ChildComponent
  getData()     --- Service1 --- Service2

dans une telle application, nous avons trois injecteurs:

  • l'injecteur d'application qui peut être configuré en utilisant le second paramètre de la bootstrap fonction
  • l'injecteur AppComponent qui peut être configuré en utilisant l'attribut providers de ce composant. Il peut "voir" les éléments définis dans l'injecteur d'application. Cela signifie que si un fournisseur n'est pas trouvé dans ce fournisseur, il sera automatiquement rechercher dans cet injecteur parent. Si ce n'est pas le cas, une erreur "provider not found" sera générée.
  • le ChildComponent injecteur qui suivra les mêmes règles que le AppComponent . Pour injecter les éléments impliqués dans la chaîne d'injection exécutée pour le composant, les fournisseurs seront recherchés d'abord dans cet injecteur, puis dans le AppComponent et enfin dans l'application.

cela signifie qu'en essayant d'injecter le Service1 dans le ChildComponent constructeur, Angular2 regardera dans le ChildComponent injecteur, puis dans le AppComponent l'un et enfin dans l'application.

depuis Service2 doit être injecté dans Service1 , le même traitement de résolution sera fait: ChildComponent injecteur, AppComponent un et une application.

cela signifie que les deux Service1 et Service2 peuvent être spécifiés à chaque niveau en fonction de vos besoins en utilisant l'attribut providers pour les composants et le deuxième paramètre de la fonction bootstrap pour le application de l'injecteur.

cela permet de partager des instances de dépendances pour un ensemble d'éléments:

  • si vous définissez un fournisseur au niveau de l'application, l'instance correspondante créée sera partagée par l'ensemble de l'application (tous les composants, tous les services, ...).
  • Si vous définissez un fournisseur au niveau du composant, l'instance sera partagée par le composant lui-même, de ses sous composants et tous les "services" impliqués dans la chaîne de dépendances.

donc c'est très puissant et vous êtes libre d'organiser comme vous voulez et pour vos besoins.

voici le plunkr correspondant pour que vous puissiez jouer avec: https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview .

ce lien de la documentation Angular2 pourrait vous aider: https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html .

espère qu'il vous aide (et désolé la longue réponse), Thierry

46
répondu Thierry Templier 2016-01-15 08:56:57
  • "Fournir" vos services, quelque part au-dessus de l'endroit où vous comptez l'utiliser, par exemple, vous pouvez les placer à la racine de votre application à l'aide de bootstrap() si vous avez seulement une fois instance de chaque service (singletons).
  • utilisez le @Injectable() décorateur sur tout service qui dépend d'un autre.
  • injecter les autres services dans le constructeur du service dépendant.

boot.ts

import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from './app.component';
import {MyFirstSvc} from '../services/MyFirstSvc';
import {MySecondSvc} from '../services/MySecondSvc';

bootstrap(AppComponent, [MyFirstSvc, MySecondSvc]);

MYS Secondsvc.ts

import {Injectable} from 'angular2/core';
import {MyFirstSvc} from '../services/MyFirstSvc';

@Injectable()
export class MySecondSvc {
  constructor(private _firstSvc:MyFirstSvc) {}
  getValue() {
    return this._firstSvc.value;
  }
}

voir Plunker pour les autres fichiers.

ce qui est un peu bizarre à propos du Service DI, c'est qu'il dépend encore de composants. Par exemple, MySecondSvc est créé lorsqu'un composant le demande, et selon l'endroit où MyFirstSvc a été "fourni" dans l'arbre des composants, cela peut affecter l'instance MyFirstSvc injectée dans MySecondSvc . Ceci est discuté plus ici: pouvez-vous seulement injecter des services dans les services par bootstrap?

5
répondu Mark Rajcok 2017-05-23 12:26:34
Le Service

est considéré comme étant partagé entre les composantes. Alors disons que si j'ai un service, je peux l'utiliser dans différents composants.

ici dans cette réponse je vous montre un service qui accepte les données d'un composant et envoie ces données à un autre composant.

j'ai utilisé le concept de routage, de service partagé, D'objet partagé. J'espère que cela vous aidera à comprendre les bases du service par actions.

Note: @Injectable Décorateur est utilisé pour rendre le service injectable.

réponse

Boot.ts

import {Component,bind} from 'angular2/core';

import {bootstrap} from 'angular2/platform/browser';

import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';

import {SharedService} from 'src/sharedService';

import {ComponentFirst} from 'src/cone';
import {ComponentTwo} from 'src/ctwo';


@Component({
  selector: 'my-app',
  directives: [ROUTER_DIRECTIVES],
  template: `
    <h1>
      Home
    </h1> 

    <router-outlet></router-outlet>
      `,

})

@RouteConfig([
  {path:'/component-first', name: 'ComponentFirst', component: ComponentFirst}
  {path:'/component-two', name: 'ComponentTwo', component: ComponentTwo}

])

export class AppComponent implements OnInit {

  constructor(router:Router)
  {
    this.router=router;
  }

    ngOnInit() {
    console.log('ngOnInit'); 
    this.router.navigate(['/ComponentFirst']);
  }



}

    bootstrap(AppComponent, [SharedService,
    ROUTER_PROVIDERS,bind(APP_BASE_HREF).toValue(location.pathname)
    ]);

FirstComponent

import {Component,View,bind} from 'angular2/core';
import {SharedService} from 'src/sharedService';
import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';
@Component({
  //selector: 'f',
  template: `
    <div><input #myVal type="text" >
    <button (click)="send(myVal.value)">Send</button>
      `,

})

export class ComponentFirst   {

  constructor(service:SharedService,router:Router){
    this.service=service;
    this.router=router;
  }

  send(str){
    console.log(str);
    this.service.saveData(str); 
    console.log('str');
    this.router.navigate(['/ComponentTwo']);
  }

}

SecondComponent

import {Component,View,bind} from 'angular2/core';
import {SharedService} from 'src/sharedService';
import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';
@Component({
  //selector: 'f',
  template: `
    <h1>{{myName}}</h1>
    <button (click)="back()">Back<button>
      `,

})

export class ComponentTwo   {

  constructor(router:Router,service:SharedService)
  {
    this.router=router;
    this.service=service;
    console.log('cone called');
    this.myName=service.getData();
  }
  back()
  {
     console.log('Back called');
    this.router.navigate(['/ComponentFirst']);
  }

}

SharedService et Objet partagé

import {Component, Injectable,Input,Output,EventEmitter} from 'angular2/core'

// Name Service
export interface myData {
   name:string;
}



@Injectable()
export class SharedService {
  sharingData: myData={name:"nyks"};
  saveData(str){
    console.log('save data function called' + str + this.sharingData.name);
    this.sharingData.name=str; 
  }
  getData:string()
  {
    console.log('get data function called');
    return this.sharingData.name;
  }
} 
4
répondu micronyks 2016-01-15 08:55:40

D'une façon ou D'une autre @Injectable ne fonctionne pas pour moi en angle 2.0.0-beta.17 lors du câblage ComponentA -> ServiceB -> ServiceC.

j'ai pris cette approche:

  1. référence tous les services dans le champ fournisseurs de @ComponentA.
  2. dans ServiceB utilisez l'annotation @Inject dans le constructeur pour raccorder ServiceC.

Exécuter ce Plunker voir un exemple ou un code de vue ci-dessous

app.ts

@Component({selector: 'my-app',
    template: `Hello! This is my app <br/><br/><overview></overview>`,
    directives: [OverviewComponent]
})
class AppComponent {}

bootstrap(AppComponent);

vue d'ensemble.ts

import {Component, bind} from 'angular2/core';
import {OverviewService} from "../services/overview-service";
import {PropertiesService} from "../services/properties-service";

@Component({
    selector: 'overview',
    template: `Overview listing here!`,
    providers:[OverviewService, PropertiesService] // Include BOTH services!
})

export default class OverviewComponent {

    private propertiesService : OverviewService;

    constructor( overviewService: OverviewService) {
        this.propertiesService = overviewService;
        overviewService.logHello();
    }
}

vue d'ensemble-service.ts

import {PropertiesService} from "./properties-service";
import {Inject} from 'angular2/core';

export class OverviewService {

    private propertiesService:PropertiesService;

    // Using @Inject in constructor
    constructor(@Inject(PropertiesService) propertiesService:PropertiesService){
        this.propertiesService = propertiesService;
    }

    logHello(){
        console.log("hello");
        this.propertiesService.logHi();
    }
}

propriétés-service.ts

// Using @Injectable here doesn't make a difference
export class PropertiesService {

    logHi(){
        console.log("hi");
    }
}
2
répondu Julius 2016-05-22 14:47:16

Je ne suis pas sûr qu'une réponse soit toujours requise, alors j'essaierai d'y répondre.

prenons l'exemple suivant, où nous avons un Composant qui utilise un service pour remplir certaines valeurs dans son modèle comme ci-dessous

testComponent.component.ts

import { Component } from "@angular/core"
import { DataService } from "./data.service"
@Component({
    selector:"test-component",
    template:`<ul>
             <li *ngFor="let person of persons">{{ person.name }}</li>
             </ul>
})

export class TestComponent {
  persons:<Array>;
  constructor(private _dataService:DataService){
    this.persons = this._dataService.getPersons()
  }
}

le code ci-dessus est assez simple et il va essayer de récupérer n'importe quel retour getPersons du service de données. Le fichier DataService est disponible ci-dessous.

data.service.ts

export class DataService {

persons:<Array>;

constructor(){
    this.persons = [
      {name: "Apoorv"},
      {name: "Bryce"},
      {name: "Steve"}
    ]
}

getPersons(){

return this.persons

}

le morceau de code ci-dessus fonctionnera parfaitement bien sans l'utilisation du décorateur @Injectable. Mais le problème commencera quand notre service (DataService dans ce cas) exigera certaines dépendances comme par exemple: Http. si nous changeons notre fichier data.service.ts comme ci-dessous, nous obtiendrons une erreur disant Cannot resolve all parameters for DataService(?). Make sure they all have valid type or annotations.

import { Http } from '@angular/http';
export class DataService {

persons:<Array>;

constructor(){
    this.persons = [
      {name: "Apoorv"},
      {name: "Bryce"},
      {name: "Steve"}
    ]
}

getPersons(){

return this.persons

}

cela a quelque chose à voir avec la façon dont les décorateurs fonctionnent en angle 2. Veuillez lire https://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html pour avoir une compréhension approfondie de cette question.

le code ci-dessus ne fonctionnera pas non plus car nous devons aussi importer HTTP dans notre module bootstrap.

mais une règle empirique que je peux suggérer est que si votre dossier de service a besoin d'une dépendance alors vous devriez décorer cette classe avec un décorateur @Injectable.

référence: https://blog.thoughtram.io/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html

2
répondu Apoorv 2017-03-21 08:25:57

la première chose à faire est d'annoter tous les services avec l'annotation @Injectable . Notez les parenthèses à la fin de l'annotation, sans cela cette solution ne fonctionnera pas.

une fois que cela est fait, nous pouvons alors injecter des services dans l'autre en utilisant l'injection du constructeur:

@Injectable()
export class MyFirstSvc {

}

@Injectable()
export class MySecondSvc {
    constructor(helpfulService: MyFirstSvc) {        
    }
}

@Injectable()
export class MyThirdSvc {
    constructor(helpfulService: MyFirstSvc) {        
    }
}
0
répondu Angular University 2016-08-21 19:53:42

vous devez D'abord fournir votre service

vous pouvez le Fournir soit dans la méthode bootstrap:

bootstrap(AppComponent,[MyFirstSvc]);

ou le sur le composant app, ou dans tout autre composant, selon vos besoins.:

@Component({
    ...
      providers:[MyFirstSvc]
}
...

alors il suffit de vous injecter le service en utilisant le constructeur:

export class MySecondSvc {
      constructor(private myFirstSvc : MyFirstSvc ){}
}
0
répondu bougsid 2017-04-05 10:07:34