Hugh Hou
Hugh Hou

Reputation: 2384

angularFire 3 way data binding won't update an function

I have a firebaseObject (MyFirebaseService.getCurrentUser()) bind to $scope.user. After binding successful, I loop tho the object to see if the object contain "associatedCourseId" equal to some value ($stateParams.id). If does, the $scope.finishLessonCount count up. The problem is, when I add new Object inside the firebaseObject (that bindto user) via other page OR inside firebase, the finishLessonCount value won't change as what I expect for 3 way binding. I need to refresh the page to see the finishLessonCount reflect the true value. What is wrong? I want the finishLessonCount change using the compare function as I add more finishedLessons into the firebaseObject. Please see code below:

MyFirebaseService.getCurrentUser().$bindTo($scope, "user").then(function(){

        for (var key in $scope.user.finishedLessons) {
            if ($scope.user.finishedLessons.hasOwnProperty(key)) {

                if ($scope.user.finishedLessons[key].associatedCourseId == $stateParams.id) {
                    $scope.finishLessonCount++;
                }
            }
        };
        console.log ($scope.finishLessonCount);
    });

UPDATE 1 according to @Kato solution: I decide to use Extending firebaseOject way to solute this problem. But still, it does not. I did not use factory here to simplify thing since I need to pass in courseId to do the operation. Here is my code:

       function countLessons(lessons, courseId) {
       var count = 0;
       for(var key in lessons) {
          if( lessons[key].associatedCourseId ==  courseId) {
             count++;
          }
       }
       return count;
    }

    var UserWithLessonsCounter = $firebaseObject.$extend({
      $$updated: function(snap) {
         var changed = $firebaseObject.prototype.$$updated.call(this, snap);
         this.lessonCount = countLessons(this.finishedLessons, $stateParams.id);
      }
    });

    var refTemp = new Firebase($rootScope.baseUrl + "users/" + $rootScope.userId);
    var userTemp = new UserWithLessonsCounter(refTemp);

    userTemp.$bindTo($scope, "userTemp").then(function(){
        console.log($scope.userTemp);
    });
    userTemp.$watch(function() {
      console.log("Does this run at all? " + $scope.userTemp.lessonCount);
   });

I update the user object, the lessonCount value did not change unless I refresh the page. And the console.log inside $watch did not run at all. What is wrong?

Upvotes: 0

Views: 251

Answers (1)

Kato
Kato

Reputation: 40582

The promise returned by $bindTo is called exactly once. It's not an event listener. You can't listen to this to get updated each time there is a change.

Please read the guide, start to finish, and read about Angular's $watch method before continuing down this route, as with some fundamental knowledge, this should not have been your first instinct.

A beginner approach would be to utilize $watch:

MyFirebaseService.getCurrentUser().$bindTo($scope, "user");

$scope.$watch('user', function() {
   for (var key in $scope.user.finishedLessons) {
      if ($scope.user.finishedLessons.hasOwnProperty(key)) {
        if ($scope.user.finishedLessons[key].associatedCourseId == $stateParams.id) {
          $scope.finishLessonCount++;
        }
      }
   };
   console.log ($scope.finishLessonCount);
});

Or, having familiarized with the AngularFire API, one might pick $scope.user.$watch() in place of the scope method, which would prove more efficient.

Having written a large portion of the AngularFire code, I would pick the $extend tool, which was added precisely for use cases like this:

// making some assumptions here since you haven't included
// the code for your firebase service, which does not seem SOLID
app.factory('UserWithLessonsCounter', function($firebaseObject) {
   return $firebaseObject.$extend({
      $$updated: function(snap) {
         var changed = $firebaseObject.prototype.$$updated.call(this, snap);
         this.lessonCount = countLessons(this.finishedLessons);
         return changed;
      }
   });
});

function countLessons(lessons) {
   var count = 0;
   for(var key in lessons) {
      if( lessons.hasOwnProperty(key) ) {
         count++;
      }
   }
   return count;
}

And now in your controller:

app.controller('...', function($scope, UserWithLessonsCounter) {
   var ref = new Firebase(...);
   var user = new UserWithLessonCounter(ref);
   user.$bindTo($scope, 'user');

   user.$watch(function() {
      console.log($scope.user.lessonCount);
   });
});

Upvotes: 1

Related Questions