I am just a week old with Angular2!
There are basically 2 API calls.
The problem comes in the 5th step due to async nature of Observables. I cannot access queryResults[i].oldLastSeen
inside inner Subscribe and is basically undefined. Hence the lastSeen doesn't gets updated.
Starting by calling getLastSeen()
,
Code looks like this:
temp: any[];
rows: any[];
getLastSeen() {
this._someService.getQueryResults(this.query)
.subscribe(queryResults => {
this.rows = queryResults;
this.updateLastSeen(this.rows);
});
}
private updateLastSeen(rows) {
this.temp = rows; //temp: any[];
for(var i=0; i<this.temp.length; i++) {
if(this.temp[i].id != null || this.temp[i].oldLastSeen != null) {
//Some operations here
this._someService.getNewLastSeen(this.temp[i].oldLastSeen)
.subscribe(
newLastSeen => {
this.rows[i].oldLastSeen = newLastSeen; //This doesnot happen!
},
error => alert(error),
() => console.log("Finished.")
);
}
}
}
Banging my head since 2 days, came across flatMap, switchMap etc. But highly skeptical about their usage.
Update: Working Code:
I followed Bryan's feedback, and made some modifications to his suggested code. Here is what worked for me:
this._someService.getQueryResults(this.query)
.do(qr => {this.rows = qr;})
.flatMap(queryResults => Observable.from(queryResults))
.filter(queryItem => queryItem["id"] != null || queryItem["oldLastSeen"] != undefined)
.do(queryItem => { // did some operations/ manipulations here})
.mergeMap(queryItem => this._someService.getNewLastSeen(this.queryItem["oldLastSeen]),
(queryItem, newLastSeen) => [queryItem, newLastSeen])
.subscribe(([queryItem, newLastSeen]) => {
queryItem.oldLastSeen = newLastSeen
});
Generally, if you have nested subscribes, you're dealing with an anti pattern that can be improved. I haven't found a compelling case for using one yet. I'm not sure what it means to be skeptical of flatMap or switchMap, but they're incredibly useful operators, especially switchMap. You will not be able to do much beyond rxjs basics without having switchMap in your tool kit.
getLastSeen() {
this._someService.getQueryResults(this.query)
.do(qr => this.rows = qr;)
.flatMap(queryResults => Observable.from(queryResults))
.filter(queryItem => queryItem.id !== null || queryItem.oldLastSeen !== null)
// you can insert a map or do operator here to accomplish the operations you mention, depending on what they are, or you can possibly just do them inside switchMap
.switchMap(queryItem => this._someService.getNewLastSeen(queryItem.oldLastSeen),
(queryItem, newLastSeen) => [queryItem, newLastSeen])
.subscribe(([queryItem, newLastSeen]) => queryItem.oldLastSeen = newLastSeen);
}
taking it piece by piece:
you get your query
you set the results to the view. functional purists will say doing this here is bad practice as it's using a side effect, but whatever. I'm not a purist.
this one is tricky, but you're flattening your results into a stream of observables. You create an observable from your results array, and flatmap emits them one by one, you could have jsut used .mergeAll() here too. This is sort of a more reactive way of looping through the results.
filter out the items you're not interested in
placeholder line for your "operations", maybe map or do, hard to say without knowing the desired effect. also may just be doable inside switchMap, but points for style and keeping operators succinct.
switchMap into the new observable, switchMap will subscribe to the inner observable and emit the result, the key to this is the second argument to switchMap which allow you to combine the item from both the inner and outter observable. here we just put them into an array and emit the array of both values
reset the item in the subscription. The only thing fun here is the use of typescript syntax that lets you name the items in your array since you know you're getting an array wiht 2 items.