Socket.IO and Angular.JS
Ryan Tucker
What is socket.io?
Socket.IO is a NodeJS package.
Socket.IO aims to make realtime apps possible in every browser and mobile device, blurring the differences between the different transport mechanisms. It's care-free realtime 100% in JavaScript.
Socket.io in practice
- Use Websockets everywhere
- Don't worry about the details
- It just works
- Useful advanced features like multiplexing
how do I use it?
var io = require('socket.io').listen(80);
io.sockets.on('connection', function(socket) {
socket.emit('news', {hello: 'world' });
socket.on('my other event', function(data) {
console.log(data);
});
});
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function(data) {
console.log(data);
socket.emit('my other event', {my: 'data'});
});
</script>
Server
Client
That's great, what can I really do with it?
Why Angular and socket.io?
- Model, View, Whatever separation of concerns
- Easily wrap Socket.IO in an Angular service for reusability and to keep third party libraries isolated from the rest of the application
Let's Build a socket.io service
- Connection
- Disconnection
- Listen for events
- Send messages
Connection/Disconnection
Socket.IO exposes two aptly names methods:
- connect
- disconnect
var mySocket = io.connect('http://localhost/');
mySocket.disconnect();
Listening for event
To listen for an event we use the familiar .on('eventName', cb) convention.
mySocket.on('userJoined', function(data) {
console.log('A user joined!', data);
});
Sending an event
So you want to send something back to the server?
In Socket.IO vernacular we emit an event and can pass some data along with it.
mySocket.emit('updateUserName', {name: 'My New Name' });
wrapping it up in angular land
We'll design an event driven service . Let's start from the top.
angular.module('wsDemo').service('SocketService', function($rootScope) {
var socket; // Our future socket
var self = this;
this.connect = function connect() {
socket = io.connect('http://localhost/');
listenForEvents(socket); // Coming in a bit
};
this.disconnect = function disconnect() {
if(socket) {
socket.disconnect();
}
};
Let's listen for some events
function listenForEvents(ws) {
ws.on('userJoined', function(data) {
$rootScope.$apply(function() {
// Let's broadcast an Angular event
$rootScope.$broadcast('userJoined', data);
});
});
ws.on('userLeft', function(data) {
$rootScope.$apply(function() {
$rootScope.$broadcast('userLeft', data);
});
});
}
Watch out!
We must wrap external calls in $apply since we are out of the Angular digest cycle.
ws.on('userLeft', function(data) {
$rootScope.$apply(function() {
$rootScope.$broadcast('userLeft', data);
});
);
let the world hear you
Sending events
// Send the given message
this.sendMessage = function(message) {
if (socket) {
socket.emit('message', message);
}
}
Building the chat demo
What's left to do?
- Build a users controller
- Build a chat controller
- Build a users service
- Build a chat service
- Some HTML to glue it all together
chat controller
function ($scope, ChatService, SocketService) {
$scope.messages = ChatService.messages;
function sendMessage(message) {
ChatService.sendMessage(message);
// Clear the message
$scope.msgToSend = '';
};
$scope.sendMessage = sendMessage;
$scope.messageKeyUp = function messageKeyUp($event, message) {
if ($event.keyCode === 13 && $scope.userForm.$valid) {
sendMessage(message);
}
};
});
chat Service
.service('ChatService', function chatService($rootScope, SocketService, UsersService) {
this.messages = [];
var self = this;
function messageReceived(event, data) { // Filter out our own messages if(UsersService.myUser.id !== data.id) { self.messages.push(data); } }
function sendMessage(message) {
self.messages.push(message);
SocketService.sendMessage(message);
}
$rootScope.$on('message', messageReceived);
return this;
}
users controller
.controller('UsersCtrl', function ($scope, UsersService) { $scope.users = [];
// Watch for a change in the users $scope.$watch(function() { return UsersService.users; }, function(val) { $scope.users = val; }, true); });
users service
.service('UsersService', function chatService($rootScope) { // Array to hold our users this.users = []; this.myUser = {};
function userJoined(event, data) { ... }
function userLeft(event, data) { ... }
function userEdited(event, data) { ... }
function userList(event, data) { ... }
function setUserId(event, data) { ... }
function disconnected(event, data) { ... }
$rootScope.$on('userJoined', userJoined);
...
$rootScope.$on('disconnected', disconnected);
return this;
A bit of HTML
how would I improve this?
- Make the service more generic
- Don't hardcode events in the main socket service
- Consider a pub-sub model to notify interested parties
Check out Brian Ford's Angular-Socket.IO Library for a more comprehensive library at https://github.com/btford/angular-socket-io
Questions?
All slides are publicly available at http://slid.es/ryantucker/socket-io-and-angular-js.
The code is available at https://github.com/rtucker88/ws-angular.
I'll put links to this on Meetup.com after the talk!
Socket.IO and Angular.JS
By Ryan Tucker
Socket.IO and Angular.JS
- 6,302