1

I'd like to join data from my trainings-table into my users-table.

The data structure it like this:

 - users
  - key1
     - name
     - trainingIds
         - t_key1
         - t_key3
 - trainings
  - t_key1
     - name
     - description
  - t_key2
     - name
     - description
  - t_key3
     - name
     - description

I've already searched Stackoverflow and found a nice solution. Unfortunately, I wasn't able to solve the problem mentioned by Romain Bruckert.

This is my code:

this.visibleUser$ = af.database.list(`${this.path}`)
        .map( users => {
          console.log( users );
          return users.map( user => {
            console.log( user );
            console.log( user.trainingIds );
            user.trainingIds.map( trainingIdsMap => {
              af.database.list(`dev/trainings/${trainingIdsMap.$key}`)
              .map( tr => {
                trainingIdsMap = tr;
              });
            });
            return user
          });
        }) as FirebaseListObservable<any>;

This is the Firebase data structure:

{
  "trainings" : {
    "-KdGENe4XiCyEowgYGbu" : {
      "description" : "lala beschreibung",
      "name" : "Bring Sally Up Challenge"
    },
    "-KdGGjQtvdLPWZOSHjKP" : {
      "description" : "Beschreibung für liegestütz",
      "name" : "Ligestütze"
    },
    "-KdGH3qNKCWGnW1kebMj" : {
      "active" : false,
      "description" : "Des funktioniert ja wirklich",
      "name" : "Wahnsinn"
    },
    "-KdHSzTb63L9_6qw461X" : {
      "description" : "klettern klettern klettern",
      "name" : "8a Training"
    },
    "-KdI2OXgEO0GnIqDXaDT" : {
      "description" : "Bes",
      "name" : "Test"
    }
  },
  "users" : {
    "8faYwT4xp4SoXzU3HPnc2rIsqyp1" : {
      "email" : "email@web.de",
      "trainingIds" : {
        "-KdGH3qNKCWGnW1kebMj" : false,
        "-KdHSzTb63L9_6qw461X" : true
      },
      "username" : "didi"
    },
    "EWt2O16J9MasAwuoi92PQ3h66Bw2" : {
      "email" : "email@yahoo.de",
      "username" : "ChrisB"
    }
  }
}

As suggested by Clark, I already imported the rxjs map operator, but this does not solve the problem. The problem seems to be the list of trainingIds. These aren't returned as an array as expected but rather as an object. This can be seen in the console log.

Consolelog:

Any Idea how to solve this problem?

Community
  • 1
  • 1
Dreanaught
  • 71
  • 1
  • 10
  • This [answer](http://stackoverflow.com/a/41771379/6024090) may be helpful to you. – J. Adam Connor Feb 19 '17 at 23:02
  • @J.AdamConnor I already checked that. But merging the data isn't accually the problem. It's how angularfire returns my trainingIds. It's supposed to be an array that i can map on. But unfortunately an object ist returned – Dreanaught Feb 20 '17 at 15:45
  • My apologies, I misunderstood the question. It looks like you're passing multiple keys into the path. – J. Adam Connor Feb 20 '17 at 17:03

1 Answers1

1

Here's a working solution.

Check out a live Plunker of this code. You'll see that the console logs the users along with their resolved trainings.

const visibleUser$ = user$
  // Flatten the array of users to emit users individually.
  .mergeMap(val => val)
  // Project each user to an observable to get its trainings.
  .mergeMap(user => {
    // Array of observables fetching the trainings for the current user.
    const trainingsArr = user.trainingIds.map(trainingId => getTraining(trainingId));
    // Unwrap the trainings and merge them with the current user.
    return Rx.Observable.from(trainingsArr)
      .mergeAll()
      .toArray()
      .map(userTrainings => Object.assign(user, {trainings: userTrainings}));
  })
  .toArray()

visibleUser$.subscribe(val => console.log(val));

The console will log:

[{
  key: "key1",
  name: "user1",
  trainingIds: ["t_key1", "t_key3"],
  trainings: [  // Array of unwrapped trainings for this user
    {
      desc: "Desc 1",
      key: "t_key1",
      name: "Training 1"
    },
    {
      desc: "Desc 3",
      key: "t_key3",
      name: "Training 3"
    }
  ]
},
{
  key: "key2",
  name: "user2",
  // ...
}]

In the Plunkr, I have substituted the live Firebase calls with observables wrapping fake data. More specifically in my example you need to replace:

  • user$ with af.database.list(this.path)
  • getTraining(trainingId) with af.database.list(dev/trainings/${trainingIdsMap.$key})

Let me know if you have questions about the code.

Minor side note: in your code "af.database.list(${this.path})" can be replaced with af.database.list(this.path). No need for interpolation if all the string contains is a variable. :)

AngularChef
  • 13,797
  • 8
  • 53
  • 69
  • Thank you for your effort. Unfortunately the part that is not working out for me is still the same user.trainingIds returns a object and not an array. I need to map the obejct to an array since i believe that `map` doesn't work on objects. – Dreanaught Feb 20 '17 at 15:36
  • When you say "user.trainingIds returns a object and not an array", you mean in your code or mine? In the Plunkr I provided, you can clearly see that each user has a `user.trainings` property that is an array of trainings. Now if you mean YOUR code, I'm not sure what to say: you're mixing RxJS `.map()` and `Array.prototype.map()` and I'm not sure how your code can work. – AngularChef Feb 20 '17 at 17:43
  • `angularfire.list()` is returning an array at top-level. Everything down the road is object. I cant figure out how this should ever work. Long story short Your code definately returns an array, whereas mine returnes an object. `map` only works at arrays and i cant figure out how this could have worked out for the guy that asked the [question](http://stackoverflow.com/questions/38421551/joining-flattened-data) – Dreanaught Feb 21 '17 at 06:04