2014-02-09 56 views
23

我目前正在玩一个角度的应用程序,它使用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; 
}]); 

控制器:

controller('MyCtrl1', ['$scope', 'MyService', function($scope, MyService) { 
    $scope.messages = MyService.messages; 
}]) 

部分:

<ul> 
    <li ng-repeat="msg in messages"> 
     {{msg}} 
    </li> 
</ul> 

然而,这无法正常工作。当收到新消息并将其推入数组中时,应显示所有消息的列表不会更新。我预计它会因为角度双向数据绑定而更新。

我发现一个解决方案,通过包装的消息推到一个呼叫服务工程$rootScope.apply()

ws.onmessage = function(event) { 
    $rootScope.$apply(function() { 
    Service.messages.push(event.data); 
    }); 
} 

我的问题是:

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

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

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

  4. 对于这个问题,$rootScope.apply()有更好的选择吗?

回答

31
  1. 是的,AngularJS的绑定 “回合制”,他们只对某些DOM事件和调用$apply/$digest火灾。有一些有用的服务,如$http$timeout可为您打包,但anything outside of that requires calls to either $apply or $digest

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

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

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

更多参考: $apply vs $digest

+0

所以,你说的任何东西'$ timeout'包裹,会自动应用正确的? – aacanakin

+1

是@aacanakin $ timeout的回调函数中的任何同步代码都会自动应用。 – Juliano