Angular-socket-io: how to disconnect the client - javascript

Update
Now the method socket.disconnect(close) has been included in angular-socket-io. It has a Boolean parameter 'close', if true, closes also the underlying connection.
I am using btford/angular-socket-io
What is the correct approach to disconnect the client?
I am trying to implement the following scenario
user login --> connect to socket
user logout --> disconnect from socket
repeat (1) (2)
I succeeded to implement the connect (1) but I am having trouble with the disconnect (2)
This is what I tried: in my Authentication service I have the following
factory('AuthenticationService', function(socketFactory) {
var mySocket;
var service = {
//...
login: function(credentials) {
var login = $http.post('/login', credentials);
return login.then(function(response) {
service.currentUser = response.data.user;
if ( service.isAuthenticated() ) {
// **connect to socket on login**
mySocket = socketFactory({ioSocket: io.connect('http://localhost')});
}
return service.isAuthenticated();
});
},
logout: function(redirectTo) {
var logout = $http.get('/logout');
logout.success(function() {
service.currentUser = null;
mySocket.disconnect(); // **disconnect on logout (not working)**
redirect(redirectTo);
});
return logout;
},
//...
};
return service;
})
mySocket.disconnect();
gives the following error
TypeError: Object # has no method 'disconnect'
mySocket.disconnect() works if instead of
mySocket = socketFactory({ioSocket: io.connect('http://localhost')});
I use
mySocket = io.connect('http://localhost');

your code looks correct. I think the problem is variable hoisting, mySocket is hoisting.
read here

My understanding is that btford/angular-socket-io wraps the socket instance in the socketFactory
but does not expose a disconnet() method
therefore it cannot be used to disconnect the socket from the client side.

The solution is very simple in fact :
Go edit the "socket.js" file from the Btford angular-socket module and you'll see :
var wrappedSocket = {
on: addListener,
addListener: addListener,
emit: function (eventName, data, callback) {
return socket.emit(eventName, data, asyncAngularify(socket, callback));
},
removeListener: function () {
return socket.removeListener.apply(socket, arguments);
},
// when socket.on('someEvent', fn (data) { ... }),
// call scope.$broadcast('someEvent', data)
forward: function (events, scope) {
if (events instanceof Array === false) {
events = [events];
}
if (!scope) {
scope = defaultScope;
}
events.forEach(function (eventName) {
var prefixedEvent = prefix + eventName;
var forwardBroadcast = asyncAngularify(socket, function (data) {
scope.$broadcast(prefixedEvent, data);
});
scope.$on('$destroy', function () {
socket.removeListener(eventName, forwardBroadcast);
});
socket.on(eventName, forwardBroadcast);
});
}
};
And then you just add this next to the others functions :
disconnect: function(){
return socket.disconnect();
},
And voilĂ , there you go :)
You should have something like that :
var wrappedSocket = {
on: addListener,
addListener: addListener,
emit: function (eventName, data, callback) {
return socket.emit(eventName, data, asyncAngularify(socket, callback));
},
disconnect: function(){
return socket.disconnect();
},
removeListener: function () {
return socket.removeListener.apply(socket, arguments);
},
// when socket.on('someEvent', fn (data) { ... }),
// call scope.$broadcast('someEvent', data)
forward: function (events, scope) {
if (events instanceof Array === false) {
events = [events];
}
if (!scope) {
scope = defaultScope;
}
events.forEach(function (eventName) {
var prefixedEvent = prefix + eventName;
var forwardBroadcast = asyncAngularify(socket, function (data) {
scope.$broadcast(prefixedEvent, data);
});
scope.$on('$destroy', function () {
socket.removeListener(eventName, forwardBroadcast);
});
socket.on(eventName, forwardBroadcast);
});
}
};

Just call the RsocketClient.close() method .

Related

Update variable value for onbeforeunload

