Node.js server emit data to backbone model - javascript

I'm trying to integrate socket.io with backbone.js, so basicly I have a node server that gets data from a database and emit the data to a backbone client, the client's model should somehow retrieve the incoming data but I'm not sure how to put socket.io in the model now, just getting confused after lost of tries. Any expert please enlight me would be much appreciated!
Node server emit data to client in url /pics
app.get('/pics', function(req, res){
db.collection('pics', function(err, collection) {
collection.find().toArray(function(err, items) {
io.sockets.on('connection', function (socket) {
socket.emit('news', items);
});
});
});
});
Client in Backbone model should retrieve the emitted data(this model works for normal http data sending):
window.Pic = Backbone.Model.extend({
urlRoot: "/pics",
idAttribute: "_id",
initialize: function (){},
defaults: {
_id: null,
name: "",
date: "",
}
});
window.PicCollection = Backbone.Collection.extend({
model: Pic,
url: "/pics"
});

I must admit using Backbone on node + socket.io is quite interesting. Still haven't put my mind into it though. First thing first, have you considered using a plugin Backbone for websockets? Some people have created such.
To give another approach, I think you don't really have much of a choice. Using socket.io to listen to incoming data would result in something like that:
socket.on('model', function(attributes) {
// do something
});
So you'd have to have access to some collection where you could update manually your model.
If you wish to really use socket.io INSIDE your model, a more transparent way would be to extend your model to start listening to some custom event on its creation. I'll link a jsfiddle to show what could be done.
Edit: here you go.
Note that this is full of boilerplate. I'm trying to figure out a way to do a generic socketModel. I'll update if I find one.

Related

Sails.js Waterline query by association

