4

I am learning AngularJS in conjunction with Firebase. I am really struggling with the on callback of Firebase and trying to update the $scope...

$apply already in progress <----

    var chat = angular.module('chat', []);
   chat.factory('firebaseService', function ($rootScope) {
  var firebase = {};
  firebase = new Firebase("http://gamma.firebase.com/myUser");
  return {
    on: function (eventName, callback) {
      firebase.on(eventName, function () {  
        var args = arguments;
        $rootScope.$apply(function () {
          callback.apply(firebase, args);
        });
      });
    },
    add: function (data) {
      firebase.set(data);
    }
  };
});

chat.controller ('chat', function ($scope, firebaseService) {
    $scope.messages = [];
    $scope.username;
    $scope.usermessage;              
    firebaseService.on("child_added",function(data){        
        $scope.messages.push(data.val());       
    });
    $scope.PushMessage = function(){
        firebaseService.add({'username':$scope.username,'usermessage':$scope.usermessage});   
    };
});

If I take the $rootscope.$apply out then it works as expected but it doesn't update the DOM on page load.

Thanks!

UPDATE

Solution 1 - Remove the $rootscope.$apply on the service and inject and apply the $timeout to the controller:

firebaseService.on('child_added',function(data){        
    $timeout(function(){
        $scope.messages.push(data.val());                       
    },0);
});

Solution 2 - Implement a "SafeApply" method (thanks to Alex Vanston):

$scope.safeApply = function(fn) {
        var phase = this.$root.$$phase;
        if(phase == '$apply' || phase == '$digest') {
            fn();
        } else {
            this.$apply(fn);
        }
    };

Although these both work and are not much code I feel they are too hacky. Isn't there some offical Angular manner in which to handle Async callbacks?

Another great example I found for a similar situation: HTML5Rocks - AngularJS and Socket.io

3
  • 3
    Firebase founder here -- this looks like more of an Angular question of which I only know a little. Sorry to not be more helpful! In the future we hope to have some nice reference examples and possibly a library to support Angular. Commented Oct 25, 2012 at 16:52
  • 2
    Also, you might want to check the Angular Google Group. There's already been some Firebase discussion there, such as this thread: groups.google.com/forum/#!topic/angular/sJzqb6UMVMQ Commented Oct 25, 2012 at 21:30
  • Very nice to hear from you! I will follow what the topic suggests and try to keep Firebase as a service. Looking forward to your examples and to playing with Firebase some more. Thanks! Commented Oct 26, 2012 at 8:30

1 Answer 1

8

Solution 1 - Remove the $rootscope.$apply on the service and inject and apply the $timeout to the controller:

firebaseService.on('child_added',function(data){        
    $timeout(function(){
        $scope.messages.push(data.val());                       
    },0);
});

Solution 2 - Implement a "SafeApply" method (thanks to Alex Vanston):

$scope.safeApply = function(fn) {
        var phase = this.$root.$$phase;
        if(phase == '$apply' || phase == '$digest') {
            fn();
        } else {
            this.$apply(fn);
        }
    };
Sign up to request clarification or add additional context in comments.

2 Comments

I like this solution, however I'm making one adjustment. Before calling fn(), make sure it's not null or undefined. if (fn) { fn(); }
Checking the phase value makes me nervous. I would use extreme caution using this method. I wonder if this would be a good time to use $scope.$evalAsync() (which I've used from time to time on event listeners that required a $timeout).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.