【问题标题】:AngularJS does not refresh view with socket.ioAngularJS 不使用 socket.io 刷新视图
【发布时间】:2016-07-05 10:40:33
【问题描述】:

我有一个带有以下设置的 node.js 服务器应用程序:

    var express = require('express');
    var io = require('socket.io');
    var http = require('http');
    var app = express();

    app.use(express.static('public'));

    var server = http.createServer(app);
    io = io.listen(server); 

    io.on('connection', function(socket){
        console.log('a user is connected');
    });

    device.on('disconnect', function() {
        console.log('we got disconnected! :( '); 
        io.emit('deviceDisconnection');    
    });

这里的设备是蓝牙连接的设备。 而且,在客户端,我有一个 AngularJS 应用程序,在应用程序的控制器中声明了以下事件订阅:

var appFront = angular.module(appFront , [])
.controller('mainController', ['$scope', '$http', function($scope,$http) {    
var socket = io();  
    socket.on('deviceDisconnection', function(){
      $scope.connected = 'TRUE';
    });
}]);

当然,在视图中,我有以下绑定:

<div class="row">
  <p>{{connected}}</p>
</div>

问题在于更改并不是真正“实时”的。我的意思是,connected 值可能在范围内发生了更改,但需要在页面上执行操作(按钮 clic)以使用正确的值更新视图。如果我没有在页面上执行任何操作,则不会刷新值。

我可能忘记了什么..但我不知道是什么..

【问题讨论】:

    标签: angularjs node.js socket.io


    【解决方案1】:

    Socket.io 事件是 AngularJS 的外部事件,因此您需要警告 AngularJS 刚刚发生了变化:

    var appFront = angular.module(appFront , [])
    .controller('mainController', ['$scope', '$http', function($scope,$http) {    
    var socket = io();  
        socket.on('deviceDisconnection', function(){
          $scope.$applyAsync(function () {
             $scope.connected = 'TRUE';
          });
        });
    }]);
    

    现在这可能很麻烦,所以你可以创建一个工厂来为你处理它

    app.factory('socket', function ($rootScope) {
      var socket = io.connect();
      return {
        on: function (eventName, callback) {
          socket.on(eventName, function () {  
            var args = arguments;
            $rootScope.$apply(function () {
              callback.apply(socket, args);
            });
          });
        },
        emit: function (eventName, data, callback) {
          socket.emit(eventName, data, function () {
            var args = arguments;
            $rootScope.$apply(function () {
              if (callback) {
                callback.apply(socket, args);
              }
            });
          })
        }
      };
    });
    

    您可以在here 中查看有关该代码 sn-p 的更多信息,感谢作者 Brian Ford。

    编辑

    正如maurycy所说,既然Angular 1.3最好用$applyAsync,而且上面的sn-p是2012年写的,有点过时了,所以更好的版本应该是这样的

    app.factory('socket', function ($rootScope) {
      var socket = io.connect();
      return {
        on: function (eventName, callback) {
          socket.on(eventName, function () {  
            var args = arguments;
            $rootScope.$applyAsync(function () {
              callback.apply(socket, args);
            });
          });
        },
        emit: function (eventName, data, callback) {
          socket.emit(eventName, data, function () {
            var args = arguments;
            $rootScope.$applyAsync(function () {
              if (callback) {
                callback.apply(socket, args);
              }
            });
          })
        }
      };
    });
    

    【讨论】:

    • 我的错,错过了。好东西 :) 坦率地说,这是最好的解决方案,将 socket.io 包装为 Angular 服务使其更容易
    • @maurycy 为作者添加了更多可见的学分,并添加了一个版本,您建议使用$applyAsync
    【解决方案2】:

    Angular 不知道其上下文之外的变化。要解决这个问题,您应该像这样使用$apply

        var appFront = angular.module(appFront , []).controller('mainController', ['$scope', '$http', function($scope,$http) {    
            var socket = io();  
    
            socket.on('deviceDisconnection', function(){
                $scope.$apply(function () {
                    $scope.connected = 'TRUE';
                });
    
            });
    
        }]);
    

    $scope.$apply() 将执行作为参数传递的函数,然后调用 $scope.$digest() 来更新任何绑定或观察者。

    【讨论】:

    • 从 Angular 1.3 开始使用 $scope.$applyAsync 更适合此目的
    【解决方案3】:

    因为socket事件在AngularJS之外,所以需要消化作用域:

    socket.on('deviceDisconnection', function(){
          $scope.connected = 'TRUE';
          $scope.$digest();
    });
    

    您可以阅读this interesting article 以真正了解为什么需要这样做

    【讨论】:

    • 不要把$scope.$digest()放在你的代码中,如果应用程序增长并且$digest会更频繁地被触发,它会抛出JS错误$digest in progres使用$scope.$apply或更新版本的angularjs $scope.$applyAsync
    • 这就是为什么在 angular 1.3 之前人们在不指定时间的情况下使用 $timeout 欺骗它,从 angular 1.3 开始,applyAsync 就是为此设计的
    • 好的。我不知道 applyAsync。
    猜你喜欢
    • 1970-01-01
    • 2014-12-08
    • 1970-01-01
    • 2023-03-11
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-09
    相关资源
    最近更新 更多