Socket.io client within an object - javascript

I am using Node.js, with Socket.IO for communications to the client on my server...
For example:
On my server, I have a User class, which contains basic information and functions about each user. Each time someone connects, a new User object will be created and added to a users array, with the Socket.IO client object parsed to it. Here is the code:
// Set up server, Socket.IO, etc.
users = [];
var User = function(client) {
this.value = "some random value";
this.client = client;
this.client.on("event",function(data) {
// Do stuff with data
});
}
socket.on("connection", function(client) {
users.push(new User(client));
});
My problem is this: when receiving messages with Socket.IO .on(), I want to do stuff the User object which client is owned by. But the problem is that accessing this doesn't access the User object, but rather the client object (or at least I think so, but it isn't the User but rather some Socket.IO object). Even when I refer the .on() function to call a function in my object, like this.event in my User object, I still can't access my User object with this. I tried creating a local variable within each object called self, and setting it to this, like this: self = this;, but I can't edit this, but only self.
Any ideas?

this.client.on("event",function(data) {
console.log(this.value === "some random value"); // true
}.bind(this));
bind makes this keyword set to the provided value i.e. User object.

Related

Node.js Socket IO: How to continuously save socket data to MongoDB

I'm building a 3D game in the browser using THREE.js. Lots of fun, but I came across the following situation:
An object in my 3D scene is continuously moving around, driven by user input. I need to save the object's position to my database in real-time.
Let's start at the front-end. Angular.js is watching my object's position using its built-in $watch functionality. The object's position can change multiple times per second.
On each change, I emit an event to the backend Node.js server using Socket IO, like so:
socket.emit('update', {
id: id,
position: position
});
In the back-end, the event is caught and immediatly emitted to other members in the same Socket IO Room. This way, everyone in this room will have the most real-time update possible.
Now, because the event can happen multiple times per second, I don't want to update my MongoDB collection on each change, since this would cause a lot of overhead. Instead, I'm looking for a way of incidentally saving data to the database.
I've came up with a solution by using Node.js setInterval function, which saves data every 1000ms. For each distinct id (which is unique per object) received on the backend, a new key is created on an JavaScript object, thus keeping track of changes on a per-object basis.
The (simplified) code on the backend:
let update_queue = new Object();
// ...
// Update Event
socket.on('update', (msg) => {
// Flag Changes
if (!update_queue[msg.id]) update_queue[msg.id] = { changes: true };
// Set Interval Timer
if (!update_queue[msg.id].timer) {
update_queue[msg.id].timer = setInterval(() => {
if (!update_queue[msg.id].changes) {
clearInterval(update_queue[msg.id].timer);
return;
}
// This saves data to MongoDB
Object3DCollection.update(msg.id, msg.position)
.then((res) => {
console.log('saved');
});
// Unflag Changes
update_queue[msg.id].changes = false;
}, 1000);
}
// Immediate Broadcast to Socket Room
socket.broadcast.to('some_room').emit('object_updated', msg);
});
The Question
Is this a proper way of handling very frequent socket data and still saving it to a database? Or are there any other suggestions/solutions that are more robuust or work better.
Note
I do not want to wait for my object to be saved to the database and then emit the saved data to the rest of the socket room. The delay of database write operations is not suitable for the real-time game situation I'm dealing with.
Thanks in advance! All suggestions/solutions are appreciated and will be considered.

Two-way data binding for a Meteor app

I've built an app that is form-based. I want to enable users to partially fill out a form, and then come back to it at a later date if they can't finish it at the present. I've used iron router to create a unique URL for each form instance, so they can come back to the link. My problem is that Meteor doesn't automatically save the values in the inputs, and the form comes up blank when it is revisited/refreshes. I tried the below solution to store the data in a temporary document in a separate Mongo collection called "NewScreen", and then reference that document every time the template is (re)rendered to auto fill the form. However, I keep getting an error that the element I'm trying to reference is "undefined". The weird thing is that sometimes it works, sometimes it doesn't. I've tried setting a recursive setTimeout function, but on the times it fails, that doesn't work either. Any insight would be greatly appreciated. Or, if I'm going about this all wrong, feel free to suggest a different approach:
Screens = new Meteor.Collection('screens') //where data will ultimately be stored
Forms = new Meteor.Collection('forms') //Meteor pulls form questions from here
NewScreen = new Meteor.Collection('newscreen') //temporary storage collection
Roles = new Meteor.Collection('roles'); //displays list of metadata about screens in a dashboard
//dynamic routing for unique instance of blank form
Router.route('/forms/:_id', {
name: 'BlankForm',
data: function(){
return NewScreen.findOne({_id: this.params._id});
}
});
//onRendered function to pull data from NewScreen collection (this is where I get the error)
Template.BlankForm.onRendered(function(){
var new_screen = NewScreen.findOne({_id: window.location.href.split('/')[window.location.href.split('/').length-1]})
function do_work(){
if(typeof new_screen === 'undefined'){
console.log('waiting...');
Meteor.setTimeout(do_work, 100);
}else{
$('input')[0].value = new_screen.first;
for(i=0;i<new_screen.answers.length;i++){
$('textarea')[i].value = new_screen.answers[i];
}
}
}
do_work();
});
//onChange event that updates the NewScreen document when user updates value of input in the form
'change [id="on-change"]': function(e, tmpl){
var screen_data = [];
var name = $('input')[0].value;
for(i=0; i<$('textarea').length;i++){
screen_data.push($('textarea')[i].value);
}
Session.set("updateNewScreen", this._id);
NewScreen.update(
Session.get("updateNewScreen"),
{$set:
{
answers: screen_data,
first: name
}
});
console.log(screen_data);
}
If you get undefined that could mean findOne() did not find the newscreen with the Id that was passed in from the url. To investigate this, add an extra line like console.log(window.location.href.split('/')[window.location.href.split('/').length-1], JSON.stringify(new_screen));
This will give you both the Id from the url and the new_screen that was found.
I would recommend using Router.current().location.get().path instead of window.location.href since you use IR.
And if you're looking for two way binding in the client, have a look at Viewmodel for Meteor.

