Angular - Websocket和$ rootScope.apply()

mar*_*k12 24 javascript websocket angularjs

我正在玩一个角度应用程序,它使用websocket与后端进行通信.我在使角度的数据绑定正常工作时遇到了一些麻烦.

在下面的示例中,我创建了一个创建websocket连接的服务.如果websocket收到消息,我只需将该消息推送到包含所有收到的消息的数组中.

在我的控制器中,我将该消息数组绑定到作用域,然后用于ng-repeat在部分视图中列出它们.

服务:

factory('MyService', [function() {

  var wsUrl = angular.element(document.querySelector('#ws-url')).val();
  var ws = new WebSocket(wsUrl);

  ws.onopen = function() {
    console.log("connection established ...");
  }
  ws.onmessage = function(event) {
      Service.messages.push(event.data);
  }   

  var Service = {};
  Service.messages = [];
  return Service;
}]);
Run Code Online (Sandbox Code Playgroud)

控制器:

controller('MyCtrl1', ['$scope', 'MyService', function($scope, MyService) {
  $scope.messages = MyService.messages;
}])
Run Code Online (Sandbox Code Playgroud)

部分:

<ul>
  <li ng-repeat="msg in messages">
      {{msg}} 
  </li>
</ul>
Run Code Online (Sandbox Code Playgroud)

但是这不能正常工作.当收到新消息并将其推送到阵列中时,应显示所有消息的列表不会更新.我预计它会因为角度双向数据绑定而更新.

我找到了一个解决方案,它通过将消息的推送包装到服务中的调用中来$rootScope.apply()实现:

ws.onmessage = function(event) {
  $rootScope.$apply(function() {
    Service.messages.push(event.data);
  });
}  
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  1. 这是角度的预期行为,如果我不使用,我的列表不会自动更新$rootScope.apply()吗?

  2. 为什么我甚至需要把它包起来$rootScope.apply()

  3. 是用$rootScope.apply()正确的方法来解决这个问题?

  4. $rootScope.apply()这个问题有更好的替代方案吗?

Jul*_*ano 34

  1. 是的,AngularJS的绑定是"基于回合的",它们只触发某些DOM事件和对$apply/的调用$digest.有一些有用的服务,比如$http$timeout为你做包装,但除此之外的任何东西都需要调用$apply或者$digest.

  2. 您需要让AngularJS知道您已经更改了绑定到范围的变量,以便它可以更新视图.还有其他方法可以做到这一点.

  3. 这取决于你需要什么.在包装代码时$apply(),AngularJS将代码包装在内部angularjs日志记录和异常处理中,当它结束时,它会传播$digest到所有控制器的作用域.在大多数情况下,包装$apply()是最好的方式,它会留下更多的门,以便将来最终使用的角度特征.这是正确的方法吗?参见下文.

  4. 如果您不使用Angular的错误处理,并且您知道您的更改不应传播到任何其他范围(根,控制器或指令),并且您需要优化性能,则可以$digest专门调用控制器$scope.这样脏检查就不会传播.否则,如果您不希望Angular捕获错误,但需要将脏检查传播到其他控制器/指令/ rootScope,您可以使用$包装,而不是在进行更改后apply调用$rootScope.$apply().

进一步参考: $applyvs$digest