I'm developing and app with Sails.js and using Waterline orm for db. I'm developing functionality for users to do friend requests and other similar requests to each other. I have following URequest model for that:
module.exports = {
attributes: {
owner: {
model: 'Person'
},
people: {
collection: 'Person'
},
answers: {
collection: 'URequestAnswer'
},
action: {
type: 'json' //TODO: Consider alternative more schema consistent approach.
}
}
};
Basically owner is association to Person who made the request and people is one-to-many association to all Persons who the request is directed. So far fine.
Now I want to have a controller which returns all requests where certain user is involved in meaning all requests where user is either in owner field or in people. How I do query like "give me all rows where there is association to person P" ? In other words how I ca know which URequest models have association to a certain Person?
I tried something like this:
getRequests: function (req, res) {
var personId = req.param('personId');
URequest.find().where({
or: [
{people: [personId]}, //TODO: This is not correct
{owner: personId}
]
}).populateAll().then(function(results) {
res.json(results);
});
},
So I know how to do the "or" part but how do I check if the personId is in people? I know I should somehow be able to look into join-table but I have no idea how and couldn't find much from Waterline docs relating to my situation. Also, I'm trying to keep this db-agnostic, though atm I'm using MongoDB but might use Postgres later.
I have to be honest this is a tricky one, and, as far as I know what you are trying to do is not possible using Waterline so your options are to write a native query using query( ) if you are using a sql based adapter or native otherwise, or try doing some manual filtering. Manual filtering would depend on how large of a dataset you are dealing with.
My mind immediately goes to reworking your data model a bit, maybe instead of a collection you have a table that stores associations. Something like this:
module.exports = {
attributes: {
owner: {
model: 'URequest'
},
person: {
model: 'Person'
}
}
Using the sailsjs model methods (like beforeCreate) you could auto create these associations as needed.
Good Luck, I hope you get it working!

Multiple Backbone.js collection options?

Intro:
Building in node.js and express on the backend, I am sending a res.json(details) to the localhost:3000/me route containing the users session information.
So that on the client side I can work with that specific user, for example on the client side initialization I write some code like this.
var me = new MeModel();
me.fetch({
success: function(response) {
App.data.me = me;
var messages = new MessagesCollection([], { id: response.get('user_id') });
messages.fetch({
success: function() {
App.data.messages = messages;
App.core.vent.trigger('app:start');
}
});
}
});
You see I fetch the me model and use that to filter the messages in the MessagesCollection.
Question:
In my MessagesCollection I pass options like this.
module.exports = MessagesCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
},
url: function() {
return '/api/messages/' + this.id;
},
model: MessageModel,
//url: '/api/messages'
});
This is great for getting the desired models when using var messages = new MessagesCollection([], { id: response.get('user_id') });
My question is when somewhere else I run window.App.data.messages.create(Message); this wants to POST to /api/messages/:id when I want to POST to the regular collection?
Theory:
So obviously I would say the best thing is to rethink how I filter the models in the fetch method.
So basically to simplify this question I need to filter the collection upon .fetch() GET... and set no filters upon .create() POST
BTW I am using marionette, maybe that can help with something?
model.url() Returns the relative URL where the model's resource
would be located on the server. If your models are located somewhere
else, override this method with the correct logic. Generates URLs of
the form: "[collection.url]/[id]" by default, but you may override by
specifying an explicit urlRoot if the model's collection shouldn't be
taken into account.
Delegates to Collection#url to generate the URL, so make sure that you
have it defined, or a urlRoot property, if all models of this class
share a common root URL. A model with an id of 101, stored in a
Backbone.Collection with a url of "/documents/7/notes", would have
this URL: "/documents/7/notes/101"
http://backbonejs.org/#Model-url
So you can define method url at MeModel and generate url there ( if there are no other users - you can just return string "/me" or generate in based on model properties ( for example switch if model has id )

Javascript Backbone model design

Fairly new to JavaScript so it might be a noobish question.
At the moment for my project I'm using NodeJS for my server and Backbone for the client. The client will send a request to the server and the server will send list of files in the server, my aim was to simply return the list of files and when user click on the file it will send another request to the server to load the file.
Currently in the client level my model and collection is defined something like:
app.MyFile = Backbone.Model.extend({
defaults: {
modifiedDate: new Date(),
path: '',
content: '' // content of the file
}
});
var MyFileList = Backbone.Collection.extend({
model: app.MyFile,
url: '/api/files'
});
// create global collection of files
app.MyFiles = new MyFileList();
app.AppView = Backbone.View.extend({
initialize: function () {
// fetch all files
app.MyFileList.fetch();
}
});
// app.js (point of entry)
$(function() {
// Kick things off by creating the **App**.
new app.AppView();
});
And my server code:
var application_root = __dirname,
express = require("express"),
...
app.get('/api/files', function(req, res) {
...
// return file list
}
app.get('/api/files/:id', function(req, res) {
...
// return file content?
}
Since it doesn't make sense to load all files in the directory and send it back to the client, what I did was I created the model in the server and fill up modifiedDate and path while leaving content to null. But the problem now is that how do I fill up the content when user clicks on the file? I'm not sure how to manually send an HTTP request from Backbone View or controller. Or is there any better way of doing this? One way that I can think of is to create another model that only keeps modifiedDate and path but to me this looks very verbose and repetitive.
Given what you have on the client side, you may not need anything more.
app.MyFiles = new MyFileList();
app.MyFiles.fetch().done(function() {
// your collection is fetched but each model's content is empty.
// now, I assume at this point you will show them in some view/views.
});
Now when one of those things is clicked on, you can fetch the content.
var model = app.MyFiles.get(id);
model.fetch().done(function() {
// now the model's content attribute will be set
});
This might work with no more code than what you showed. Because the url a model uses to fetch is created by default by appending the model's id to the end of its collection's url.
So from your server, you return a json array from '/api/files': [{id:1, path:'foo'}, {id:2, path:'bar'}]
Then from '/api/files/1': {id:1, path:'foo', content:'whatever'}
When user clicks the file, you can call backbone's fetch method on the model. Then your model will be filled with data from the server.
Note that for this to be working you should return collection from the server first, where models at least have id's. Every other field will be filled after fetch call. Also, you should override model url, if it differs from standard (which is collection/id).

Socket.IO Scoping Issue (Node.JS)

I am working on a node.js project that I am leveraging Socket.IO in, and am having an issue getting my head around a scoping issue. Here is what I am trying to do:
var io = require('socket.io').listen(80);
session_manager = require('./includes/session_manager');
// client joins the socket server
io.sockets.on('connection', function(client) {
client.on('X.Session.join', function(session_id, client) {
session_manager.joinSession(session_id, function(err, session) {
// do whatever
});
});
// BRING IN MORE LISTENERS/EMITTERS?
require('someModuleIBuild');
});
As you can see I am basically setting up the initial connection, joining a session via a managing class (so I know who to emit to for which session) and then I am trying to dynamically bring in some custom stuff that ALSO is going to be emitting and listening via the socket connection.
So how do I reference this current connection from within the confines of my custom modules? All the examples I have seen have all the "on" and "emit" functions in one file, which seems like it could get out of control pretty quickly.
I am possibly over-thinking/over-complicating this (this is my first node.js project, first socket-based project, first mostly-javascript project....etc) but any help would be appreciated.
create your modules like this and you can pass the client into the module
module.exports = function(client) {
client.on("whatever", function () {
});
client.on("whenever", function (data) {
});
};
and then do the require like this
require('someModuleIBuild')(client);

Backbone.js in an offline application

I am using Titanium to build a desktop app using web technologies. I decided to use Backbone.js as my mvc. The problem is the application runs not on a server. This is my Backbone model and collection:
window.Student = Backbone.Model.extend({
initialize: function(){
this.bind("save", this.value_change);
},
value_change: function(){
alert("Student model saved for : " + this.attributes.first_name);
},
urlRoot : http://localhost:8080/student/,
});
window.Students = Backbone.Collection.extend({
model: Student,
url: 'http://localhost:8080/students/',
});
and try fetching the values from the server using
var students = new Students
students.fetch()
I get this error:
message: "'undefined' is not an object (evaluating '$.ajax')"
I am assuming this has to do with the url part. It is not able to fetch the values from the server. Any Ideas?
The problem is backbone saves models on a server. It does this by sending ajax requests to your server. What you want to do is overwrite the persistence mechanism
Use backbone.localStorage to save state in localStorage rather then a database
collection.fetch() will fire a reset event on the collection, and not a save event. Save is a Model method to execute a POST request to your server, when you want to persist your instance model on your server.
You should try this instead :
window.Student = Backbone.Model.extend({
});
window.Students = Backbone.Collection.extend({
model: Student,
url: 'http://localhost:8080/students/',
initialize: function(){
this.bind("reset", this.value_change);
},
value_change: function(){
alert("Students fetched ");
},
});
var students = new Students();
students.fetch();
I'm not sure what you mean when you say
The problem is the application runs not on a server
But if your javascript does not run on the same domain as your server, you may have some cross domain javascript issues... Here is post with an exemple using Ruby on Rails : http://www.tsheffler.com/blog/?p=428
Thanks for all your answers. The problem laid in loading jquery after backbone. I loaded jquery first and it worked out fine. Thanks to parshap from the irc of #documentcloud.
Try https://github.com/Ask11/backbone.offline
Allows your Backbone.js app to work offline

Categories