angular - การเชื่อมต่อ Observables ของ RxJS จากข้อมูล http ใน Angular2 ด้วย TypeScript
ตอนนี้ฉันกำลังพยายามสอนตัวเอง Angular2 และ TypeScript หลังจากทำงานกับ AngularJS 1.* อย่างมีความสุขมาตลอด 4 ปีที่ผ่านมา! ฉันต้องยอมรับว่าฉันเกลียดมัน แต่ฉันมั่นใจว่าช่วงเวลาที่ฉันจะเข้าใจทุกอย่างกำลังใกล้เข้ามา... อย่างไรก็ตาม ฉันได้เขียน service ในแอปตัวอย่างของฉันที่จะดึงข้อมูล http จาก backend ปลอมที่ฉันเขียนซึ่งให้บริการ 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');
}
}
ตอนนี้ใน Component ฉันต้องการเรียกใช้ (หรือเชื่อมต่อ) ทั้งสองเมธอด 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 อย่างไรให้ดีที่สุด? ขอโทษถ้าฉันถามคำถามโง่ๆ... แต่ก็ขอบคุณสำหรับความช่วยเหลือล่วงหน้า! ฉันก็สังเกตเห็นโค้ดซ้ำซ้อนในฟังก์ชันของฉันเมื่อประกาศ header ของ http ด้วยเช่นกัน...
คำตอบ #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';
คำตอบนี้สามารถให้รายละเอียดเพิ่มเติมแก่คุณได้:
- Angular HTTP GET กับ TypeScript ข้อผิดพลาด http.get(...).map ไม่ใช่ฟังก์ชันใน [null]
ถ้าคุณต้องการใช้เฉพาะวิธี 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];
});
