用 TypeScript 从 Angular2中的 http 数据链接 RxJS 可观测数据

在愉快地使用 AngularJS1之后,我现在正在尝试自学 Angular2和 TypeScript。* for the last 4 years!我不得不承认我讨厌它,但我确信我的灵光一现的时刻就在眼前... ... 不管怎样,我已经在我的虚拟应用程序中编写了一个服务,它将从我编写的服务于 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;
});
}


}

我显然做错了什么... 我怎样才能最好的观察和 RxJS?对不起,如果我问愚蠢的问题... 但感谢提前帮助!我还注意到在声明 http 头时函数中的重复代码..。

76952 次浏览

For your use case, I think that the flatMap operator is what you need:

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

This way, you will execute the second request once the first one is received. The flatMap operator is particularly useful when you want to use the result of the previous request (previous event) to execute another one. Don't forget to import the operator to be able to use it:

import 'rxjs/add/operator/flatMap';

This answer could give you more details:

If you want to only use subscribe method, you use something like that:

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


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

To finish, if you would want to execute both requests in parallel and be notified when all results are then, you should consider to use Observable.forkJoin (you need to add import 'rxjs/add/observable/forkJoin'):

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

What you actually need is the switchMap operator. It takes the initial stream of data (User info) and when completed, replaces it with the images observable.

Here is how I'm understanding your flow:

  • Get User info
  • Get User Id from that info
  • Use the User Id to Get User Photos

Here is a demo. NOTE: I mocked the service but the code will work with the real service.

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