webentwicklung-frage-antwort-db.com.de

Was ist der beste Weg, um einen Service in Winkel 2 (Beta) in einen anderen zu injizieren?

Ich weiß, wie ein Service in eine Komponente eingefügt wird (über @Component), aber wie kann ich DI verwenden, um Services außerhalb von Komponenten zu übergeben?

Mit anderen Worten, ich möchte das nicht tun:

export class MyFirstSvc {

}

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

export class MyThirdSvc {
    constructor() {
        this.helpfulService = new MyFirstSvc();
    }
}
39
Bryce Johnson

Ja, als Erstes fügen Sie den @Injectable-Dekorator zu allen Diensten hinzu, die Sie einfügen möchten. Tatsächlich ist der Injectable-Name etwas heimtückisch. Das bedeutet nicht, dass die Klasse "injizierbar" ist, aber sie wird so dekoriert, dass die Konstruktorparameter injiziert werden können. Weitere Informationen finden Sie in dieser Github-Ausgabe: https://github.com/angular/angular/issues/4404 .

Hier ist mein Verständnis des Einspritzmechanismus. Beim Festlegen eines @Injectable-Dekorators für eine Klasse versucht Angular, Instanzen für entsprechende Typen im Injektor für die aktuelle Ausführungskette zu erstellen oder abzurufen. Tatsächlich gibt es nicht nur einen Injektor für eine Angular2-Anwendung, sondern einen Baum von Injektoren. Sie sind implizit der gesamten Anwendung und den Komponenten zugeordnet. Ein Hauptmerkmal auf dieser Ebene ist, dass sie hierarchisch miteinander verbunden sind. Dieser Baum der Injektoren bildet den Baum der Komponenten ab. Für "Dienste" sind keine Injektoren definiert.

Nehmen wir eine Probe. Ich habe folgende Anwendung:

  • Komponente AppComponent: Die Hauptkomponente meiner Anwendung, die beim Erstellen der Anwendung Angular2 in der bootstrap-Funktion bereitgestellt wird

    @Component({
      selector: 'my-app', 
        template: `
          <child></child>
        `,
        (...)
        directives: [ ChildComponent ]
    })
    export class AppComponent {
    }
    
  • Komponente ChildComponent: eine Unterkomponente, die in der AppComponent-Komponente verwendet wird

    @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; 
      }
    }
    
  • Zwei Dienste, Service1 und Service2: Service1 werden von ChildComponent und Service2 von Service1 verwendet.

    @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' }
        ];
      }
    }
    

Hier eine Übersicht über alle diese Elemente und deren Beziehungen:

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

In einer solchen Anwendung haben wir drei Injektoren:

  • Der Anwendungsinjektor, der mit dem zweiten Parameter der bootstrap-Funktion konfiguriert werden kann
  • Der AppComponent-Injektor, der mit dem providers-Attribut dieser Komponente konfiguriert werden kann. Er kann im Applikationsinjektor definierte Elemente "sehen". Das heißt, wenn kein Anbieter in diesem Anbieter gefunden wird, wird automatisch nach diesem übergeordneten Injektor gesucht. Wird er nicht gefunden, wird der Fehler "Provider nicht gefunden" ausgegeben.
  • Der ChildComponent-Injektor, der denselben Regeln folgt wie der AppComponent-Injektor. Um die an der für die Komponente ausgeführten Einspritzkette beteiligten Elemente einzuspritzen, wird in diesem Einspritzventil zuerst nach Anbietern gesucht, dann in der AppComponent-Instanz und schließlich in der Anwendung.

Das bedeutet, dass Angular2 beim Versuch, den Service1 in den ChildComponent-Konstruktor zu injizieren, in den ChildComponent-Injektor, dann in den AppComponent-Injektor und schließlich in die Anwendung schauen wird.

Da Service2 in Service1 injiziert werden muss, wird dieselbe Auflösungsverarbeitung durchgeführt: ChildComponent Injektor, AppComponent und Anwendung eins.

Dies bedeutet, dass sowohl Service1 als auch Service2 auf jeder Ebene entsprechend Ihren Anforderungen festgelegt werden können, indem das providers-Attribut für Komponenten und der zweite Parameter der bootstrap-Funktion für den Anwendungsinjektor verwendet wird.

Dadurch können Instanzen von Abhängigkeiten für eine Gruppe von Elementen gemeinsam genutzt werden:

  • Wenn Sie einen Provider auf Anwendungsebene definieren, wird die entsprechend erstellte Instanz von der gesamten Anwendung (allen Komponenten, allen Diensten usw.) gemeinsam genutzt.
  • Wenn Sie einen Anbieter auf Komponentenebene definieren, wird die Instanz von der Komponente selbst, ihren Unterkomponenten und allen an der Abhängigkeitskette beteiligten "Diensten" gemeinsam genutzt.

Es ist also sehr leistungsfähig und Sie können sich frei nach Ihren Wünschen und für Ihre Bedürfnisse organisieren.

Hier ist der entsprechende Plunkr, damit Sie damit spielen können: https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview .

