1. ホーム
  2. typescript

[解決済み] Angular2のhttpデータからRxJS ObservablesをTypeScriptで連鎖させる。

2022-12-31 10:41:12

質問

私は現在、Angular2とTypeScriptを独学で勉強しようとしています。とにかく、私はJSONを提供する私が書いた偽のバックエンドからhttpデータを取得する私のダミーアプリのサービスを書きました。

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

今度はComponentの中で getUserInfo()getUserPhotos(myId) メソッドを使用します。AngularJSでは、コントローラで次のようなことをして、"Pyramid of doom"を避けるので簡単でした...。

// 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ヘッダを宣言するときに、私の関数でコードが繰り返されていることに気づきました...

どのように解決するのですか?

あなたのユースケースの場合、私は flatMap 演算子が必要だと思います。

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

この方法では、最初のリクエストを受信したら、2番目のリクエストを実行することになります。このとき 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];
});