In my application, I'm trying to use sendBeacon to send data to my remote server. One of the data that I need is how many clicks its been on the page and I'm doing it as follows:
var clickNumber = 0;
document.addEventListener("mouseup", function () {clickNumber++;});
var SendToRemote = window.SendToRemote || [];
SendToRemote.init({
clicks: clickNumber
});
My sendBeacon
navigator.sendBeacon = (url, data) =>
window.fetch(url, { method: 'POST', body: {data: data}, credentials: 'include' });
My only issue now is that the clickNumber is always 0 (which is the default value) and even that mouseup does increment clickNumber, but when sending it sends 0.
How am I able to update the clickNumber so when sendBeacon is triggered, it gets the incremented/updated clickNumber instead of 0.
This is my SendToRemote.init which works fine: (PS: I have removed parts of the codes as it would be over 1000 lines, but kept whats needed):
if (!SendToRemote) {
var SendToRemote = (function(){
var defaults = {
endpoints: {
unload: "https://remote.com"
},
processData: function(results){},
},
results = {
click: 0,
// more stuff here
},
support = !!document.querySelector && !!document.addEventListener,
settings;
var actions = {
sendData: function () {
results.hmn = parseInt(actions.hmnDetection(
results.times.tt, results.times.tp, results.click, results.mouse, results.touch, results.scroll, results.tab
));
let url = settings.endpoints.unload,
data = JSON.stringify(results);
navigator.sendBeacon(url, data);
},
// more functions here
}
function init(options) {
document.addEventListener('DOMContentLoaded', (event) => {
// More stuff here
// Event Listener to porcess
if(modifiable.processOnAction){
let node = document.querySelector(modifiable.selector);
if(!!!node) throw new Error('Selector was not found.');
actions._e(node, modifiable.event, `init-${modifiable.selector}-processOnAction`, function() {
let nodeInput = document.getElementsByName(modifiable.element)[0];
if(!!!nodeInput) throw new Error('nodeInput was not found.');
nodeInput.value = JSON.stringify(results);
hasProcessed = true;
})
}
addEventListener('unload', (event) => {
if (!navigator.sendBeacon) {
navigator.sendBeacon = (url, data) =>
window.fetch(url, { method: 'POST', body: {data: data}, credentials: 'include' });
}
if (!hasProcessed) {
actions.sendData();
hasProcessed = true;
}
return;
});
});
}
function processResults(){
if(settings.hasOwnProperty('processData')){
if (!modifiable.processOnAction){
return settings.processData.call(undefined, results);
}
return results;
}
return false;
}
// Module pattern, only expose necessary methods
return {
init: init,
processResults: processResults,
};
})();
}
Thanks in advance!

Access Vue Instance Using this

When I try to use this in my VueJs methods I get the following error
this is undefined
I think that I shouldn't use arrow functions because their this does not bind to the context I expect it to.
I try with a regular function and get the error above.
What I've tried so far
methods: {
connection(){
new elasticsearch.Client({...});
client.search({...})
.then(function (resp) {
var hits = resp.aggregations;
this.tmp = hits[1].value;
}, function (err) {
console.trace(err.message);
});
}
}
I cannot use the this that I want to in the functions passed to .search and .then . How can I have this bind to my VueJs instance so I can access data, computed, etc...?
You should use arrow function to save this context, and don't forget that inside Vue methods this refers to the current instance.
data() {
return {
counter:0,
connections:2,
tmp: 0,
}
},
methods: {
connection() {
// ...
var client = new elasticsearch.Client({
host: 'xxxxxxxxxxxx'
});
client.search({
[...]
}).then((resp) => {
var hits = resp.aggregations;
this.tmp = hits[1].value;
}, (err) => {
console.trace(err.message);
});
}
}
You can assign this variable to local variable(self) and use it in .then function
data () {
return {
counter:0,
connections:2
}
},
methods: {
connection(){
var self = this;
var tmp=0
var elasticsearch = require('elasticsearch');
var client = new elasticsearch.Client({
host: 'xxxxxxxxxxxx'
});
client.search({
"index":"400000",
[...]
}
}).then(function (resp) {
var hits = resp.aggregations;
self.tmp=hits[1].value;
}, function (err) {
console.trace(err.message);
});
console.log("tmp:",tmp)
}
}

Sinon js check stub called with exact arguments

Sinon js check stub called with exact arguments
Requirement: I want to test ejs.renderFile called with right arguments.
My function file:
html_to_pdf_converter.js
var ejsToPdfConvert = function (template, data, callback) {
var row = data.voucher;
html = ejs.renderFile(
path.join(__dirname+'/../../views/', template),
{
data: data
},
function (error, success) {
if (error) {
callback(error, null);
} else {
var pdfPath = getPdfUploadPath(row);
htmlToPdf.convertHTMLString(success, pdfPath, function (error, success) {
if (error) {
if (typeof callback === 'function') {
callback(error, null);
}
} else {
if (typeof callback === 'function') {
callback(null, success, pdfPath);
}
}
});
}
});
};
Mt test is: html_to_pdf_converter.test.js
describe("ejs to html converter", function () {
it('ejs to html generation error', function() {
var data = {
voucher: {},
image_path: 'tmp/1.jpg',
date_format: '',
parameters: ''
};
var cb_1 = sinon.spy();
var cb_2 = sinon.spy();
var ejsStub = sinon.stub(ejs, 'renderFile');
var pathStub = sinon.stub(path, 'join');
ejsStub.callsArgWith(2, 'path not found', null);
htmlToPdfConverter.ejsToPdfConvert('voucher', data, cb_1);
sinon.assert.calledOnce(ejs.renderFile);
sinon.assert.calledOnce(path.join);
sinon.assert.calledOnce(cb_1);
sinon.assert.calledWith(ejsStub, path.join('views/', templateName), data, cb_2); //Error in this line
ejsStub.restore();
pathStub.restore();
});
});
Here are 2 problems with this line:
sinon.assert.calledWith(ejsStub, path.join('views/', templateName), data, cb_2);
First, you want ejsStub to be called with argument 'data' but when you actually call renderFile you wrap it like this: {data: data}.
The second is that cb_2 is not equal function (error, success) { if (error) ... } that you are actually passing to renderFile.
To make it working run it like this:
sinon.assert.calledWith(ejsStub, path.join('views/', templateName), {data: data});
There is no need to pass cb_2 or anything else because the actual callback is defined in the function and cannot be changed.

