52

AngularJSでsocket.ioを使用したい。私は次の工場を見つけました:

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);
                    }
                });
            })
        }
    };

そしてそれは次のようにコントローラーで使用されます:

function MyCtrl($scope, socket) {
    socket.on('message', function(data) {
        ...
    });
};

問題は、コントローラーにアクセスするたびに別のリスナーが追加されるため、メッセージを受信すると、そのメッセージが複数回処理されることです。

socket.ioをAngularJSと統合するためのより良い戦略は何でしょうか?

編集:私は工場で何も返さず、そこでリスニングを行ってから、コントローラーで$rootScope。$broadcastと$scope。$onを使用できることを知っていますが、それは良い解決策のようには見えません。

EDIT2:ファクトリに追加されました

init: function() {
            socket.removeAllListeners();
}

そして、socket.ioを使用する各コントローラーの先頭で呼び出します。

まだ最善の解決策のようには感じていません。

4

13 に答える 13

52

コントローラが破棄されるたびに、ソケットリスナーを削除します。$destroy次のようにイベントをバインドする必要があります。

function MyCtrl($scope, socket) {
    socket.on('message', function(data) {
        ...
    });

    $scope.$on('$destroy', function (event) {
        socket.removeAllListeners();
        // or something like
        // socket.removeListener(this);
    });
};

詳細については、angularjsのドキュメントを確認してください。

于 2013-01-17T23:26:12.793 に答える
8

スコープをまとめてブロードキャストを監視することで、最小限の作業でこれを処理できる$destroy場合があります。その場合は、そのスコープのコンテキストで追加されたリスナーをソケットから削除するだけです。警告:以下はテストされていません。実際のコードよりも擬似コードのように扱います。:)

// A ScopedSocket is an object that provides `on` and `emit` methods,
// but keeps track of all listeners it registers on the socket.
// A call to `removeAllListeners` will remove all listeners on the
// socket that were created via this particular instance of ScopedSocket.

var ScopedSocket = function(socket, $rootScope) {
  this.socket = socket;
  this.$rootScope = $rootScope;
  this.listeners = [];
};

ScopedSocket.prototype.removeAllListeners = function() {
  // Remove each of the stored listeners
  for(var i = 0; i < this.listeners.length; i++) {
    var details = this.listeners[i];
    this.socket.removeListener(details.event, details.fn);
  };
};

ScopedSocket.prototype.on = function(event, callback) {
  var socket = this.socket;
  var $rootScope = this.$rootScope;

  var wrappedCallback = function() {
    var args = arguments;
    $rootScope.$apply(function() {
      callback.apply(socket, args);
    });
  };

  // Store the event name and callback so we can remove it later
  this.listeners.push({event: event, fn: wrappedCallback});

  socket.on(event, wrappedCallback);
};

ScopedSocket.prototype.emit = function(event, data, callback) {
  var socket = this.socket;
  var $rootScope = this.$rootScope;

  socket.emit(event, data, function() {
    var args = arguments;
    $rootScope.$apply(function() {
      if (callback) {
        callback.apply(socket, args);
      }
    });
  });
};

app.factory('Socket', function($rootScope) {
  var socket = io.connect();

  // When injected into controllers, etc., Socket is a function
  // that takes a Scope and returns a ScopedSocket wrapping the
  // global Socket.IO `socket` object. When the scope is destroyed,
  // it will call `removeAllListeners` on that ScopedSocket.
  return function(scope) {
    var scopedSocket = new ScopedSocket(socket, $rootScope);
    scope.$on('$destroy', function() {
      scopedSocket.removeAllListeners();
    });
    return scopedSocket;
  };
});

function MyController($scope, Socket) {
  var socket = Socket($scope);

  socket.on('message', function(data) {
     ...
  });
};
于 2013-01-18T06:29:15.557 に答える
5

受け入れられた回答にコメントを追加しますが、できません。だから、私は返信を書きます。私は同じ問題を抱えていました、そして私が見つけた最も簡単で最も簡単な答えはあなたがここで見つけることができるものです、別の投稿で、 michaeljoserによって提供されました。

便宜上、以下にコピーします。

ファクトリにremoveAllListenersを追加し(以下を参照)、各コントローラーに次のコードを含める必要があります。

$scope.$on('$destroy', function (event) {
socket.removeAllListeners();
});

更新されたソケットファクトリ:

var socket = io.connect('url');
    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);
                    }
                });
            })
        },
      removeAllListeners: function (eventName, callback) {
          socket.removeAllListeners(eventName, function() {
              var args = arguments;
              $rootScope.$apply(function () {
                callback.apply(socket, args);
              });
          }); 
      }
    };
});

それは私の一日を救った、私はそれが他の誰かに役立つことを願っています!

于 2014-05-23T13:48:53.460 に答える
2

以下のように、サービスまたはファクトリで関数を作成します。

unSubscribe: function(listener) {
    socket.removeAllListeners(listener);
}

次に、以下のような「$destroy」イベントの下でコントローラーを呼び出します。

