web-dev-qa-db-ger.com

Verketten von RxJS Observables aus http-Daten in Angular2 mit TypeScript

Ich versuche gerade, mir Angular2 und TypeScript beizubringen, nachdem ich in den letzten 4 Jahren glücklich mit AngularJS 1. * gearbeitet habe! Ich muss zugeben, dass ich es hasse, aber ich bin mir sicher, dass mein eureka-Moment gleich um die Ecke ist ... trotzdem habe ich in meiner Dummy-App einen Service geschrieben, der http-Daten von einem gefälschten Backend abruft, das ich geschrieben habe und das JSON bedient.

import {Injectable} from 'angular2/core';
import {Http, Headers, Response} from 'angular2/http';
import {Observable} from 'rxjs';

@Injectable()
export class UserData {

    constructor(public http: Http) {
    }

    getUserStatus(): any {
        var headers = new Headers();
        headers.append('Content-Type', 'application/json');
        return this.http.get('/restservice/userstatus', {headers: headers})
            .map((data: any) => data.json())
            .catch(this.handleError);
    }

    getUserInfo(): any {
        var headers = new Headers();
        headers.append('Content-Type', 'application/json');
        return this.http.get('/restservice/profile/info', {headers: headers})
            .map((data: any) => data.json())
            .catch(this.handleError);
    }

    getUserPhotos(myId): any {
        var headers = new Headers();
        headers.append('Content-Type', 'application/json');
        return this.http.get(`restservice/profile/pictures/overview/${ myId }`, {headers: headers})
            .map((data: any) => data.json())
            .catch(this.handleError);
    }

    private handleError(error: Response) {
        // just logging to the console for now...
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }   
}

Jetzt möchte ich in einer Komponente beide Methoden getUserInfo() und getUserPhotos(myId) ausführen (oder verketten). In AngularJS war das einfach, da ich in meinem Controller so etwas tun würde, um die "Pyramide des Schicksals" zu umgehen ...

// Good old AngularJS 1.*
UserData.getUserInfo().then(function(resp) {
    return UserData.getUserPhotos(resp.UserId);
}).then(function (resp) {
    // do more stuff...
}); 

Jetzt habe ich versucht, etwas Ähnliches in meiner Komponente zu tun (.then Durch .subscribe Zu ersetzen), aber meine Fehlerkonsole wurde verrückt!

@Component({
    selector: 'profile',
    template: require('app/components/profile/profile.html'),
    providers: [],
    directives: [],
    pipes: []
})
export class Profile implements OnInit {

    userPhotos: any;
    userInfo: any;

    // UserData is my service
    constructor(private userData: UserData) {
    }

    ngOnInit() {

        // I need to pass my own ID here...
        this.userData.getUserPhotos('123456') // ToDo: Get this from parent or UserData Service
            .subscribe(
            (data) => {
                this.userPhotos = data;
            }
        ).getUserInfo().subscribe(
            (data) => {
                this.userInfo = data;
            });
    }

}

Ich mache offensichtlich etwas falsch ... wie würde ich am besten mit Observables und RxJS umgehen? Sorry, wenn ich blöde Fragen stelle ... aber danke für die Hilfe im Voraus! Ich habe auch den wiederholten Code in meinen Funktionen bemerkt, als ich meine http-Header deklarierte ...

87
Mike Sav

Für Ihren Anwendungsfall denke ich, dass Sie den Operator flatMap brauchen:

this.userData.getUserPhotos('123456').flatMap(data => {
  this.userPhotos = data;
  return this.userData.getUserInfo();
}).subscribe(data => {
  this.userInfo = data;
});

Auf diese Weise führen Sie die zweite Anforderung aus, sobald die erste empfangen wurde. Der Operator flatMap ist besonders nützlich, wenn Sie das Ergebnis der vorherigen Anforderung (vorheriges Ereignis) verwenden möchten, um eine andere Anforderung auszuführen. Vergessen Sie nicht, den Operator zu importieren, um ihn verwenden zu können:

import 'rxjs/add/operator/flatMap';

Diese Antwort könnte Ihnen weitere Details geben:

Wenn Sie nur die Methode subscribe verwenden möchten, verwenden Sie Folgendes:

this.userData.getUserPhotos('123456')
    .subscribe(
      (data) => {
        this.userPhotos = data;

        this.userData.getUserInfo().subscribe(
          (data) => {
            this.userInfo = data;
          });
      });

Wenn Sie abschließend beide Anforderungen gleichzeitig ausführen und benachrichtigt werden möchten, wenn alle Ergebnisse vorliegen, sollten Sie die Verwendung von Observable.forkJoin In Betracht ziehen (Sie müssen import 'rxjs/add/observable/forkJoin' Hinzufügen):

Observable.forkJoin([
  this.userData.getUserPhotos(),
  this.userData.getUserInfo()]).subscribe(t=> {
    var firstResult = t[0];
    var secondResult = t[1];
});
128