Can an object become 'stuck' inside another object (because its deep)?

This question is not specific to this particular scenario. The scenario could be concerning any complex/deep JavaScript object but for me to visualize the question I need a scene. Do not answer specifically on the supplied example - only cloning, scope and objects.
Brief Outline
If I have stored a websocket object inside an object, could I later move this websocket out of the storage object and put into another object? kind of like when you pop or splice an array, the array item is not only removed from the array but it's also returned to you (not obliterated but transferred / 'plucked from'). Or is the websocket object stuck/tied to the storage object {}? (If so, in what state would the object be in a lower scope?? What is it 'then'?)
Obviously:
//pseudo code
var finalobject = Object.assign({},storageobject.socket);
//not in nodejs? probably a bad idea anyway
var finalobject = storageobject.socket;
//only a shallow reference
delete storageobject.socket
finalobject.test = 'abc123';
// Obvious TypeError: Cannot set property..bla bla
what would I have though if?:
function lowerscope(x){return x;}
var sameobject=lowerscope(storageobject.socket);
is sameobject now dolly the sheep? a shallow reference or a deep reference? a copy?
My Question And The reason Why I think it's not a stupid question
If I first assign the socket in the storage object to the finalobject
then delete the storageobject.socket then I cant use the finalobect or set properties because that was just a shallow reference to what I just deleted.
So what exactly is going on if I pass the finalobject through a function like so? (again pseudo code)
var finalobject = storageobject.socket;
function appInit(mySocket) {
// do app stuff here with mySocket
// set up some functions...
return; // * come back out to main scope
}
appInit(finalobject);
delete storageobject.socket; // * what am I deleting??
Is the object cloned into appInit? Or does it live there as just another reference?
Example of my code flow (optional; you can skip this):
This part is (too) specific to my case and is here only in case someone asks to see code or 'needs' to understand why I ask.
I have put/created-reference-to my newly created (node.js ws) Websocket in another object:
var uid = '7657rrfdt6e6t'; //unique id
var socketsReference = {}; //main scope
socketServer.on('connection',function(mainSocket) {
var socketsGroup = {}; //local scope
mainSocket.uid = uid;
socketsGroup[1] = mainSocket;
socketsReference[uid] = socketsGroup;
socketsGroup[1].send('uid:' + uid);
So that is the main socket that is stored in the object. It has just sent its uid to the client so that it can set that as its uid property too.
Then next thing I do is connect the client to a secondary socket. For this I want the secondary socket to end up with the similar uid+'.s2' unique id as the main so what I do is connect the second socket then send the new 2nd connection a message from the client to the server which tells the server side socket the uid of the mainSocket
secondarySocket.on('message', function(data) {
this.uid = data.uid + '.s2';
socketsReference[data.uid][2] = this;
Later in the application I take the correct user specific socket group and inject it into the main application where the connected user is in his own scope.
var mySockets = socketsReference[my_uid]
appInit(mySockets);

Parse.com cloud function - manually modify object fields before sending to client

I'm trying to limit the visibility of some fields of parse User object in cloud function.
I have a "Product" class, with a pointer named "owner" to a the "User" that uploaded the item.
I also have a cloud function called "getProducts", I use query.include("owner") to get the owner data at the same time.
What i want to achieve, is that the output of the "getProduct", will be a list of products, but the "Owner" object will contain only certain fields, such as "firstName" or "facebookId",
I don't want to return to the client other sensitive data even though I'm not presenting it (such as Location, email, family name etc..).
After searching I've seen 2 possible solutions.
1.) Cut the User class into 2 classes, 1 of is "Private" class with ACL just for the user.
2.) The second approach that i prefer, i to edit the fields in the cloud function, but i can't seem to change the "owner" object at the "product" object. i'm getting the error:
"Error: Uncaught Tried to save an object with a pointer to a new, unsaved object. (Code: 141, Version: 1.2.19)"
var output[] = [];
_.each(results, function(result) {
var responseData = {};
var owner = result.get("owner");
//Remove fields from the user object
var itemOwnerId = owner.id;
var itemOwnerFirstName = owner.firstName;
var itemOwnerFacebookID = owner.facebookID;
var itemOwner = new Parse.User();
itemOwner.id = itemOwnerId;
itemOwner.id = itemOwnerId;
itemOwner.firstName = itemOwnerFirstName;
itemOwner.facebookID = itemOwnerFacebookID;
result.set("owner", itemOwner);
responseData.item = result;
output.push(responseData);
});
It seems that calling result.set("owner", itemOwner) isn't good, and throwing me exepction:
rror: Uncaught Tried to save an object with a pointer to a new, unsaved object. (Code: 141, Version: 1.2.19)
What am I doing wrong?
The SDK doesn't allow an object that has been changed to be serialized into a response.
A hack way to work around this would be:
result.dirty = function() { return false; };
This would disable the check and allow you to return the modified object.
If you wanted to re-enable it later, you'd need to store the original value of result.dirty and reassign it later.

reuse serialized reference to "this"-Keyword

First things first: I'm not sure whether the information that I'm going to provide will be enough, I will happily add additional information if needed.
I'm serializing a complex structure into the JSON-Format, Field[i][0] is the "this"-reference to an object.
Firebug's Output on JSON.Stringify(myObj)
This is all fine and working as long as I keep it all JS. But now I have the requirement to serialize and send it to my backend to get the reference + computed information back.
Now how do I map back to the reference I had before? How do I bind this ref back to an Object?
This $$hash thing looks internal and proprietarish so I havent even bothered trying something like Object[$$hash] = ref or whatever.
This general idea probably seems pretty whack, but the result is returned asynchrously and I need an identifier to bind the new information back to the original object. Obviously I could just make up my own identifier for that, but I was wondering whether there's an option to solve it this way.
EDIT
The objects are created like this (likewise)
var arrayOfObj = []
arrayOfObj.push(new Object.With.SomeSettersAndGetters());
The Object has a method like
function GetRef(){
return this;
}
Which I'm using to keep a ID/Ref through my code.
Thank you!
Update
If you want to update a series of instances and make many Ajax requests, then you need to look at Ajax long polling and queueing techniques. You won't be able to preserve the reference, but regardless of what Ajax technique you use, make use of the below trick to preserve the reference.
Add long polling on top and you're good to go.
The idea is this:
Assume the server will respond in JSON format. If you need to refer to the original references, here's my two cents:
Update the exact references when the server replies. Say you have 10 instances of Something stored in an array. On a successful response, you use the methods in the Something class to update the specific instances in whatever way you want.
/**
* The array with something instances.
* #type {Array.<Something>}
*/
var instances = [];
/**
* The Ajax success function.
* #param {Event} event The event object.
*/
function ajaxSuccess(event) {
var response = event.target.getResponseText();
var actualResponse = JSON.parse(response);
for (var i = 0, len = actualResponse.length; i++) {
instances[i].setWhatever(actualResponse[i].whatever);
};
};
The above is a more procedural approach. If you want full blown OOP in JS, then you think in modular design patterns. Say you have a module that loads data into some place. Basically, everything related to that module is an instance property.
var myModule = function() {
this.whatever = 1;
};
myModule.prototype.loadMore = function() {
var request = new XMLHttpRequest(),
that = this; // store a reference to this.
request.send(); // etc
request.onreadystatechange = that.onSucess;
};
myModule.prototype.onSucess = function(event) {
var response = JSON.parse(event.target.getResponseText());
this.whatever = response.whatever;
};
var moduleInstance = new myModule();
myModule.loadMore();
// Now the scope is always preserved. The callback function will be executed in the right scope.
Let's assume on the backend side of things, you have a model class that mimics your client side JavaScript model. Say you want to update a reference inside a model that displays text. I use Scala on the backend, but look at the fields/properties and ignore the syntax.
case class Article (
title: String,// these are my DB fields for an Article.
punchline: String,
content: String,
author: String
);
// now assume the client is making a request and the server returns the JSON
// for an article. So the reply would be something like:
{"title": "Sample title", "punchline": "whatever", "content": "bla bla bla boring", "author": "Charlie Sheen"};
// when you do
var response = JSON.parse(event.target.getResponseText());
// response will become a JavaScript object with the exact same properties.
// again, my backend choice is irrelevant.
// Now assume I am inside the success function, which gets called in the same scope
// as the original object, so it refers TO THE SAME THING.
// the trick is to maintain the reference with var that = this.
// otherwise the onSuccess function will be called in global scope.
// now because it's pointing to the same object.
// I can update whatever I want.
this.title = response.title;
this.punchline = response.punchline;
this.content = response.content;
this.author = response.author;
// or I can put it all in a single variable.
this.data = response;
What you need to remember is that scope needs to be preserved. That's the trick.
When I do var that = this; I copy a reference to the model instance. The reference is remembered through higher-order, not current scope.
Then I tell the XMLHttpRequest object to call that.ajaxSuccess when it is complete. Because I used that, the ajaxSuccess function will be called in the scope of the current object. So inside the ajaxSuccess function, this will point to the original this, the same instance.
JavaScript remembers it for me it when I write var that = this;

Categories