2016-01-15 25 views
27

So come iniettare un servizio in un componente (tramite @Component), ma come posso utilizzare DI per trasferire servizi al di fuori dei componenti?Qual è il modo migliore per iniettare un servizio in un altro in angular 2 (Beta)?

In altre parole, non voglio fare questo:

export class MyFirstSvc { 

} 

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

export class MyThirdSvc { 
    constructor() { 
     this.helpfulService = new MyFirstSvc(); 
    } 
} 
+2

Leggi http: //blog.thoughtram.io/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html –

+0

questa risposta può aiutarti [angular service inject] (http://stackoverflow.com/a/36736192/4236520) – Mikki

risposta

4
  • "Fornire" i vostri servizi da qualche parte o al di sopra dove si intende utilizzarli, ad esempio, li si potrebbe mettere al root della tua applicazione usando bootstrap() se hai una sola istanza di ogni servizio (singleton).
  • Utilizzare il decoratore @Injectable() su qualsiasi servizio che dipende da un altro.
  • Iniettare gli altri servizi nel costruttore del servizio dipendente.

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

MySecondSvc.ts

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

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

Vedi Plunker per altri file.

Ciò che è un po 'strano in Service DI è che dipende ancora dai componenti. Ad esempio, MySecondSvc viene creato quando un componente lo richiede e, a seconda di dove è stato "fornito" MyFirstSvc nell'albero dei componenti, ciò può influire sull'istanza MyFirstSvc immessa in MySecondSvc. Questo è discusso di più qui: Can you only inject services into services through bootstrap?

3

Il servizio è considerato condiviso tra i componenti. Quindi diciamo che se ho un servizio, posso usarlo in diversi componenti.

Qui in questa risposta vi sto mostrando un servizio che accetta i dati da un componente e li invia ad altri componenti.

Ho usato il concetto di Routing, Shared-Service, Shared-Object. Spero che questo ti aiuterà a capire le basi del servizio di condivisione.

Nota: @ Injectable decoratore viene utilizzato per rendere il servizio iniettabile.

Answer

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 e oggetto condiviso

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; 
    } 
} 
41

Sì, la prima cosa è aggiungere il decoratore @Injectable su ciascun servizio che si desidera iniettare. In effetti, il nome Injectable è un po 'insidioso. Ciò non significa che la classe sarà "iniettabile" ma decorerà in modo tale che i parametri del costruttore possano essere iniettati. Vedi questo problema github per maggiori dettagli: https://github.com/angular/angular/issues/4404.

Ecco la mia comprensione del meccanismo di iniezione. Quando si imposta un decoratore @Injectable per una classe, Angular tenterà di creare o ottenere istanze per i tipi corrispondenti nell'iniettore per la catena di esecuzione corrente. Infatti, non esiste un solo iniettore per un'applicazione Angular2 ma un albero di iniettori. Sono implicitamente associati all'intera applicazione e ai componenti. Una caratteristica chiave a questo livello è che sono collegati tra loro in modo gerarchico. Questo albero di iniettori mappa l'albero dei componenti. Non sono definiti iniettori per "servizi".

Prendiamo un campione. Ho la seguente applicazione:

  • Componente AppComponent: la componente principale della mia applicazione che viene fornito quando si crea l'applicazione Angular2 nel bootstrap funzione di

    @Component({ 
        selector: 'my-app', 
        template: ` 
         <child></child> 
        `, 
        (...) 
        directives: [ ChildComponent ] 
    }) 
    export class AppComponent { 
    } 
    
  • Componente ChildComponent: un componente sub che sarà essere utilizzato all'interno del componente AppComponent

    @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; 
        } 
    } 
    
  • due servizi, Service1 e Service2: Service1 viene utilizzato dal ChildComponent e Service2 da 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' } 
        ]; 
        } 
    } 
    

Ecco una panoramica di tutti questi elementi e le relazioni là:

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

In tale applicazione, abbiamo tre iniettori:

  • L'iniettore applicazione che può essere configurato utilizzando il secondo parametro della funzione bootstrap
  • Il AppComponent iniettore che può essere configurato utilizzando l'attributo providers di questo componente. Può "vedere" gli elementi definiti nell'iniettore dell'applicazione.Ciò significa che se un provider non viene trovato in questo provider, verrà automaticamente cercato in questo iniettore padre. Se non viene trovato in quest'ultimo, verrà generato un errore "provider non trovato".
  • L'iniettore che seguirà le stesse regole di quello AppComponent. Per iniettare gli elementi coinvolti nella catena di iniezione eseguita per il componente, i provider verranno cercati prima in questo iniettore, quindi nello AppComponent e infine nell'applicazione uno.

Ciò significa che quando si cerca di iniettare il Service1 al costruttore ChildComponent, Angular2 esaminerà la ChildComponent iniettore, poi nel AppComponent uno e infine nell'applicazione uno.

Da Service2 deve essere iniettato in Service1, la stessa elaborazione risoluzione avverrà: ChildComponent iniettore, AppComponent uno e uno applicazione.

