I am trying to map an observable, get a value from my returned observable then feed this value into another observable and return that result. Here is what I have so far:
getJobsByUser(user: User): Observable<Job[]> {
return this.getUsersGroupsAsObservable(user.uid, 'contacts').map(groups => {
groups.map(group => {
this.getJobsbyGroup(group.id);
});
});
getJobsbyGroup(groupId: string): Observable<Job[]> {
return this.afs
.collection<Job>('jobs', ref => ref.where(`group.${groupId}`, '==', true))
.valueChanges();
}
getUsersGroupsAsObservable(
userId: string,
type: string = 'users',
): Observable<Group[]> {
return this.afs
.collection<Group>('groups', ref =>
ref.where(`${type}.${userId}`, '==', true),
)
.valueChanges();
}
The problem is typescript is indicating that my getJobsByUser function will return an observable of type:void. When I do output it on my template I get nothing or undefined. I feel like I need to use switchMap but im a little fuzzy with rx/js. I am unsure how to return an Observable of type Job[]
Update: With help from @Pranay Rana I am now returning array, and can get the first value like this:
getJobsByUser(user: User): Observable<Job[]> {
return this.getUsersGroupsAsObservable(user.uid, 'contacts').pipe(
mergeMap(groups => {
// returns an array of groups - we need to map this
return this.getJobsbyGroup(groups[0].id); // works with the first value - do we need another map here?
}),
);
}
Update 2: I have managed to get some data back from firestore, but it is emitting multiple observables rather than a combined stream:
this.fb.getUsersGroupsAsObservable(user.uid, 'contacts')
.switchMap(groups => {
return groups.map(group => this.fb.getJobsbyGroup(group.id));
})
.subscribe(res => {
console.log(res);
// this emits multiple observables rather than one
this.job$ = res;
});
We've seen that operators like map and filter are functions which take in and return observables. Each operator exposes a public function like map or filter , which is what we import from 'rxjs/operators' and pass into pipe .
Returns Observable Returns an Observable that emits a boolean based on the input value: If no predicate method, the value will be converted to it's Boolean value.
Executing an Observable But the observable function does not emit any values until it is executed. The subscribe() method calls the observable's function that produces and emits data. Thus, subscribing to an observable starts a flow of data between the observable and the observer.
Below approach discussed in detail at : Way to handle Parallel Multiple Requests
Below approach make use of mergemap
getJobsByUser(user: User) {
return this.getUsersGroupsAsObservable(user.uid, 'contacts').pipe(
mergeMap(group => this.getJobsbyGroup( group.id))
);
}
callingfunction(){
const requests = this.getJobsByUser(this.user);
requests.subscribe(
data => console.log(data), //process item or push it to array
err => console.log(err));
}
you can also make use of forkJoin
getJobsByUser(user: User) {
return this.getUsersGroupsAsObservable(user.uid, 'contacts').pipe(
map(group => this.getJobsbyGroup( group.id))
);
}
callingfunction(){
const requests = forkJoin(this.getJobsByUser(this.user));
requests.subscribe(
data => console.log(data), //process item or push it to array
err => console.log(err));
}
Ok, so first you are missing two return
statements in getJobsByUser
function (shown below with capital RETURN
:
getJobsByUser(user: User): Observable<Job[]> {
return this.getUsersGroupsAsObservable(user.uid, 'contacts').map(groups => {
RETURN groups.map(group => {
RETURN this.getJobsbyGroup(group.id);
});
});
Or, a bit more elegantly:
getJobsByUser(user: User): Observable<Job[]> {
return this.getUsersGroupsAsObservable(user.uid, 'contacts')
.map(groups => groups.map(group => this.getJobsbyGroup(group.id)));
}
Now we need to flatten the result, because, if I get this right, 1 user
has many groups
and each group
many jobs
and you want your final result to be just a Job[]
with all the jobs
of all the groups
of the user
.
You can use several operators to flatten results, e.g. switchMap
, or concatMap
. Here is an example:
getJobsByUser(user: User): Observable<Job[]> {
return this.getUsersGroupsAsObservable(user.uid, 'contacts')
.concatMap(groups => groups)
.concatMap(group => this.getJobsbyGroup(group.id))
.concatMap(jobs => jobs)
.toArray();
}
What each line says is this:
groups
for a user
// returns Observable<Group[]>
Group[]
// returns Observable<Group>
jobs
for a group
// returns Observable<Job[]>
job[]
// returns Observable<Job>
Job
to one event that has a Job[]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With