Цепочка RxJS Observables из http-данных в Angular2 с помощью TypeScript


в настоящее время я пытаюсь научить себя Angular2 и TypeScript после счастливой работы с AngularJS 1.* за последние 4 года! Я должен признать, что ненавижу его, но я уверен, что мой момент эврики не за горами... во всяком случае, я написал службу в своем фиктивном приложении, которая будет извлекать данные http из фальшивого бэкэнда, который я написал, который обслуживает 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');
    }   
}

теперь в компоненте я хочу запустить (или цепь) оба getUserInfo() и getUserPhotos(myId) методы. В AngularJS это было легко, как в моем контроллер я бы сделал что-то подобное, чтобы избежать "пирамиды обреченности"...

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

теперь я попытался сделать что-то подобное в моем компоненте (замена .then на .subscribe) однако моя консоль ошибок сходит с ума!

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

}

Я, очевидно, делаю что-то не так... как бы я лучше всего с Observables и RxJS? Извините, если я задаю глупые вопросы... но спасибо за помощь заранее! Я также заметил повторяющийся код в моих функциях при объявлении моего http заголовки...

1 71

1 ответ:

для вашего случая, я думаю, что flatMap оператор-это то, что вам нужно:

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

таким образом, вы будете выполнять второй запрос после первого получения. Элемент flatMap оператор особенно полезен, когда вы хотите использовать результат предыдущего запроса (предыдущего события) для выполнения другого. Не забудьте импортировать оператор, чтобы иметь возможность использовать его:

import 'rxjs/add/operator/flatMap';

этот ответ может дать вам больше подробности:

если вы хотите использовать только subscribe метод, вы используете что-то вроде этого:

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

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

чтобы закончить, если вы хотите выполнять оба запроса параллельно и получать уведомления, когда все результаты будут тогда, вы должны рассмотреть возможность использования Observable.forkJoin (вы должны добавить import 'rxjs/add/observable/forkJoin'):

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