Ciò significa che sia Service1 e Service2 può essere specificato ad ogni livello in base alle proprie esigenze utilizzando l'attributo providers per i componenti e il secondo parametro della funzione bootstrap per l'iniettore applicazione.

Questo permette di condividere le istanze di dipendenze per una serie di elementi:

  • Se si definisce un fornitore a livello di applicazione, il correspoding creato istanza verrà condivisa da tutta l'applicazione (tutti i componenti, tutti i servizi , ...).
  • Se si definisce un provider a livello di componente, l'istanza sarà condivisa dal componente stesso, dai relativi componenti secondari e da tutti i "servizi" coinvolti nella catena di dipendenze.

Quindi è molto potente e tu sei libero di organizzarti come vuoi e per le tue esigenze.

Ecco il plunkr corrispondente in modo da poter giocare con esso: https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview.

Questo collegamento dalla documentazione Angular2 può essere d'aiuto: https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html.

spero che voi (e scusate la lunga risposta) aiuta, Thierry

+2

Ho sentito che questa è un'ottima risposta! Ero un po 'confuso riguardo a una frase che hai detto: "Il nome iniettabile è un po' insidioso, non significa che la classe sarà" iniettabile "ma decorerà in modo tale che i parametri del costruttore possano essere iniettati" .. Poiché, nel tuo Service1 stava cercando di iniettare Service2, quindi è necessario avere il @injectable che decora il tuo servizio1, quindi il tuo servizio2 può essere iniettato (ho rimosso il decoratore iniettabile dal servizio1, quindi il codice non funzionerà). Ho ragione? Volevo solo confermare. Grazie :-) –

+3

@GeorgeHuang, sì, '@Injectectable()' è necessario se un servizio dipende da un altro servizio. –

+1

@thierry cosa succede se vogliamo utilizzare il componente comune in tutti gli altri componenti, cioè come fornire un componente comune a tutti gli altri attraverso l'intera app? –

1

In qualche modo @Injectable non funziona per me in Angular 2.0.0-beta.17 durante il cablaggio Componenta -> ServiceB -> ServiceC.

ho preso questo approccio:

  1. di riferimento di tutti i servizi nel @ di Componenta campo fornitori.
  2. In ServiceB utilizzare l'annotazione @Inject nel costruttore per collegare ServiceC.

Run this Plunker per vedere un esempio di codice o vista sotto

app.ts

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

bootstrap(AppComponent); 

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

panoramica di 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(); 
    } 
} 

proprietà-service.ts

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

    logHi(){ 
     console.log("hi"); 
    } 
} 
+0

L'uso di '@Inject (...)' è ridondante se il tipo del parametro del costruttore è uguale a quello passato a '@Inject (...)' e la classe ha il '@Injectable()' (con '()') annotazione. –

+0

Ricevo "Impossibile risolvere tutti i parametri per OverviewService (?)" Quando provo. Controlla https://plnkr.co/edit/g924s5KQ0wJW83Qiwu0e?p=preview – Julius

0

La prima cosa da fare è quello di annotare tutti i servizi con la @Injectable annotazione. Notare le parentesi alla fine dell'annotazione, senza questa soluzione non funzionerà.

Una volta fatto questo, possiamo poi iniettare servizi in a vicenda iniezione costruttore utilizza:

@Injectable() 
export class MyFirstSvc { 

} 

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

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

non è sicuro se la risposta è ancora necessaria quindi vorrei andare avanti e cercare di rispondere a questa.

Si consideri il seguente esempio in cui abbiamo un componente che utilizza un servizio per popolare alcuni valori nel suo modello come qui di seguito

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

Il codice di cui sopra è abbastanza semplice e cercheremo di recuperare qualsiasi cosa getPersons restituisca dal DataService. Il file DataService è disponibile sotto.

data.service.ts

export class DataService { 

persons:<Array>; 

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

getPersons(){ 

return this.persons 

} 

Il pezzo di codice di cui sopra funzionerà perfettamente bene senza l'uso del decoratore @Injectable. Ma il problema inizierà quando il nostro servizio (DataService in questo caso) richiede alcune dipendenze come ad es. Http. se cambiamo il nostro file data.service.ts come di seguito avremo un errore che dice 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 

} 

Questo ha qualcosa a che fare con il modo in cui funzionano in decoratori angolare 2. Si prega di leggere https://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html per ottenere una profonda conoscenza di questo problema.

Anche il codice sopra riportato non funzionerà in quanto dobbiamo importare HTTP nel nostro modulo bootstrap.

Ma una regola del pollice che posso suggerire è che se il tuo file di servizio ha bisogno di una dipendenza, allora dovresti decorare quella classe con un decoratore @Indectable.

di riferimento: https://blog.thoughtram.io/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html

0

prima cosa è necessario per fornire il servizio

Si potrebbe fornire sia nel metodo bootstrap:

bootstrap(AppComponent,[MyFirstSvc]); 

o sul componente app, o in qualsiasi altra componente, a seconda delle esigenze .:

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

quindi semplicemente iniettare il servizio utilizzando il costruttore:

export class MySecondSvc { 
     constructor(private myFirstSvc : MyFirstSvc){} 
} 
Problemi correlati