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

Damjan Pavlica
  • 31,277
  • 10
  • 71
  • 76
petrov.alex
  • 1,089
  • 2
  • 12
  • 20

1 Answers1

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]);
        });
      };
    }
  };
});
T J
  • 42,762
  • 13
  • 83
  • 138
Vojta
  • 23,061
  • 5
  • 49
  • 46
  • 1
    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. – duma Nov 16 '12 at 20:11
  • Cool. I didn't know you could inject the root scope. – heneryville Jul 01 '13 at 12:43
  • 4
    For `setTimeout` callbacks, use the `$timeout` service instead: https://coderwall.com/p/udpmtq – igortg Aug 13 '14 at 18:44