58

Updating the model property has no effect on the view when updating the model in event callback, any ideas to fix this?

This is my service:

angular.service('Channel', function() {        
    var channel = null; 

    return {        
        init: function(channelId, clientId) {
            var that = this;        

            channel = new goog.appengine.Channel(channelId);
            var socket = channel.open();

            socket.onmessage = function(msg) {
                var args = eval(msg.data);              
                that.publish(args[0], args[1]);
            };
        }       
    };
});

publish() function was added dynamically in the controller.

Controller:

App.Controllers.ParticipantsController = function($xhr, $channel) {
    var self = this;

    self.participants = [];     

    // here publish function is added to service
    mediator.installTo($channel); 

    // subscribe was also added with publish        
    $channel.subscribe('+p', function(name) { 
        self.add(name);     
    });                 

    self.add = function(name) {     
        self.participants.push({ name: name });     
    }
};

App.Controllers.ParticipantsController.$inject = ['$xhr', 'Channel'];

View:

<div ng:controller="App.Controllers.ParticipantsController">      
    <ul>
        <li ng:repeat="participant in participants"><label ng:bind="participant.name"></label></li>
    </ul>

    <button ng:click="add('test')">add</button>
</div>

So the problem is that clicking the button updates the view properly, but when I get the message from the Channel nothings happens, even the add() function is called

1 Answer 1

131

You are missing $scope.$apply().

Whenever you touch anything from outside of the Angular world, you need to call $apply, to notify Angular. That might be from:

  • xhr callback (handled by $http service)
  • setTimeout callback (handled by $defer service)
  • DOM Event callback (handled by directives)

In your case, do something like this:

// inject $rootScope and do $apply on it
angular.service('Channel', function($rootScope) {
  // ...
  return {
    init: function(channelId, clientId) {
      // ...
      socket.onmessage = function(msg) {
        $rootScope.$apply(function() {
          that.publish(args[0], args[1]);
        });
      };
    }
  };
});
Sign up to request clarification or add additional context in comments.

3 Comments

This was helpful, and once I thought about why I had to do this in the case of DOM/jQuery events, it made sense to me and I was able to accept it.
Cool. I didn't know you could inject the root scope.
For setTimeout callbacks, use the $timeout service instead: coderwall.com/p/udpmtq

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.