Test and stub params in POST request

Guys how can I stub params in POST request, for example here a part of function
gridCalculator : function(req,res){
// calculation logic
var options=[];
options.dateFirstLicensed = req.param('DateFirstLicensed');
options.dateFirstInsured = req.param('DateFirstInsured');
options.claimList = req.param('ClaimList');
options.suspenList = req.param('SuspenList');
...etc
if I did this
it('grid Calculate', function (done) {
var req = {
'DateFirstLicensed' : "01-01-2010",
'DateFirstInsured': "01-01-2011",
'ClaimList': ['05-03-2012'],
'SuspenList': [{'StartDate':'05-03-2012','EndDate':'05-05-2012' }]
};
gridCalculator.gridCalculator(req,function (err, result) {
result.should.exist;
done();
});
});
I get error because I'm simply passing an object not POST request
TypeError: req.param is not a function
Two options come to mind (there are probably a lot more):
Option 1: Define the param function yourself:
it('grid Calculate', function (done) {
var params = function(param) {
switch (param) {
case 'DateFirstLicensed':
return "01-01-2010";
case 'DateFirstInsured':
... //do the same for all params
}
};
var req = {
param: params
};
gridCalculator.gridCalculator(req,function (err, result) {
result.should.exist;
done();
});
});
Option 2: Use tools like supertest to create calls to your server's endpoint.
The problem is that you don't stub the function that is used in your gridCalculator method in your test.
It should look like this:
it('grid Calculate', function (done) {
var testParams = {
'DateFirstLicensed' : "01-01-2010",
'DateFirstInsured': "01-01-2011",
'ClaimList': ['05-03-2012'],
'SuspenList': [{'StartDate':'05-03-2012','EndDate':'05-05-2012'}]
};
var req = {
param: function (paramName) {
return testParams[paramName];
}
};
gridCalculator.gridCalculator(req,function (err, result) {
result.should.exist;
done();
});
});

how to call a function after a function in angular

Here is my two function i want execute the GetAllSLUDetails() at first and then get broadcase value from the controllerTa. So could you help me
//==============call the state function after changed Country Name // get broadcase value from the controllerTa=================
$scope.$on('evtTACountryCodeSelect', function (event, args) {
$scope.message = args.message;
var taCountryCode = $scope.message.split(",")[0];
var taStateCode = $scope.message.split(",")[1];
alert(taCountryCode);
alert(taStateCode);
GetAllSLUDetails(taCountryCode);
alert(taStateCode);
if (taStateCode != "") {
document.getElementById("ddlState").value = taStateCode;
}
});
//================To Get All Records ======================
function GetAllSLUDetails(CountryCode) {
// alert('ctrl State' + CountryCode);
var Data = stateService.getSLU(CountryCode);
Data.then(function (d) {
$scope.StateListUpdate = d.data;
//alert(d.data);
alert(JSON.stringify(d));
}, function () {
alert('Error');
});
}
Please explain better what you are trying to do, but generally you can achieve executing a function after another using callbacks or promises.
So if you want to execute something after the GetAllSLUDetails you can either:
$scope.$on('evtTACountryCodeSelect', function (event, args) {
GetAllSLUDetails(taCountryCode, function() { // THIS
// Do whatever
});
});
function GetAllSLUDetails(CountryCode, callback) {
// alert('ctrl State' + CountryCode);
var Data = stateService.getSLU(CountryCode);
Data.then(function (d) {
$scope.StateListUpdate = d.data;
//alert(d.data);
alert(JSON.stringify(d));
callback(); // THIS
}, function () {
alert('Error');
});
}
or using Promise:
$scope.$on('evtTACountryCodeSelect', function (event, args) {
GetAllSLUDetails(taCountryCode).then(function() { // THIS
// Do whatever
});
});
function GetAllSLUDetails(CountryCode) {
return new Promise(function(resolve, reject) { // THIS
// alert('ctrl State' + CountryCode);
var Data = stateService.getSLU(CountryCode);
Data.then(function (d) {
$scope.StateListUpdate = d.data;
//alert(d.data);
alert(JSON.stringify(d));
resolve(d); // THIS
}, function (error) {
alert('Error');
reject(error); // THIS
});
});
}

Categories