web-dev-qa-db-fra.com

Chaînage des observables RxJS à partir de données http dans Angular2 avec TypeScript

J'essaie actuellement d'apprendre Angular2 et TypeScript après avoir travaillé avec AngularJS 1. * pendant 4 ans! Je dois avouer que je le déteste mais je suis sûr que mon moment eureka est imminent ... de toute façon, j’ai écrit un service dans mon application factice qui récupère les données http d’un backend bidon que j'ai écrit et qui sert JSON.

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

Maintenant, dans un composant, je souhaite exécuter (ou chaîner) les deux méthodes getUserInfo() et getUserPhotos(myId). Dans AngularJS, c'était facile, car dans mon contrôleur, je ferais quelque chose comme ceci pour éviter la "pyramide de Doom" ...

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

Maintenant, j'ai essayé de faire quelque chose de similaire dans mon composant (en remplaçant .then par .subscribe), mais ma console d'erreur est devenue folle!

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

}

Je fais évidemment quelque chose de mal ... comment pourrais-je mieux utiliser Observables et RxJS? Désolé si je pose des questions stupides ... mais merci pour l'aide à l'avance! J'ai également remarqué le code répété dans mes fonctions lors de la déclaration de mes en-têtes http ...

87
Mike Sav

Pour votre cas d'utilisation, je pense que l'opérateur flatMap est ce dont vous avez besoin:

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

De cette façon, vous exécuterez la deuxième requête une fois la première reçue. L'opérateur flatMap est particulièrement utile lorsque vous souhaitez utiliser le résultat de la requête précédente (événement précédent) pour en exécuter une autre. N'oubliez pas d'importer l'opérateur pour pouvoir l'utiliser:

import 'rxjs/add/operator/flatMap';

Cette réponse pourrait vous donner plus de détails:

Si vous voulez utiliser uniquement la méthode subscribe, vous utilisez quelque chose comme ça:

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

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

Pour terminer, si vous souhaitez exécuter les deux demandes en parallèle et être averti de tous les résultats, envisagez d'utiliser Observable.forkJoin (vous devez ajouter import 'rxjs/add/observable/forkJoin'):

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