Merangkai RxJS Dapat diamati dari data http di Angular2 dengan TypeScript

96

Saat ini saya mencoba mempelajari Angular2 dan TypeScript sendiri setelah bekerja dengan senang hati dengan AngularJS 1. * selama 4 tahun terakhir! Saya harus mengakui bahwa saya membencinya tetapi saya yakin momen eureka saya sudah dekat ... bagaimanapun, saya telah menulis layanan di aplikasi dummy saya yang akan mengambil data http dari backend palsu yang saya tulis yang melayani 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');
    }   
}

Sekarang dalam sebuah Komponen saya ingin menjalankan (atau merangkai) keduanya getUserInfo()dan getUserPhotos(myId)metode. Di AngularJS ini mudah karena di controller saya, saya akan melakukan sesuatu seperti ini untuk menghindari "Pyramid of doom" ...

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

Sekarang saya telah mencoba melakukan sesuatu yang serupa di komponen saya (mengganti .thenuntuk .subscribe) namun konsol kesalahan saya menjadi gila!

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

}

Saya jelas melakukan sesuatu yang salah ... bagaimana cara terbaik saya dengan Observables dan RxJS? Maaf jika saya mengajukan pertanyaan bodoh ... tapi terima kasih atas bantuannya sebelumnya! Saya juga memperhatikan kode berulang dalam fungsi saya saat mendeklarasikan header http saya ...

Mike Sav
sumber

Jawaban:

138

Untuk kasus penggunaan Anda, menurut saya flatMapoperator adalah yang Anda butuhkan:

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

Dengan cara ini, Anda akan menjalankan permintaan kedua setelah yang pertama diterima. The flatMapOperator ini sangat berguna ketika Anda ingin menggunakan hasil dari permintaan sebelumnya (event sebelumnya) untuk mengeksekusi satu sama lain. Jangan lupa untuk mengimpor operator agar dapat menggunakannya:

import 'rxjs/add/operator/flatMap';

Jawaban ini bisa memberi Anda lebih banyak detail:

Jika Anda hanya ingin menggunakan subscribemetode, Anda menggunakan sesuatu seperti itu:

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

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

Untuk menyelesaikannya, jika Anda ingin mengeksekusi kedua permintaan secara paralel dan diberi tahu ketika semua hasil sudah tersedia, Anda harus mempertimbangkan untuk menggunakan Observable.forkJoin(Anda perlu menambahkan import 'rxjs/add/observable/forkJoin'):

Observable.forkJoin([
  this.userData.getUserPhotos(),
  this.userData.getUserInfo()]).subscribe(t=> {
    var firstResult = t[0];
    var secondResult = t[1];
});
Thierry Templier
sumber
Namun saya mendapatkan kesalahan 'TypeError: source.subscribe bukan fungsi di [null]'
Mike Sav
Di mana Anda mendapatkan kesalahan ini? Saat menelepon this.userData.getUserInfo()?
Thierry Templier
3
Oh! Ada kesalahan ketik dalam jawaban saya: this.userData.getUserPhotos(), this.userData.getUserInfo()bukan this.userData.getUserPhotos, this.userData.getUserInfo(). Maaf!
Thierry Templier
1
mengapa flatMap? Mengapa tidak switchMap? Saya akan berasumsi bahwa jika permintaan GET awal tiba-tiba mengeluarkan nilai lain untuk Pengguna, Anda tidak ingin terus mendapatkan gambar untuk nilai Pengguna sebelumnya
f.khantsis
4
Bagi siapa pun yang melihat ini dan bertanya-tanya mengapa ini tidak berfungsi: di RxJS 6 sintaks import dan forkJoin telah sedikit berubah. Anda sekarang perlu menambahkan import { forkJoin } from 'rxjs';untuk mengimpor fungsi. Selain itu, forkJoin bukan lagi anggota Observable, tetapi fungsi terpisah. Jadi, bukannya Observable.forkJoin()itu adil forkJoin().
Bas