I want to use socket.io in AngularJS. I found the following factory:
app.factory(\'socket\', function ($rootScope) {
var socket = io.connect();
retur
I tried different ways but nothing worked as expected.
In my app I'm using a socket factory in both the MainController and a GameController. When the user switches to a different view I only want to remove the duplicates events generated by the GameController and leave the MainController running so I cannot use a removeAllListeners function. Instead I discovered a better way to just avoid creating duplicates inside my socket factory:
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
};
}
I tried with the above code in my AngularApp and found the events duplicating. With the same Example from @pootzko using the SocketIoFactory
I have added a unSubscribe(even_name) inside the $destroy of the Controller, that will remove/clear the 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>');
});
}]);
I use something like the code below. socketsService is only instantiated once and I believe Angular takes care of GC the $on's
If you don't like $broadcast/$on, there are some slightly more solid Message Bus implementations for Angular available...
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) {
//...
}]);
Instead of doing app.factory, create a service (singleton) like so:
var service = angular.module('socketService', []);
service.factory('$socket', function() {
// Your factory logic
});
You can then simply inject the service to your app and use it in controllers as you would $rootScope.
Here is a more complete example of how I have this set up:
// 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
}]);
I was having the exact same problem of duplicate events after a browser refresh. I was using a 'factory', but switched to use a 'service'. Here's my socket.io wrapper:
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);
}
});
});
}
}
}]);
I use this service inside my controller and listen for events:
myApp.controller('myController', ['mysocketio', function(mysocketio)
{
mysocketio.on( 'myevent', function(msg)
{
console.log('received event: ' + msg );
}
}]);
Once I switched from using a factory to using a service, I don't receive duplicates after a browser refresh.
create function in your service or factory like below.
unSubscribe: function(listener) {
socket.removeAllListeners(listener);
}
and then call in your controller under the "$destroy" event like below.
$scope.$on('$destroy', function() {
yourServiceName.unSubscribe('eventName');
});
that's solve