改进这个AngularJS工厂使用socket.io
我想在AngularJS中使用socket.io。 我find了下面的工厂:
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。$广播和$ scope。$在控制器,但它看起来不是一个好的解决scheme。
编辑2:添加到工厂
init: function() { socket.removeAllListeners(); }
并在每个使用socket.io的控制器开始时调用它。
仍然不觉得是最好的解决scheme。
控制器被销毁时,删除套接字侦听器。 您将需要像这样绑定$destroy
事件:
function MyCtrl($scope, socket) { socket.on('message', function(data) { ... }); $scope.$on('$destroy', function (event) { socket.removeAllListeners(); // or something like // socket.removeListener(this); }); };
有关更多信息,请查看angularjs文档 。
你可以用最less的工作来处理这个问题,包括一个Scope,并且监听$destroy
是否被广播,并且只能从这个套接字中删除在这个Scope的上下文中添加的监听器。 被警告:以下没有经过testing – 我认为它更像伪代码而不是实际的代码。 🙂
// 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) { ... }); };
我会给接受的答案添加评论,但我不能。 所以,我会写一个答复。 我遇到了同样的问题,我find的最简单最简单的答案就是你可以在这里find的另一篇文章 ,由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); }); }); } }; });
它拯救了我的一天,我希望对别人有用!
在你的服务或工厂创buildfunction如下。
unSubscribe: function(listener) { socket.removeAllListeners(listener); }
然后在下面的“$ destroy”事件下调用你的控制器。
$scope.$on('$destroy', function() { yourServiceName.unSubscribe('eventName'); });
那就解决了
我在阅读之前就解决了一个类似的问题。 我在服务中做了所有的事情。
.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; }])
我尝试了不同的方式,但没有按预期工作。 在我的应用程序中,我在MainController
和GameController
中都使用了socket
工厂。 当用户切换到一个不同的视图,我只想删除由GameController
生成的重复事件,并保持MainController
运行,所以我不能使用removeAllListeners
函数。 相反,我发现了一个更好的方法来避免在socket
工厂内部创build重复项:
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 }; }
而不是做app.factory,像这样创build一个服务 (单例):
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 }]);
在上面的布兰登的回答扩展,我创build了一个服务,应另外1)剥离angular度标签像。$$ 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);
我使用类似下面的代码。 socketsService只实例化一次,我相信Angular照顾GC的$上的
如果你不喜欢$ broadcast / $,那么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) { //... }]);
我通过检查侦听器是否已经存在解决了这个问题。 如果您有多个控制器同时加载(想到不同的页面模块都使用socketIO),那么删除$destroy
上的所有注册监听器将会破坏被破坏的控制器和所有仍然加载的控制器的function。
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); } }); }) } }; });
这可以通过跟踪哪个监听器被哪个控制器注册并且仅仅移除属于被破坏的控制器的监听器来清理存储器来进一步改善。
我正在这样做,以避免重复的听众,工作得很好。
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; }); },
刷新浏览器后,我遇到了重复事件的问题。 我正在使用“工厂”,但转而使用“服务”。 这是我的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 ); } }]);
一旦我从使用工厂切换到使用服务,浏览器刷新后我不会收到重复。
我尝试了AngularApp中的上述代码,发现事件重复。 与使用SocketIoFactory的@pootzko相同的例子
我在控制器的$destroy
内添加了一个unSubscribe(even_name)
,它将删除/清除socketEventListner
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>'); }); }]);