$scope.$on('$destroy', function() {
    yourServiceName.unSubscribe('eventName');
});

それは解決します

于 2016-02-22T17:28:38.313 に答える
1

これを読む前に、私はちょうど同様の問題を解決しました。私はそれをすべてサービスで行いました。

.controller('AlertCtrl', ["$scope", "$rootScope", "Socket", function($scope, $rootScope, Socket) {
    $scope.Socket = Socket;
}])

// this is where the alerts are received and passed to the controller then to the view
.factory('Socket', ["$rootScope", function($rootScope) {
    var Socket = {
        alerts: [],
        url: location.protocol+'//'+location.hostname+(location.port ? ':'+location.port: ''),
        // io is coming from socket.io.js which is coming from Node.js
        socket: io.connect(this.url)
    };
    // set up the listener once
    // having this in the controller was creating a
    // new listener every time the contoller ran/view loaded
    // has to run after Socket is created since it refers to itself
    (function() {
        Socket.socket.on('get msg', function(data) {
            if (data.alert) {
                Socket.alerts.push(data.alert);
                $rootScope.$digest();
            }
        });
    }());
    return Socket;
}])
于 2014-06-06T20:48:08.843 に答える
1

さまざまな方法を試しましたが、期待どおりに機能しませんでした。私のアプリでは、とのsocket両方でファクトリを使用しています。ユーザーが別のビューに切り替えたときに、によって生成された重複イベントのみを削除して実行を終了したいので、関数を使用できません。代わりに、ファクトリ内で重複を作成しないようにするためのより良い方法を発見しました。MainControllerGameControllerGameControllerMainControllerremoveAllListenerssocket

app.factory('socket', function ($rootScope) {
  var socket = io.connect();

  function on(eventName, callback) {
    socket.on(eventName, function () {
      var args = arguments;

      $rootScope.$apply(function () {
        callback.apply(socket, args);
      });
    });

    // Remove duplicate listeners
    socket.removeListener(eventName, callback);
  }

  function emit(eventName, data, callback) {
    socket.emit(eventName, data, function () {
      var args = arguments;

      $rootScope.$apply(function () {
        if (callback) {
          callback.apply(socket, args);
        }
      });
    });

    // Remove duplicate listeners
    socket.removeListener(eventName, callback);
  }

  return {
    on: on,
    emit: emit
  };
}
于 2016-01-10T12:46:17.507 に答える
0

app.factoryを実行する代わりに、次のようなサービス(シングルトン)を作成します。

var service = angular.module('socketService', []);
service.factory('$socket', function() {
    // Your factory logic
});

次に、サービスをアプリに挿入し、$rootScopeと同じようにコントローラーで使用できます。

これは、私がこれをどのように設定したかについてのより完全な例です。

// App module
var app = angular.module('app', ['app.services']);

// services
var services = angular.module('app.services', []);

// Socket service
services.factory('$socket', ['$rootScope', function(rootScope) {

    // Factory logic here

}]);

// Controller
app.controller('someController', ['$scope', '$socket', function(scope, socket) {

    // Controller logic here

}]);
于 2013-01-17T23:06:05.083 に答える
0

上記のブランドンの答えを拡張して、1)要素に残される。$$ hashKeyのような角度タグを削除し、2)socketsof('..')。on( ' .. '

(function (window, app, undefined) {
    'use strict';


    var ScopedSocket = function (socket, $rootScope) {
        this.socket = socket;
        this.$rootScope = $rootScope;
        this.listeners = [];
        this.childSockets = [];
    };

    ScopedSocket.prototype.removeAllListeners = function () {
        var i;

        for (i = 0; i < this.listeners.length; i++) {
            var details = this.listeners[i];
            this.socket.removeListener(details.event, details.fn);
        }

        for (i = 0; i < this.childSockets.length; i++) {
            this.childSockets[i].removeAllListeners();
        }
    };

    ScopedSocket.prototype.on = function (event, callback) {
        var socket = this.socket;
        var $rootScope = this.$rootScope;

        this.listeners.push({event: event, fn: callback});

        socket.on(event, function () {
            var args = arguments;
            $rootScope.$apply(function () {
                callback.apply(socket, args);
            });
        });
    };

    ScopedSocket.prototype.emit = function (event, data, callback) {
        var socket = this.socket;
        var $rootScope = this.$rootScope;

        socket.emit(event, angular.fromJson(angular.toJson(data)), function () {
            var args = arguments;
            $rootScope.$apply(function () {
                if (callback) {
                    callback.apply(socket, args);
                }
            });
        });
    };

    ScopedSocket.prototype.of = function (channel) {
        var childSocket = new ScopedSocket(this.socket.of(channel), this.$rootScope);

        this.childSockets.push(childSocket);

        return childSocket;
    };


    app.factory('Socket', ['$rootScope', function ($rootScope) {
        var socket = $rootScope.socket;

        return function(scope) {
            var scopedSocket = new ScopedSocket(socket, $rootScope);
            scope.$on('$destroy', function() {
                scopedSocket.removeAllListeners();
            });
            return scopedSocket;
        };
    }]);
})(window, window.app);
于 2013-04-19T01:29:29.100 に答える
0

以下のコードのようなものを使用します。socketsServiceは一度だけインスタンス化され、Angularが$onのGCを処理すると思います

$ broadcast / $ onが気に入らない場合は、Angular用のもう少し堅実なメッセージバスの実装が利用可能です...

app.service('socketsService', ['$rootScope', function ($rootScope) {
    var socket = window.io.connect();

    socket.on('info', function(data) {
        $rootScope.$broadcast("info_received", data);
    });

    socket.emit('ready', "Hello");
}]);

app.controller("infoController",['$scope',
    function ($scope) {
        $scope.$root.$on("info_received", function(e,data){
            console.log(data);
        });
        //...
    }]);

app.run(
    ['socketsService',
        function (socketsService) {
        //...
    }]);
于 2013-08-09T21:28:49.310 に答える
0

リスナーがすでに存在するかどうかを確認することで、この問題を解決しました。すべて同時にロードされる複数のコントローラーがある場合(すべてsocketIOを利用する異なるページモジュールを考えてください)、登録されているすべてのリスナーを削除する$destroyと、破棄されたコントローラーとまだロードされているすべてのコントローラーの両方の機能が損なわれます。

app.factory("SocketIoFactory", function ($rootScope) {
    var socket = null;
    var nodePath = "http://localhost:12345/";

    function listenerExists(eventName) {
        return socket.hasOwnProperty("$events") && socket.$events.hasOwnProperty(eventName);
    }

    return {
        connect: function () {
            socket = io.connect(nodePath);
        },
        connected: function () {
            return socket != null;
        },
        on: function (eventName, callback) {
            if (!listenerExists(eventName)) {
                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);
                    }
                });
            })
        }
    };
});

