Skip to content

Instantly share code, notes, and snippets.

@JoshMock
Created November 27, 2013 17:57
Show Gist options
  • Save JoshMock/7680146 to your computer and use it in GitHub Desktop.
Save JoshMock/7680146 to your computer and use it in GitHub Desktop.
Fire Backbone model/collection events via websockets using Socket.io. Client code depends on RequireJS for AMD and Cocktail (https://github.com/onsi/cocktail) to apply websocket functionality as a mixin. Assumes views are listening to model/collection changes and updating the DOM accordingly. Some cruft may be specific to the project I'm working…
module.exports = function (io) {
"use strict";
// set up websockets
io.sockets.on('connection', function (socket) {
var eventTypes = [
'Backbone.Model.change',
'Backbone.Collection.add',
'Backbone.Collection.remove',
'Backbone.Collection.sort'
];
// thin wrapper that broadcasts all Backbone changes to all other sockets
eventTypes.forEach(function (type) {
socket.on(type, function (data) {
socket.broadcast.emit(type, data);
});
});
});
};
define(["backbone", "cocktail", "socket-event-mixin"], function (Backbone, Cocktail, SocketEventMixin) {
"use strict";
var MyModel = Backbone.Model.extend({
// stuff
})
Cocktail.mixin(MyModel, SocketEventMixin);
return MyModel;
});
define(["backbone", "underscore"], function (Backbone, _) {
"use strict";
function initModel (model) {
model.socketId = _.uniqueId("socketEventModel");
// require App inside fn to prevent a circular dependency
require(["app"], function (App) {
var socket = App.socket;
// publish model events to socket
model.on("change", function (changedModel, options) {
if (!options.triggeredBySocket) {
socket.emit("Backbone.Model.change", {
id: model.socketId,
updates: model.toJSON()
});
}
});
// apply socket events to model
socket.on("Backbone.Model.change", function (data) {
if (data.id === model.socketId) {
model.set(data.updates, { triggeredBySocket: true });
}
});
});
}
function initCollection (collection) {
collection.socketId = _.uniqueId("socketEventCollection");
// require App inside fn to prevent a circular dependency
require(["app"], function (App) {
var socket = App.socket,
emitData = { id: collection.socketId };
// publish collection events to socket
collection.on("add", function (model, changedCollection, options) {
if (!options.triggeredBySocket) {
socket.emit("Backbone.Collection.add", _.extend(_.clone(emitData), {
model: model.toJSON(),
modelSocketId: model.socketId,
index: changedCollection.models.indexOf(model)
}));
}
});
collection.on("remove", function (model, changedCollection, options) {
if (!options.triggeredBySocket) {
socket.emit("Backbone.Collection.remove", _.extend(_.clone(emitData), {
modelSocketId: model.socketId
}));
}
});
collection.on("sort", function (changedCollection, options) {
if (!options.triggeredBySocket) {
socket.emit("Backbone.Collection.sort", _.extend(_.clone(emitData), {}));
}
});
// apply socket events to collection
socket.on("Backbone.Collection.add", function (data) {
if (data.id === collection.socketId) {
collection.add(data.model, {
at: data.index,
triggeredBySocket: true
});
collection.at(data.index).socketId = data.modelSocketId;
}
});
socket.on("Backbone.Collection.remove", function (data) {
if (data.id === collection.socketId) {
var modelToRemove;
collection.each(function (model) {
if (model.socketId === data.modelSocketId) {
modelToRemove = model;
}
});
collection.remove(modelToRemove, { triggeredBySocket: true });
}
});
socket.on("Backbone.Collection.sort", function (data) {
// TODO
});
});
}
return {
initialize: function () {
if (this instanceof Backbone.Model) {
initModel(this);
} else if (this instanceof Backbone.Collection) {
initCollection(this);
} else {
throw new Error("This object is not a Backbone.Model or Backbone.Collection");
}
}
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment