5

I want to know how to loop through the children of everyone. I'm using Firebase and AngularJS.

My firebase object looks like:

Firebase Objects

To me it looks like a dictionary, so from Getting a list of associative array keys I have tried

syncData('everyone').$bind($scope, 'everyone').then(function() {
  var keys = $scope.everyone.$getIndex();
  for (var key in $scope.everyone) {
   console.log("key : " + key + " value : " + $scope.everyone[key]);
  }
});

The log does contain the child objects, but it also includes all the methods. Like so

... Before this line is all the other methods.
key : $on value : function (a,c){if("loaded"==a&&b._loaded)return b._timeout(function(){c()}),void 0;if(!b._on.hasOwnProperty(a))throw new Error("Invalid event type "+a+" specified");b._on[a].push(c)} controllers.js:58
key : $off value : function (a,c){if(b._on.hasOwnProperty(a))if(c){var d=b._on[a].indexOf(c);-1!==d&&b._on[a].splice(d,1)}else b._on[a]=[];else b._fRef.off()} controllers.js:58
key : $auth value : function (a){var c=b._q.defer();return b._fRef.auth(a,function(a,b){null!==a?c.reject(a):c.resolve(b)},function(a){c.reject(a)}),c.promise} controllers.js:58
key : $getIndex value : function (){return angular.copy(b._index)} controllers.js:58
key : -JH45WOOAtnZfUZkrJb1 value : [object Object] controllers.js:58
key : -JH45YdfwptGv3y6UqyV value : [object Object] controllers.js:58
key : -JH45_zxptV_dmibyGzL value : [object Object] 

Is there a way I can get just the children?

I'm doing this because my code was designed to use an array, but Firebase discourage using arrays (for values that multiple people could change). So I'm trying to loop through the firebase dictionary and copy the objects into an array on the client side. So I don't have to change too much of my code.

Community
  • 1
  • 1
Lango
  • 2,995
  • 5
  • 26
  • 27

3 Answers3

4

UPDATE: As of AngularFire 0.8.x, one can use $asArray() to obtain a sorted array of the records and this answer is no longer necessary

The correct way to iterate values in an angularFire object is by using $getIndex(). You have this in your code above, but did not utilize it in the for loop.

Since you are already using the angularFire lib (syncData is the angularFire-seed service that uses angularFire), there is no need to worry about calling $apply() or any of the other complexities of coaxing data into Angular detailed in the previous answer (which is a good response for a raw Firebase/Angular implementation).

Instead, just change your for loop to iterate the keys instead of the angularFire instance:

syncData('everyone').$bind($scope, 'everyone').then(function() {
  var keys = $scope.everyone.$getIndex();

  // utilizing Angular's helpers
  angular.forEach(keys, function(key) {
     console.log(key, $scope.everyone[key]);
  });

  // or as a for loop
  for(var i=0, len = keys.length; i < len; i++) {
     console.log(keys[i], $scope.everyone[keys[i]]);
  }
});

To utilize the object in the DOM, use ng-repeat:

<ul>
  <li ng-repeat="(key,user) in everyone">{{key}}, {{user|json}}</li>
</ul>
Kato
  • 40,352
  • 6
  • 119
  • 149
  • Thanks. That is exactly what I was looking for. You were right to about having but not using $getIndex(), I'm not sure how I missed that. – Lango Mar 06 '14 at 01:52
  • I think you have a typo in your for loop. I think it should be `for(var i=0; i < keys.length; i++) {` – Lango Mar 06 '14 at 01:53
  • Thanks! Actually, it should read `var i=0, len = keys.length; i < len; i++`, which is considerably faster in some browsers due to the way scoping and lookups work. – Kato Mar 06 '14 at 04:56
  • The object returned by $asArray still includes an extra three children when iterated. Trying to figure this out myself. – Obie Oct 02 '14 at 15:21
  • @obie don't use the (key,user) notation with arrays; that's only for objects. – Kato Oct 02 '14 at 15:56
  • Please update your answer to reflect new Angular and Firebase – TheBlackBenzKid Oct 07 '16 at 10:59
0

The way I do it is pretty simple, you just push all the children into an array when they arrive like this.

Everyone.on('child_added', function(snap) {
  implementLogic(snap.val());
  $scope.$apply();
});

function implementLogic(person) {
  $scope.everyone.push(person);

  if (something) {
    $scope.todaysPeople.push(person);
  }
  if (something else) {
    $scope.peopleToTest.push(person);
  }
  ...
}

That leaves you with an array of the child objects you want.

Zack Argyle
  • 8,057
  • 4
  • 29
  • 37
  • I did see that in the docs. But I couldn't work out 1. How do I know when it is finished and 2.How do I kick off the rest of my logic? Right now I have a .then() method after my sync (I updated the question to show this) Does if(!$scope.$$phase) do this? – Lango Mar 03 '14 at 00:47
  • What kind of logic do you need to implement? The $apply call tells the DOM to update, since the firebase socket is outside the digest. – Zack Argyle Mar 03 '14 at 00:52
  • Once I have the entire array. The existing logic will then go through that 'everyone' array and create more arrays from it, like 'todaysPeople', 'peopleToTest' etc. So the 'everyone' array needs to have everyone in it before the code can use it. If that makes sense? – Lango Mar 03 '14 at 00:55
  • As an aside, https://github.com/angular/angular.js/wiki/When-to-use-$scope.$apply() tells my not to use if(!$scope.$$phase) $scope.$apply();? – Lango Mar 03 '14 at 00:55
  • @Lango Very true, you don't want to call it all over the place, this is an asynchronous event, so you should never be in a $$phase anyway, but it is a nice check. As far as the logic goes, could you put it inside here? I edited the answer as an example – Zack Argyle Mar 03 '14 at 00:59
  • Thanks for all your help so far. That looks close. The only thing I think will cause an issue, is the code tries to re-order the array before it creates the other arrays. I guess I'm looking for a 'finished_updating' event? – Lango Mar 03 '14 at 01:19
  • Oh right on. As far as I know there is no finished event. It is an open socket, so there isn't really an ending. – Zack Argyle Mar 03 '14 at 01:22
  • I guess that makes sense. Thanks for your help again. Looks like I'll have to do a bit of refactoring to make this work. – Lango Mar 03 '14 at 01:24
0

Use ng-fire-alarm with collection: true like this:

angular.module('demo', ['ng-fire-alarm']).controller('IndexCtrl', IndexCtrl);

function IndexCtrl ($scope) {
  var everyone = new Firebase(URL).child('everyone');
  everyone
  .$toAlarm({collection: true}) // will transform object into native array
  .$thenNotify(function(everyones){ // notify you on ANY changes 
    $scope.everyones = everyones;
  });
}
Tom Chen
  • 1,489
  • 2
  • 12
  • 17