Search code examples
javascriptfirebasefirebase-realtime-databaseangularfire

Angularfire: Getting nested data from a $firebaseObject in controller


Firebase newbie here.

In my controller I have a variable called currentChallenge that contains the following $firebaseObject:

$$conf: {sync: {…}, ref: X, binding: f, listeners: Array(0)}
$id: "gRH2EWGQB6dbejKYap40Z0ea0nz2"
$priority: null
-Kuy9HAfvVIhPhtQraLy:
  challenge: {...}
  challenger: "gRH2EWGQB6dbejKYap40Z0ea0nz2"
  pending: true
  timestamp: 1506428920780
  __proto__: Object
__proto__: Object

In Firebase it looks like: enter image description here

The code from my controller is as follows:

Challenge.lastChallenge(uid, challengeUserId).$loaded()
  .then(function (currentChallenge) {
    if (currentChallenge.challenger == uid) {
      // stuff happens
    }

This is calling the Challenge service method:

lastChallenge: function (uid1, uid2) {
   var path = uid1 < uid2 ? uid1 + '/' + uid2 : uid2 + '/' + uid1;
   return $firebaseObject(userChallengeRef
     .child(path)
     .orderByChild('timestamp')
     .limitToLast(1));
},

The data I am trying to access is challenger, however I am unsure how to do this as $value doesn't seem to work on objects.

I can't put -Kuy9HAfvVIhPhtQraLy because this is subject to change depending on which challenge I'm looking at. It is also not always going to be the same value as $id.


Solution

  • You're executing a query against Firebase:

    userChallengeRef
      .child(path)
      .orderByChild('timestamp')
      .limitToLast(1))
    

    When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.

    That means that you'll need to use $firebaseArray to get the result(s) and iterate over it/them. When using the regular JavaScript SDK, it'll be something like this:

    lastChallenge: function (uid1, uid2) {
       var path = uid1 < uid2 ? uid1 + '/' + uid2 : uid2 + '/' + uid1;
       return new Promise(function(resolve, reject) {
         userChallengeRef
           .child(path)
           .orderByChild('timestamp')
           .limitToLast(1))
           .once("value").then(function(snapshot) {
             snapshot.forEach(function(child) {
               resolve($firebaseObject(child.ref));
             });
           });
    }
    

    Invoke it as:

    Challenge.lastChallenge(uid, challengeUserId).then(function(firebaseObject){ 
      firebaseObject.$loaded().then(function (currentChallenge) {
        if (currentChallenge.challenger == uid) {
          // stuff happens
        }
    

    Note that I don't think using AngularFire helps you at all here. You should only be using AngularFire services for things that you bind directly to your Angular UI. For all other operations, just use the Firebase JavaScript SDK directly. It keeps your code simpler, and makes it easier to reason about your app's logic vs its UI.