我目前正在玩一个使用websocket与后端通信的有角度的应用程序。我在使angular的数据绑定正常工作时遇到了一些麻烦。
在下面的示例中,我创建了一个服务,该服务创建了websocket连接。如果websocket收到一条消息,我只需将该消息推送到包含所有收到消息的数组中即可。
在我的控制器中,我将该消息数组绑定到作用域,然后用于ng-repeat在我的局部视图中列出所有消息。
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; }]);
控制器:
controller('MyCtrl1', ['$scope', 'MyService', function($scope, MyService) { $scope.messages = MyService.messages; }])
部分:
<ul> <li ng-repeat="msg in messages"> {{msg}} </li> </ul>
但是,这不能正常工作。收到新消息并将其推送到数组时,应显示所有消息的列表不会得到更新。我期望它会由于角度双向数据绑定而被更新。
我找到了一种解决方案,该解决方案可以通过将消息推送包装到服务中的调用中来$rootScope.apply()工作:
$rootScope.apply()
ws.onmessage = function(event) { $rootScope.$apply(function() { Service.messages.push(event.data); }); }
我的问题是:
这是我不使用列表时不会自动更新的angular的预期行为$rootScope.apply()吗?
为什么什至需要将其包装$rootScope.apply()?
是否使用$rootScope.apply()正确的方法来解决此问题?
有更好的替代方法来$rootScope.apply()解决此问题吗?
是的,AngularJS的绑定是“基于回合的”,它们仅在某些DOM事件以及对$apply/的调用时触发$digest。有一些有用的服务,例如$http和$timeout,它们可以为您做包装,但除此之外的任何东西都需要调用$apply或$digest。
$apply
$digest
$http
$timeout
您需要让AngularJS知道您已经更改了绑定到范围的变量,以便它可以更新视图。不过,还有其他方法可以做到这一点。
这取决于您的需求。将代码包装到时$apply(),AngularJS将代码包装到内部angularjs日志记录和异常处理中,当结束时,它将传播$digest到控制器的所有作用域。在大多数情况下,包裹$apply()是最好的方法,它为将来可能要使用的角度功能留出了更多的空间。这是正确的方法吗?参见下文。
$apply()
如果您不使用Angular的错误处理,并且知道更改不应传播到任何其他范围(root,控制器或指令),并且需要优化性能,则可以$digest专门调用控制器的$scope。这样脏检查不会传播。否则,如果您不希望Angular捕获错误,而是需要进行脏检查以传播到其他控制器/指令/ rootScope,则可以使用$进行包装,而不必使用$进行包装,而apply只需$rootScope.$apply()进行更改即可。
$scope
apply
$rootScope.$apply()
进一步参考: $applyvs$digest