I have two nodes in my database:
users: {user1: {uid: 'user1', name: "John"}, user2: {uid: 'user2', name: "Mario"}}
homework: {user1: {homeworkAnswer: "Sample answer"}}
Some users might or might not have a homework.
I want to obtain a list of all users with each user's homework data with a single call and subscribe. What would be the best way to achieve this?
Here is how the list obtained should look like for the example above:
[{uid: 'user1', name: "John", homework: {homeworkAnswer: "Sample answer"}}, {uid: 'user2', name: "Mario"}]
These are my two observables for users
and homework
:
let usersObservable = this.af.getObservable(`users/`);
let hwObservable = this.af.getObservable(`homework/`);
I have two nodes in my database:
users: {user1: {uid: 'user1', name: "John"}, user2: {uid: 'user2', name: "Mario"}}
homework: {user1: {homeworkAnswer: "Sample answer"}}
Some users might or might not have a homework.
I want to obtain a list of all users with each user's homework data with a single call and subscribe. What would be the best way to achieve this?
Here is how the list obtained should look like for the example above:
[{uid: 'user1', name: "John", homework: {homeworkAnswer: "Sample answer"}}, {uid: 'user2', name: "Mario"}]
These are my two observables for users
and homework
:
let usersObservable = this.af.getObservable(`users/`);
let hwObservable = this.af.getObservable(`homework/`);
Share
Improve this question
edited Sep 4, 2018 at 13:52
Smitherson
asked Sep 4, 2018 at 13:45
SmithersonSmitherson
4331 gold badge6 silver badges12 bronze badges
7
- Please be more exact with your terminology. Nowhere in your code you have a list. You ask for a list in your question but your example on how the result should look is an object. Which one do you need? – tom van green Commented Sep 4, 2018 at 13:50
-
So
hwObservable
returns all homeworks for all users? – martin Commented Sep 4, 2018 at 13:50 - @tomvangreen I have edited my answer. – Smitherson Commented Sep 4, 2018 at 13:51
-
@martin Yes, and
this.af.getObservable('homework/${user.uid}');
obtains the homework for a specified user. – Smitherson Commented Sep 4, 2018 at 13:52 -
Thanks. There is still a little error in your example. Lists don't have properties. If you need a list you need to remove
user1:
anduser2:
from the result and only have the objects ([{}, {}]
) – tom van green Commented Sep 4, 2018 at 13:52
1 Answer
Reset to default 7Basically you need to do two things, but there might be some details to consider.
- Get the data from both observables.
- Transform the data to your desired result.
The first step can easily be achieved by using forkJoin
. In forkJoin
you can pass in multiple observables and it will emit a value as soon as all observables have pleted. Important: forkJoin
only emits after pletion. If your data es from a store or a subject, you might have to add a take(1) operator so it actually pletes.
The transformation part should be easy as well. I assume you want to have all users that exist in the user object, so we can use Object.keys
to iterate over the existing user keys and then use map
to transform the data.
// Imports:
import { forkJoin } from 'rxjs';
import { take } from 'rxjs/operators';
// Step 1: Use fork join to get the result from both observables
forkJoin(
// pipe(take(1)) is only needed if the observables don't plete.
usersObservable.pipe(take(1)),
hwObservable.pipe(take(1))
)
// Step 2: Transform the data.
// We now need to map our both results. We want to return all
// users and add the homework if available. So we can user
// Object.keys to iterate over the existing user keys in your object.
.pipe(map(([users, homeworkMap]) => Object.keys(users)
// Now we can map the keys to the actual user objects
// and merge them with the homework
.map(userKey => {
const user = users[userKey];
const homework = homeworkMap[userKey];
return {
...user,
homework
};
})
))
.subscribe(users => console.log('Users: ', users));