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/`);
  • 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 Sep 4 '18 at 13:50
  • So hwObservable returns all homeworks for all users? – martin Sep 4 '18 at 13:50
  • @tomvangreen I have edited my answer. – Smitherson Sep 4 '18 at 13:51
  • @martin Yes, and this.af.getObservable('homework/${user.uid}'); obtains the homework for a specified user. – Smitherson Sep 4 '18 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: and user2: from the result and only have the objects ( [{}, {}] ) – tom van green Sep 4 '18 at 13:52

Basically you need to do two things, but there might be some details to consider.

  1. Get the data from both observables.
  2. 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 completed. Important: forkJoin only emits after completion. If your data comes from a store or a subject, you might have to add a take(1) operator so it actually completes.

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
    // pipe(take(1)) is only needed if the observables don't complete.
    // 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 {
    .subscribe(users => console.log('Users: ', users));
  • I had a small error in my example (I don't have an angular environment running here, so I could not verify it works correctly). I updated the example and it should be correct now. The first map should actually be an rxjs operator and I forgot to add the pipe statement. – tom van green Sep 4 '18 at 14:13
  • Worked like a charm. Thank you, sir! – Smitherson Sep 4 '18 at 14:20
  • 1
    You're welcome. Glad I could help – tom van green Sep 4 '18 at 14:21

Your Answer

