Angular - Websocket и $rootScope.apply()
В настоящее время я играю с приложением angular, которое использует websocket для связи с бэкэнд. У меня возникли проблемы с правильной работой привязки данных angular.
В приведенном ниже примере я создал службу, которая создает соединение с 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>
Это, однако, работает неправильно. Когда новое сообщение принимается и вводится в массив, список, который должен отображать все сообщения, не обновляется. Я ожидал, что он будет обновлен из-за angular двусторонней привязки данных.
Я нашел одно решение, которое работает путем переноса нажатия сообщения в вызов $rootScope.apply()
в службе:
ws.onmessage = function(event) {
$rootScope.$apply(function() {
Service.messages.push(event.data);
});
}
Мои вопросы:
-
Является ли это ожидаемым поведением angular, что мой список не обновляется автоматически, если я не использую $rootScope.apply()
?
-
Почему мне даже нужно обернуть его в $rootScope.apply()
?
-
Использует $rootScope.apply()
правильный способ решить эту проблему?
-
Есть ли лучшие альтернативы $rootScope.apply()
для этой проблемы?
Ответы
Ответ 1
-
Да, привязки AngularJS являются "пошаговыми", они запускаются только при определенных событиях DOM и при звонках на $apply
/$digest
. Для этого есть несколько полезных сервисов, таких как $http
и $timeout
, но что-либо за пределами этого требует вызовов либо $apply
, либо $digest
.
-
Вы должны позволить AngularJS знать, что вы изменили переменную, связанную с областью, чтобы она могла обновлять представление. Есть и другие способы сделать это, хотя.
-
Это зависит от того, что вам нужно. Когда вы завершаете свой код в $apply()
, AngularJS обертывает ваш код во внутреннем журнале и обработке исключений, а когда он заканчивается, он распространяет $digest
на все области вашего контроллера. В большинстве случаев упаковка в $apply()
является наилучшим способом, она оставляет больше дверей открытыми для будущих функций angular, которые могут закончиться использованием. Правильно ли это? Читайте ниже.
-
Если вы не используете обработку ошибок angular, и знаете, что ваши изменения не должны распространяться на другие области (корневые, контроллеры или директивы), и вам нужно оптимизировать производительность, вы можете позвонить $digest
на вашем контроллере $scope
. Таким образом, грязная проверка не распространяется. В противном случае, если вы не хотите, чтобы ошибки были пойманы с помощью Angular, но вам нужна грязная проверка для распространения на другие контроллеры/директивы/rootScope, вы можете вместо обертывания с помощью $apply
просто позвонить $rootScope.$apply()
после внесения изменений.
Дальнейшая ссылка:
$apply
vs $digest