Chuỗi các Quan sát RxJS từ dữ liệu http trong Angular2 với TypeScript


95

Tôi hiện đang cố gắng tự học Angular2 và TypeScript sau khi vui vẻ làm việc với AngularJS 1. * trong 4 năm qua! Tôi phải thừa nhận rằng tôi ghét nó nhưng tôi chắc chắn rằng khoảnh khắc eureka của tôi đang ở gần ... dù sao, tôi đã viết một dịch vụ trong ứng dụng giả của mình sẽ lấy dữ liệu http từ một phần mềm phụ trợ phoney mà tôi đã viết để phục vụ 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');
    }   
}

Bây giờ trong một Thành phần, tôi muốn chạy (hoặc chuỗi) cả hai getUserInfo()getUserPhotos(myId)các phương thức. Trong AngularJS, điều này thật dễ dàng vì trong bộ điều khiển của tôi, tôi sẽ làm điều gì đó như thế này để tránh "Kim tự tháp diệt vong" ...

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

Bây giờ tôi đã thử làm điều gì đó tương tự trong thành phần của mình (thay thế .thencho .subscribe) tuy nhiên bảng điều khiển lỗi của tôi đang phát điên!

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

}

Rõ ràng là tôi đang làm sai điều gì đó ... làm cách nào tốt nhất với Observables và RxJS? Xin lỗi nếu tôi đang hỏi những câu hỏi ngu ngốc ... nhưng cảm ơn sự giúp đỡ trước! Tôi cũng đã nhận thấy mã lặp lại trong các hàm của mình khi khai báo các tiêu đề http ...

Câu trả lời:


137

Đối với trường hợp sử dụng của bạn, tôi nghĩ rằng flatMapnhà điều hành là những gì bạn cần:

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

Bằng cách này, bạn sẽ thực hiện yêu cầu thứ hai sau khi nhận được yêu cầu đầu tiên. Các flatMapnhà điều hành đặc biệt hữu ích khi bạn muốn sử dụng kết quả của các yêu cầu trước đó (sự kiện trước đó) để thực hiện một số khác. Đừng quên nhập toán tử để có thể sử dụng nó:

import 'rxjs/add/operator/flatMap';

Câu trả lời này có thể cung cấp cho bạn thêm chi tiết:

Nếu bạn chỉ muốn sử dụng subscribephương thức, bạn sử dụng một cái gì đó như thế:

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

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

Để kết thúc, nếu bạn muốn thực hiện cả hai yêu cầu song song và được thông báo khi có tất cả kết quả, bạn nên cân nhắc sử dụng Observable.forkJoin(bạn cần thêm import 'rxjs/add/observable/forkJoin'):

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

Tuy nhiên tôi làm gặp phải lỗi 'Lỗi Loại: source.subscribe không phải là một chức năng trong [null]
Mike Sav

Bạn bị lỗi này do đâu? Khi gọi điện this.userData.getUserInfo()?
Thierry Templier,

3
Oh! Có một lỗi đánh máy trong câu trả lời của tôi: this.userData.getUserPhotos(), this.userData.getUserInfo()thay vì this.userData.getUserPhotos, this.userData.getUserInfo(). Lấy làm tiếc!
Thierry Templier,

1
tại sao flatMap? Tại sao không switchMap? Tôi cho rằng nếu các yêu cầu GET ban đầu đột nhiên kết quả đầu ra một giá trị cho người dùng, bạn sẽ không muốn tiếp tục nhận hình ảnh cho các giá trị trước đó của người dùng
f.khantsis

4
Đối với bất kỳ ai nhìn vào điều này và tự hỏi tại sao nó không hoạt động: trong RxJS 6, cú pháp import và forkJoin đã thay đổi một chút. Bây giờ bạn cần thêm import { forkJoin } from 'rxjs';để nhập chức năng. Ngoài ra, forkJoin không còn là một thành viên của Observable, mà là một chức năng riêng biệt. Vì vậy, thay vì Observable.forkJoin()nó chỉ forkJoin().
Bas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.