これは、どのリスナーがどのコントローラーによって登録されたかを追跡し、破壊されたコントローラーに属するリスナーのみを削除してメモリをクリーンアップすることにより、さらに改善できます。

于 2014-08-15T17:22:46.167 に答える
0

リスナーの重複を避けるためにこれを行っており、かなりうまく機能します。

 on: function (eventName, callback) {
  //avoid duplicated listeners
  if (listeners[eventName] != undefined) return;

  socket.on(eventName, function () {
     var args = arguments;
     $rootScope.$apply(function () {
        callback.apply(socket, args);
     });
     listeners[eventName] = true;
  });
},
于 2015-07-22T00:47:48.607 に答える
0

ブラウザを更新した後、イベントが重複するというまったく同じ問題が発生していました。「ファクトリ」を使用していましたが、「サービス」を使用するように切り替えました。これが私のsocket.ioラッパーです:

myApp.service('mysocketio',['$rootScope', 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);
                    }
                });
            });
        }
    }

}]);

私はコントローラー内でこのサービスを使用し、イベントをリッスンします。

myApp.controller('myController', ['mysocketio', function(mysocketio)
{
    mysocketio.on( 'myevent', function(msg)
    {
        console.log('received event: ' + msg );
    }
}]);

ファクトリの使用からサービスの使用に切り替えた後、ブラウザを更新した後、重複を受け取りません。

于 2015-09-02T20:52:19.563 に答える
0

AngularAppで上記のコードを試してみたところ、イベントが重複していることがわかりました。SocketIoFactoryを使用した@pootzkoの同じ例

unSubscribe(even_name)コントローラの内部に追加しました。これにより、 socketEventListner$destroyが削除/クリアされます。

var app = angular.module("app", []);
..
..
..
//Create a SocketIoFactory
app.service('SocketIoFactory', function($rootScope){

    console.log("SocketIoFactory....");
    //Creating connection with server
    var protocol = 'ws:',//window.location.protocol,
        host = window.location.host,
        port = 80,
        socket = null;
    var nodePath = protocol+'//'+host+':'+port+'/';

    function listenerExists(eventName) {
        return socket.hasOwnProperty("$events") && socket.$events.hasOwnProperty(eventName);
    }

    return {
        connect: function () {
            socket = io.connect(nodePath);
            console.log('SOCKET CONNECTION ... ',nodePath);
        },
        connected: function () {
            return socket != null;
        },
        on: function (eventName, callback) {
            if (!listenerExists(eventName)) {
                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);
                    }
                });
            })
        },
        unSubscribe: function(listener) {
            socket.removeAllListeners(listener);
        }
    };
});

..
..
..

//Use in a controller
app.controller("homeControl", ['$scope', 'SocketIoFactory', function ($scope, SocketIoFactory) {

  //Bind the events
  SocketIoFactory.on('<event_name>', function (data) {

  });

  //On destroy remove the eventListner on socketConnection
   $scope.$on('$destroy', function (event) {
        console.log('[homeControl] destroy...');
        SocketIoFactory.unSubscribe('<event_name>');
    });
}]);
于 2017-06-22T13:23:23.433 に答える