【问题标题】:Chaining RxJS Observables from http data in Angular2 with TypeScript使用 TypeScript 从 Angular2 中的 http 数据链接 RxJS Observables
【发布时间】:2016-05-18 01:07:46
【问题描述】:

在过去 4 年愉快地使用 AngularJS 1.* 之后,我目前正在尝试自学 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');
    }   
}

现在在一个组件中,我希望同时运行(或链接)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 标头时,我还注意到函数中的重复代码...

【问题讨论】:

    标签: typescript angular observable rxjs


    【解决方案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];
    });
    

    【讨论】:

    • 但是我确实收到错误“TypeError: source.subscribe is not a function in [null]”
    • 你在哪里遇到这个错误?打电话时this.userData.getUserInfo()?
    • 哦!我的回答中有一个错字:this.userData.getUserPhotos(), this.userData.getUserInfo() 而不是this.userData.getUserPhotos, this.userData.getUserInfo()。对不起!
    • 为什么选择平面地图?为什么不切换地图?我会假设,如果初始 GET 请求突然为 User 输出另一个值,您不会希望继续获取 User 先前值的图像
    • 对于任何看到这个并想知道它为什么不起作用的人:在 RxJS 6 中,import 和 forkJoin 语法略有变化。您现在需要添加import { forkJoin } from 'rxjs'; 来导入该函数。另外,forkJoin 不再是 Observable 的成员,而是一个单独的函数。所以不是Observable.forkJoin(),而是forkJoin()
    【解决方案2】:

    您真正需要的是switchMap 运算符。它采用初始数据流(用户信息),完成后,将其替换为可观察的图像。

    我是这样理解你的流程的:

    • 获取用户信息
    • 从该信息中获取用户 ID
    • 使用用户 ID 获取用户照片

    Here is a demo。注意:我模拟了服务,但代码将与真实服务一起使用。

      ngOnInit() {
        this.getPhotos().subscribe();
      }
    
      getUserInfo() {
        return this.userData.getUserInfo().pipe(
          tap(data => {
            this.userInfo = data;
          }))
      }
      getPhotos() {
        return this.getUserInfo().pipe(
          switchMap(data => {
            return this.userData.getUserPhotos(data.UserId).pipe(
              tap(data => {
                this.userPhotos = data;
              })
            );
          })
        );
      }
    
    

    【讨论】:

      猜你喜欢
      • 2016-08-18
      • 1970-01-01
      • 1970-01-01
      • 2017-01-06
      • 1970-01-01
      • 2016-10-08
      • 2016-09-07
      • 1970-01-01
      • 2016-01-24
      相关资源
      最近更新 更多