3

I am pulling out one value from every object in Firebase, picUrl (the url of a picture) and storing that in a scope array variable, $scope.bricks. How do I make it so $scope.bricks updates everytime Firebase is updated with a new object, and therefore new picUrl? Thanks in advance!

angular.module('noorApp').controller('MainCtrl', function ($scope, $http, $firebase) {
    var sync = $firebase(ref);
    var firebaseObj = sync.$asObject();
    $scope.bricks = [];

    firebaseObj.$loaded().then(function(){
        angular.forEach(firebaseObj.products, function(value, key){
            $scope.bricks.push({src: value.picUrl});
        });
    });
});

EDIT:

I should have posted how I'm using $scope.bricks in the DOM.

<div class="masonry-brick" ng-repeat="brick in bricks">
  <img ng-src="{{ brick.src }}">
</div>

The issue is that while firebaseObj is synced with Firebase, $loaded() is only running once.
Thanks.

user3527354
  • 586
  • 1
  • 7
  • 20
  • You can call `$scope.$apply` after the `push`. But there's probably something wrong in your design, because AngularFire's array class is supposed to do that for you automatically. – Frank van Puffelen Dec 11 '14 at 22:19

2 Answers2

4

As I said in my comment, you can probably get it working by:

    firebaseObj.$loaded().then(function(){
        angular.forEach(firebaseObj.products, function(value, key){
            $scope.bricks.push({src: value.picUrl});
            $scopy.$apply();
        });
    });

Update: the above doesn't work, see OP's comment below.

To learn more about $scope.$apply read this article: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html

But even though this may fix your current issue, you'll run into other problems later. The reason for this is that you're creating an "unmanaged array" of bricks. AngularFire has quite some code to ensure that Firebase's order collections and AngularJS's two-way data-bound arrays play nice together.

For this reason it is probably better if you set up a separate sync for your array of products:

angular.module('noorApp').controller('MainCtrl', function ($scope, $http, $firebase) {
    var sync = $firebase(ref);
    var firebaseObj = sync.$asObject();
    $scope.bricks = $firebase(ref.child('products')).$asArray();    
});

With this setup, AngularFire will call $apply() automaticaly when the array items initially load or are modified afterwards.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks for the response Frank! Some comments: 1. $scope.$apply() would not work here. Had I done something like this (not using AngularFire) http://jsfiddle.net/k2g7kfe4/ it would work. Otherwise, I will get a digest loop error. 2. Your separate sync solution is absolutely correct. $scope.bricks array will sync with Firebase but this was true in my code as well. firebaseObj synced with Firebase. The issue was that firebaseObj.$loaded() only runs once, not everytime firebaseObj syncs. Then I realized I should just reference firebaseObj in the DOM and not try and create a new object. – user3527354 Dec 12 '14 at 17:57
  • OK, thanks for the update. `$loaded` is documented to only fire for the initial data (https://github.com/firebase/angularfire/blob/master/src/FirebaseObject.js#L106). Are you sure you shouldn't be using `$watch` (https://github.com/firebase/angularfire/blob/master/src/FirebaseObject.js#L157)? – Frank van Puffelen Dec 12 '14 at 18:18
  • See how I fixed the problem below. Thinking about it now it was a stupid problem. I was overcomplicating the issue when I didn't need to. Monday morning qb! – user3527354 Dec 12 '14 at 18:27
2

After reading Frank's answer, I realized my problem was with my html. I was trying to create a new variable from my synced variable when I didn't need to. I can just reference the synced variable directly.

I should have modified my ng-repeat variable rather than my model. firebaseObj is synced with Firebase, so lets put that on the $scope.

angular.module('noorApp').controller('MainCtrl', function ($scope, $http, $firebase) {
    var sync = $firebase(ref);
    $scope.firebaseObj = sync.$asObject();
});

Now in the DOM, I should just reference firebaseObj:

<div class="masonry-brick" ng-repeat="brick in firebaseObj.products">
   <img ng-src="{{ brick.picUrl }}">
</div>

Or as Frank said, we can just pull the products document from Firebase:

angular.module('noorApp').controller('MainCtrl', function ($scope, $http, $firebase) {
    $scope.bricks = $firebase(ref).products.$asArray();
});

In this case, the html would look like this:

<div class="masonry-brick" ng-repeat="brick in bricks">
  <img ng-src="{{ brick.picUrl }}">
</div>

The point is that by referencing synced variable directly, I don't need $loaded() to run!

Thanks Frank.

user3527354
  • 586
  • 1
  • 7
  • 20