2015-12-24 5 views
14

Ich versuche mit Angular 2-Beta zu spielen und ich möchte mit Http Komponente arbeiten. Aber es ist ein ernstes Problem hier:Wie Http-Komponente in einem Service in Winkel 2 Beta effizient zu konsumieren?

Ich las this und Ich weiß, in Angular 2 (Im Gegensatz zu Angular 1), Http Komponente ist kein Dienst, der eine Versprechen zurückgibt. Es gibt etwas zurück, das Observable genannt wird. Wir wissen, dass eine Komponente besser nicht Http direkt verwenden soll. Effizienter Weg ist es, einen Dienst zu machen, der für den Verbrauch verantwortlich ist Http. Aber wie?! Sollte dies nach Abschluss einer Anfrage ein Versprechen abgeben? (siehe here)

Macht das überhaupt Sinn ?!

+1

Sie können HTTP ** als Versprechen verwenden, indem Sie '.toPromise()' gefolgt von Ihrer Kette von '.then()' Aufrufen hinzufügen. Dennoch sind Observables der empfohlene Ansatz. –

+1

@EvanPlaice Yeah Ich lese über sie und jetzt bin ich ein Fan von Observables :) –

+0

Werfen Sie einen Blick auf diese http://StackOverflow.com/A/34758630/5043867 –

Antwort

23

Mit Angular 2 ist es möglich, Dienste zu implementieren. Sie entsprechen einfach den nachstehend beschriebenen injizierbaren Klassen. In diesem Fall kann diese Klasse in andere Elemente wie Komponenten injiziert werden.

import {Injectable} from 'angular2/core'; 
import {Http, Headers} from 'angular2/http'; 
import 'rxjs/add/operator/map'; 

@Injectable() 
export class CompanyService { 
    constructor(http:Http) { 
    this.http = http; 
    } 
} 

Sie können ein Http Objekt in sie injizieren am Zustand (seinen Konstruktor) Sie HTTP_PROVIDERS angegeben wird, wenn die Hauptkomponente der Anwendung bootstraping:

import {bootstrap} from 'angular2/platform/browser' 
import {HTTP_PROVIDERS} from 'angular2/http'; 
import {AppComponent} from './app.component' 

bootstrap(AppComponent, [ 
    HTTP_PROVIDERS 
]); 

Dieser Dienst kann dann in eine injiziert werden Komponente, wie unten beschrieben. Vergessen Sie nicht, es in der providers Liste der Komponente anzugeben.

import { Component, View, Inject } from 'angular2/core'; 
import { CompanyService } from './company-service'; 

@Component({ 
    selector: 'company-list', 
    providers: [ CompanyService ], 
    template: ` 
    (...) ` 
}) 

export class CompanyList { 
    constructor(private service: CompanyService) { 
    this.service = service; 
    } 
} 

Sie können dann eine Methode implementieren, um die Http Objekt in Ihrem Dienst nutzen und senden Sie das beobachtbare Objekt zu Ihrer Anfrage entsprechen:

@Injectable() 
export class CompanyService { 
    constructor(http:Http) { 
    this.http = http; 
    } 

    getCompanies() { 
    return this.http.get('https://angular2.apispark.net/v1/companies/') 
        .map(res => res.json()); 
    } 
} 

Die Komponente kann dann nennen diese getCompanies Methode und einen Rückruf abonnieren auf das Observable-Objekt, das benachrichtigt werden soll, wenn die Antwort zur Aktualisierung des Status der Komponente vorhanden ist (wie bei Versprechungen in Angular1):

export class CompanyList implements OnInit { 
    public companies: Company[]; 

    constructor(private service: CompanyService) { 
    this.service = service; 
    } 

    ngOnInit() { 
    this.service.getCompanies().subscribe(
     data => this.companies = data); 
    } 
} 

bearbeiten

Wie foxx in seinem Kommentar vorgeschlagen, das async Rohr auch auf dem beobachtbaren Objekt implizit abonniert verwendet werden könnte, um. Hier ist der Weg, es zu benutzen.Zuerst aktualisieren Sie Ihre Komponente die beobachtbare Objekt im Attribut setzen möchten Sie anzeigen:

export class CompanyList implements OnInit { 
    public companies: Company[]; 

    constructor(private service: CompanyService) { 
    this.service = service; 
    } 

    ngOnInit() { 
    this.companies = this.service.getCompanies(); 
    } 
} 

Verwenden Sie dann die Asynchron Rohr in der Vorlage:

@Component({ 
    selector: 'company-list', 
    providers: [ CompanyService ], 
    template: ` 
    <ul> 
     <li *ngFor="#company of companies | async">{{company.name}}</li> 
    </ul> 
    ` 
}) 
export class CompanyList implements OnInit { 
    (...) 
} 

Dieser Artikel ist in zwei Teile könnten mehr Details geben als gut:

Hoffe, es hilft Ihnen, Thierry

+7

Sie möchten möglicherweise asynchrone Pipe statt manuell abonnieren . – foxx

+0

Vielen Dank @foox für Ihren Kommentar! Ich habe meine Antwort aktualisiert, um zu beschreiben, wie man die asynchrone Pipe benutzt ;-) –

+0

Eine kleine Frage, Sie haben 'HTTP_PROVIDERS' im Bootstrap importiert, aber' ROUTER_PROVIDERS' injiziert. ist es ein Tippfehler? –

