<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 "> Bitovi Blog - UX and UI design, JavaScript and Front-end development
Loading

Bitovi |

Weekly Widget 5 - Real-Time Chat

Weekly Widget 5 - Real-Time Chat

The Bitovi Team

The Bitovi Team

Twitter Reddit

This week's widget is a real-time chat application that demonstrates sending and receiving messages using Socket.IO and can.Model. This article shows how to integrate Socket.IO to can.Model's events system.

The Widget

The same demo page is in each window below. Messages created in one page are pushed to and displayed in the other window.

How it works

This widget works by integrating Socket.IO into can.Model's event system and using can.Model's event system to listen for messages being created and display them on the page. To understand how it works, I'll cover:

  • can.Model's event system
  • Connecting can.Model's event system to Socket.IO
  • Using can.Model to create the chat client

can.Model's event system

By default, can.Model connects to a RESTful interface. When a can.Model is created like:

Message = can.Model({
    findAll : 'GET /todos',
    findOne : 'GET /todos/{id}',
    create  : 'POST /todos',
    update  : 'PUT /todos/{id}',
    destroy : 'DELETE /todos/{id}'
}, {})

You can create a new message on the server like:

message = new Message({body: "Hi, anyone there?"}).save()

When connecting to a standard RESTful interface, .save() posts the message to the server. The server creates the message and returns the id to can.Model. Upon receiving the response, can.Model triggers a "created" event on the instance and the Model constructor function. This allows you to listen to when a message is created like:

message.bind("created", function( ev, message ){
  // do something when the message is created
})

Or when any message is created like:

Message.bind("created", function( ev, createdMessage ) {
  // do something with the created message
})

can.Model also produces "updated" and "destroyed" events on instances and the constructor function:

Message.bind("updated", function( ev, updatedMessage ) {

}).bind("destroyed", function(ev, destroyedMessage) {

})

A real-time web application uses server-side events (SSEs) to send updates to the client. For example, when a new Message is created by anyone, the client is notified immediately. But, it's beneficial to maintain can.Model's event interface for flexibility and loose coupling. Using can.Model's interface, a chat widget could insert all new messages into the page like:

Message.bind("created", function( ev, createdMessage ) {
  $("#messages").append("<p>"+createdMessage.body+"</p>")
}) 

In the next section, I'll show how to connect can.Model to Socket.IO, so that anytime ANY message is created on the server, a Message model "created" event is published.

Connecting can.Model's event system to Socket.IO

First, we will look at the server's code, then the client.

Server

Using a Node express server and Socket.IO, I listen for POST /messages request with the following code:

var messages = [],
   id=0; 

app.post('/messages', function (req, res) {
  var message = {
      id: ++id,
      body: req.body.body
  }

  messages.push(message);

  if(messages.length === 100) {
      messages.shift();
  }

  io.sockets.emit('message-created', message); 
      res.send(message);
  });
})

The preceding code:

  1. Creates a message object with an id.
  2. Adds the message to an array of messages (which will be used later for retrieving old messages)
  3. Uses Socket.IO to publish a "message-created" event with the message object.

The following provides a service to retrieve existing messages:

app.get('/messages', function (req, res) {
    res.send(messages);
});

Client

Ignoring the server-side events for the moment, I'd create a Message model like:

Message = can.Model({
    findAll: "GET /messages",
    create: "POST /messages"
},{})

The following uses Socket.IO to listen to 'message-created' server side events and publish a Message model "created" event:

var socket = io.connect(myServerUrl)
socket.on('message-created', function(message){
    new Message(message).created()
})

.created() is a method on can.Model that internally publishes a "created" event on a model instance and model constructor function.

But wait! There's a problem. When the user creates a message like:

new Message({body: "Hi world"}).save()

Two Message model "created" events will fire:

  1. from the response
  2. from the server side event

To prevent this, I have chosen to stop the created event from firing in the model. Usually when save is called, the model will make an ajax request. When it is done it will fire a 'created' event and return a deferred object. I update the create function to this:

create : function(attrs) {
    $.post(myServerUrl + '/messages', attrs)
    return $.Deferred()
}

The preceding will post the message to the server. Then it will return a deferred object that will never resolve, so it will never fire a created event.

new Message({body: "Hi world"}).save() will post the message to the server, whereas new Message(message).created() fires a created event, which appends that message to the DOM.

Now, the newly created message will only be appended to the DOM once.

Conclusion

can.Model connects to a RESTful interface by default, but can be easily used for real-time applications. When connecting to a library like Socket.IO, it's beneficial to maintain can.Model's event interface, which can be done easily.

For next week's weekly widget, I will demonstrate a model that makes a request for a list of items, but automatically keeps that list updated. It then caches those items in local storage so future requests are perfect.

I'm calling it super-model.

super-model