happy coding

How To Combine Express, AngularJS and Socket.io

									

Let’s have some fun with express, angularjs and socket.io.
For backend, we will use latest version of Express (4.xx at the time of writing),
for template engine – EJS,
for express session… well… express-session,
and to connect socket.io (1.3) with express session, we’ll use express-socket.io-session.
We will also need body parser because in new express version it’s no longer bundled with express.
Ok, so let’s set it all up as simply as possible.
First, create some project structure. In this tutorial we’ll use:

[js]/
-views
--pages
---index.ejs
--public
---angular.js
-index.js
[/js]

index.js is our main file, in views folder we have pages subfolder for all pages we can template and public subfolder is for everything you want to expose publicly, for example: images, client javascript…
Now let’s checkout our index.js

[js]var express = require('express'),
bodyParser = require('body-parser'),
http = require('http'),
app = express(),
session = require('express-session')({
secret: "aliensAreAmongUs",
resave: true,
saveUninitialized: true
}),
sharedsession = require('express-socket.io-session'),
port = 3016;

app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/views/public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(session);

app.all('/', function (req, res) {
req.session.myCustomData = {msg:"add something you need to session, like userID", userID:Math.floor(Math.random()*100)}
res.render('pages/index', {msg:"hello", session: req.session});
});

var httpServer = http.Server(app);
httpServer.listen(port, function(){
console.log("server listening on port", port);
});

io = require('socket.io').listen(httpServer);
io.use(sharedsession(session));
io.on('connection', function(socket){
console.log("connected");
socket.emit("greetings", {msg:"hello"});
socket.on("something", function(data){
console.log("client["+socket.handshake.session.myCustomData.userID+"] sent data: " + data);
})
});
[/js]

if you get any errors after copy/pasting this code, check if you have installed all packages mentioned above, just do “npm install <package name you’re missing>”
views/pages/index.ejs:

[html]<!doctype html>
<html lang="en" ng-app="fenixApp">
<head>
<title>playing around</title>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2Fsocket.io%2Fsocket.io.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.2.25%2Fangular.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.2.25%2Fangular-route.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22angular.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
</head>
<body ng-controller="mainController">

<div>


msg: <%= msg %>



session: <%= JSON.stringify(session) %>



recievedTroughSocket: {{recievedTroughSocket}}

<button ng-click="sendWithSocket('hello server')">send something</button>
</div>

</body>
</html>
[/html]

views/public/angular.js

[js]var fenixApp = angular.module('fenixApp', ['ngRoute']);

fenixApp.factory('socket', ['$rootScope', function ($rootScope) {
var socket = io.connect();
console.log("socket created");

return {
on: function (eventName, callback) {
function wrapper() {
var args = arguments;
$rootScope.$apply(function () {
callback.apply(socket, args);
});
}

socket.on(eventName, wrapper);

return function () {
socket.removeListener(eventName, wrapper);
};
},

emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {
var args = arguments;
$rootScope.$apply(function () {
if(callback) {
callback.apply(socket, args);
}
});
});
}
};
}]);
fenixApp.config(['$routeProvider', function($routeProvider){
$routeProvider
//route for home page
.when('/', {
templateUrl : '/',
controller : 'mainController'
})
}]);
fenixApp.controller('mainController', function($scope, socket) {
$scope.recievedTroughSocket = "still waiting for data...";
$scope.sendWithSocket = function(msg){
socket.emit("something", msg);
}
socket.on("greetings", function(data){
console.log("user data: " + JSON.stringify(data));
$scope.recievedTroughSocket = data.msg;
});
});[/js]

clicking on button will send to server string specified in sendWithSocket function argument, you can change string to object if you want to send more data. Server will output something like this:

[html]client[38] sent data: hello server[/html]

Personally I like this setup (plus mongodb), you can use jade if you don’t like ejs, also you don’t need to use angularjs, you can strip down this code to bare bones and just go with node and socket.io. You can also try templating js files in case you need more control over what you want to expose to clients.
For example you can transfer angular.js from public folder to some other folder in views and name it angular.ejs just edit the route for it like so:

[js]app.get('/otherFolder/angular.js', function (req, res) {
res.setHeader('Content-Type', 'text/javascript');
res.render('otherFolder/angular', {someData:"to pass to ejs"});
});[/js]

That’s it, have fun.