6

Es gibt keine Notwendigkeit, die beobachtbare zurück von Http die Methode get() in ein Versprechen zu konvertieren. In den meisten Fällen kann der Dienst das Observable einfach zurückgeben.

Wenn wir eine Array oder einen primitiven Typ sind Abrufen (dh, string, Zahl, boolean) vom Server, können wir unsere Controller-Logik vereinfachen, indem die zurück beobachtbaren direkt in unserer Vorlage, mit den asyncPipe . Diese Pipe wird die Observable automatisch abonnieren (sie funktioniert auch mit einer Zusage) und gibt den neuesten Wert zurück, den die Observable ausgegeben hat. Wenn ein neuer Wert ausgegeben wird, markiert die Pipe die Komponente, die auf Änderungen geprüft werden soll. Daher wird die Ansicht automatisch mit dem neuen Wert aktualisiert.

Wenn wir ein Objekt vom Server holen, Ich bin mir nicht bewusst irgendeine Weise asyncPipe zu verwenden, wir das Asynchron-Rohr, in Verbindung mit dem sicheren Navigation Operator verwenden könnten wie folgt aussehen:

{{(objectData$ | async)?.name}} 

Aber das sieht kompliziert aus, und wir müssten das für jede Objekteigenschaft wiederholen, die wir anzeigen wollten.

Stattdessen schlage ich subscribe() auf die Observable in der Komponente und speichern Sie das enthaltene Objekt in einer Komponente Eigenschaft. Wir verwenden dann die safe navigation operator (?.) Oder (wie @Evan Plaice in einem Kommentar erwähnt) NgIf in der Vorlage. Wenn wir den sicheren Navigationsoperator oder NgIf nicht verwenden, wird ein Fehler ausgegeben, wenn die Vorlage zum ersten Mal versucht, zu rendern, da das Objekt noch nicht mit einem Wert gefüllt ist.

Beachten Sie, dass der folgende Service immer eine Observable für jede Get-Methode liefert.

service.ts

import {Injectable} from 'angular2/core'; 
import {Http} from 'angular2/http'; 
import 'rxjs/add/operator/map'; // we need to import this now 

@Injectable() 
export class MyService { 
    constructor(private _http:Http) {} 
    getArrayDataObservable() { 
    return this._http.get('./data/array.json') 
     .map(data => data.json()); 
    } 
    getPrimitiveDataObservable() { 
    return this._http.get('./data/primitive.txt') 
     .map(data => data.text()); // note .text() here 
    } 
    getObjectDataObservable() { 
    return this._http.get('./data/object.json') 
     .map(data => data.json()); 
    } 
} 

app.ts

import {Component} from 'angular2/core'; 
import {MyService} from './my-service.service'; 
import {HTTP_PROVIDERS} from 'angular2/http'; 

@Component({ 
    selector: 'my-app', 
    providers: [HTTP_PROVIDERS, MyService], 
    template: ` 
    <div>array data using '| async': 
     <div *ngFor="#item of arrayData$ | async">{{item}}</div> 
    </div> 
    <div>primitive data using '| async': {{primitiveData$ | async}}</div> 
    <div>object data using ?.: {{objectData?.name}}</div> 
    <div *ngIf="objectData">object data using NgIf: {{objectData.name}}</div>` 
}) 
export class AppComponent { 
    constructor(private _myService:MyService) { console.clear(); } 
    ngOnInit() { 
    this.arrayData$  = this._myService.getArrayDataObservable(); 
    this.primitiveData$ = this._myService.getPrimitiveDataObservable(); 
    this._myService.getObjectDataObservable() 
     .subscribe(data => this.objectData = data); 
    } 
} 

Hinweis: I "beobachtbare" in den Dienst Methodennamen – zB setzen, getArrayDataObervable() – nur hervorheben, dass das Verfahren eine beobachtbare zurückgibt . Normalerweise werden Sie nicht "Observable" in den Namen setzen.

Daten/array.json

[ 1,2,3 ] 

Daten/primitiv.json

Greetings SO friends! 

data/object.json

{ "name": "Mark" } 

Output:

array data using '| async': 
1 
2 
3 
primitive data using '| async': Greetings SO friends! 
object data using .?: Mark 
object data using NgIf: Mark 

Plunker


Ein Nachteil bei usin g Die async Pipe ist, dass es keinen Mechanismus gibt, um Serverfehler in der Komponente zu behandeln. I answered another question, die erläutert, wie solche Fehler in der Komponente zu fangen, aber wir müssen immer subscribe() in diesem Fall verwenden.

+1

Eine nützliche Alternative zum "?" - (elvis) -Operator besteht darin, dem Template-Abschnitt, in dem die Daten verwendet werden, eine '* ngIf'-Bedingung hinzuzufügen. Es bietet eine gröbere Kontrollebene, so dass Sie sich nicht darum kümmern müssen, die Elvis-Operatoren über die gesamte Vorlage zu streuen oder sich Gedanken darüber zu machen, wie die Vorlage aussieht, wenn sie ohne Daten gerendert wird. –

+0

@EvanPlaice, danke, ich habe die Antwort aktualisiert, um deinen Vorschlag hinzuzufügen. –

Verwandte Themen