In this post I will try to explain how to do the same thing, but using angular + rxjs library.
Firstly - lets write here the usual angular service which has "getUsers" and "getRepos" methods:
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable} from 'rxjs';
import { from } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class DataService {
token = 'PUT-YOUR-TOKEN-HERE';
constructor(private http: HttpClient) { }
getUsers(since=0) : Observable {
return this.http.get(`https://api.github.com/users?per_page=10&since=${since}&access_token=${this.token}`)
.pipe(
catchError(this.handleError('getUsers'))
) as Observable;
}
getReposCount(user) : Observable {
return this.http.get(`https://api.github.com/users/${user}/repos?per_page=10000&access_token=${this.token}`)
.pipe(
map(repos => repos.length || 0 ),
catchError(this.handleError('getReposCount'))
) as Observable;
}
...
...
}
But how to combine those two requests to work together as nested requests?After googling around I came up with following code:
getData(since) {
return this.getUsers(since)
.pipe(
concatMap(users => from(users)),
mergeMap((user) => this.getReposCount(user.login)
.pipe(
map(count => ({login: user.login, avatar: user.avatar_url, count}))
)
),
toArray()
)
}
What a mess! - What is "form"? What are those "concatMap" and "mergeMap"? and why this "toArray" needed?I will try to understand myself
Big Picture (as i understood so far)
The big picture of what is need to be done here - is:
* We need to convert the stream which brings as a collection to stream which brings us single user (one by one)
* For each user we need to send the request to get repos count and update the user object
* Stream of single users need to be converted back to stream of arrays
concatMap
according to the docs: Map values to inner observable, subscribe and emit in order (same as mergeMap but preserves the order)
from
according to this article - converts an array to an observable. - need for converting the observable of users array to observable of single users
mergeMap
Map to observable, emit values
why not switchMap?
In many examples, demonstrating how to do some operation on the stream - the "switchMap" method is widely used:
getQuote(): Observable {
return Observable.create(observer => observer.next(this.nextId++)).pipe(
// here
switchMap((id: number) => this.http.get(`api/quotes/${id}`)),
map((q: Quote) => q.quote),
...
)
SwitchMap is also maps the observable to different one , however - the main feature of switchMap is that it "kills" the previous observable, thus it cannot fit for our case...