Dieser Link aus der Angular2-Dokumentation könnte Ihnen helfen: https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html .

Ich hoffe, es hilft Ihnen (und tut mir leid, die lange Antwort), Thierry

46
  • Stellen Sie Ihre Dienste irgendwo "bereit", wo Sie sie verwenden möchten, z. B. könnten Sie sie mit bootstrap() im Stammverzeichnis Ihrer Anwendung ablegen, wenn Sie nur eine Instanz jedes Dienstes (Singletons) verwenden.
  • Verwenden Sie den @Injectable() -Dekorator für jeden Dienst, der von einem anderen abhängt.
  • Injizieren Sie die anderen Services in den Konstruktor des abhängigen Services.

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

Sehen Plunker für andere Dateien.

Das Besondere an Service DI ist, dass es immer noch auf Komponenten ankommt. Beispiel: MySecondSvc wird erstellt, wenn eine Komponente dies anfordert. Abhängig davon, wo MyFirstSvc im Komponentenbaum "bereitgestellt" wurde, kann dies die MyFirstSvc - Instanz beeinflussen, die in MySecondSvc. Dies wird hier näher erläutert: Können Sie Dienste nur über Bootstrap in Dienste einfügen?

5
Mark Rajcok

Der Service gilt als von Komponenten gemeinsam genutzt. Sagen wir also, wenn ich einen Dienst habe, kann ich ihn in verschiedenen Komponenten verwenden. 

Hier In dieser Antwort zeige ich Ihnen einen Dienst, der Daten von einer Komponente akzeptiert und diese Daten an andere Komponenten sendet.

Ich habe das Konzept von Routing, Shared-Service, Shared-Object ... verwendet.

Hinweis: @Injectable decorater wird verwendet, um den Service injizierbar zu machen. 

Antworten

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 und Shared Object

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
micronyks

ich bin mir nicht sicher, ob noch eine Antwort erforderlich ist, und ich würde versuchen, dies zu beantworten.

Betrachten Sie das folgende Beispiel, in dem wir eine Komponente haben, die einen Dienst verwendet, um einige Werte in der Vorlage wie folgt zu füllen

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

Der obige Code ist ziemlich einfach und versucht, die von getPersons zurückgegebenen Daten vom DataService abzurufen. Die DataService-Datei ist unten verfügbar.

data.service.ts

export class DataService {

persons:<Array>;

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

getPersons(){

return this.persons

}

Der obige Code funktioniert ohne den @Injectable-Dekorator einwandfrei. Das Problem beginnt jedoch, wenn unser Dienst (in diesem Fall DataService) einige Abhängigkeiten erfordert, z. Http. Wenn wir unsere data.service.ts-Datei wie folgt ändern, wird eine Fehlermeldung ausgegeben, die Cannot resolve all parameters for DataService(?). Make sure they all have valid type or annotations. sagt.

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

persons:<Array>;

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

getPersons(){

return this.persons

}

Dies hat etwas mit der Funktionsweise der Dekorateure in Angular 2 zu tun. Bitte lesen Sie https://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html um ein tieferes Verständnis dieses Themas zu erlangen. 

Der obige Code funktioniert auch nicht, da wir auch HTTP in unser Bootstrap-Modul importieren müssen. 

Eine Daumenregel, die ich vorschlagen kann, ist, dass, wenn Ihre Servicedatei eine Abhängigkeit benötigt, Sie diese Klasse mit einem Dekorateur @Injectable schmücken sollten.

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

2
Apoorv

Irgendwie funktioniert @Injectable in Angular 2.0.0-beta.17 nicht, wenn ich ComponentA -> ServiceB -> ServiceC verdrahte.

Ich habe diesen Ansatz gewählt: 

  1. Verweisen Sie auf alle Dienste im Feld der @ ComponentA-Anbieter.
  2. Verwenden Sie in ServiceB die Annotation @Inject im Konstruktor, um ServiceC zu verbinden.

Führen Sie diesen Plunker aus, um unten ein Beispiel oder Code anzuzeigen

app.ts

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

bootstrap(AppComponent);

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

übersicht-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();
    }
}

properties-service.ts

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

    logHi(){
        console.log("hi");
    }
}
2
Julius

Zuerst müssen alle Dienste mit der Annotation @Injectable versehen werden. Beachten Sie die Klammern am Ende der Anmerkung. Andernfalls funktioniert diese Lösung nicht.

Sobald dies erledigt ist, können wir Services mit Konstruktorinjektion ineinander injizieren:

@Injectable()
export class MyFirstSvc {

}

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

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

Zuerst müssen Sie Ihren Service anbieten

Sie können es entweder in der Bootstrap-Methode angeben:

bootstrap(AppComponent,[MyFirstSvc]);

oder auf der App-Komponente oder in einer anderen Komponente, je nach Ihren Bedürfnissen:

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

dann injizieren Sie einfach Ihren Dienst mit dem Konstruktor:

export class MySecondSvc {
      constructor(private myFirstSvc : MyFirstSvc ){}
}
0
bougsid