Realtime Grid updates with ExtJS, NodeJS and Socket.IO

I was recently asked to demonstrate a prototype of the ExtJS grid which was capable of highlighting realtime updates using Socket.IO as the communication layer. Although this example is using an older version of the ExtJS framework (3.2) – all of the general principles will still apply for ExtJS 4 and will of course be much more simple to implement with the new MVC architecture.

A quick search showed several solid examples of this technique in action (see end of article for links)

In this example, I’m simply extending the Ext Direct example from a previous post. To hookup Socket.IO is trivial simply issuing from the project root:

npm install socket.io

Then reconfiguring my main app.js file to connect the socket.io listener along with my http server.

/app.js

var express = require('express')
, app = express()
, server = require('http').createServer(app)
, io = require('socket.io').listen(server)
, load = require('express-load')
, path = require('path')
, cookie = express.cookieParser(SECRET)
, store = new express.session.MemoryStore()
, session = express.session({secret: SECRET
                              , key: KEY
                              , store: store});

To configure the socket.io handlers on the server side, I first wanted to make sure they were authorized. Obviously, in any enterprise application all socket connections would be specifically linked to the session which would have been authenticated and authorized. In this example, I’m merely checking the signed cookie to identify the user. In a future example I can use this session to bind the socket connection to a specific user no matter how many browsers or tabs they have concurrently open to the application.

io.set('authorization', function(data, accept) {
  cookie(data, {}, function(err) {
    if (!err) {
      var sessionID = data.signedCookies[KEY];
      store.get(sessionID, function(err, session) {
        if (err || !session) {
          accept(null, false);
        } else {
          data.session = session;
          data.sessionID = sessionID;
          accept(null, true);
        }
      });
    } else {
      accept(null, false);
    }
  });
});

Interestingly, in this example I am not specifically doing anything to the on connection event of the socket server. Instead, I’m passing the server into my Ext Direct handler middleware.

app.use('/direct', directhandler('on', models, io));

So the application flow in this example is this:

  • A client loads the main page (http://localhost:3000)
  • The Grid on the client makes an Ext Direct call to the server (getAll)
  • The server responds with the Grid data
  • Client renders the Grid data
  • If a cell is updated, the client which updates its store makes its Ext direct call (update) and the direct handler will then emit an event to all connected sockets with a message that contains the modified record
  • Any client which is listening for that event will update its local store and highlight to the user that a change was made

On the client side of the application, I’ve added a class called Socket.io which extends Ext.util.Observable and connects to a configured socket.io server.

In the client app.js I create the Socket observer and pass it to the new Grid:

                 var socket =  new App.ux.Socketio({
					host: 'localhost',
					port: 3000

                });

                var grid = new App.CompanyGrid({
                    renderTo: 'grid',
                    socket: socket

                });

So when the client loads, it will build a new socket connection to the server. When the client recieves an update message from the server:

this.socket.on('update', function(data){
			// handle updates from the server
 			me.onUpdate(data);

 		});

The client will fire the update event which is handled by the grid. Any matching records in the store are retrieved and updated by the data contained in data of the socket update event.

 		this.socket.on('update', function(data){

 			this.companyStore.suspendEvents();
 			record = this.companyStore.getById(data.id);
 			if (record){
 				// do the update
 				record.set('id', data.id);
 				record.set('company', data.company);
 				record.set('price', data.price);
 				record.set('change', data.change);
 				record.set('pctChange', data.pctChange);
 				record.set('lastChange', data.lastChange);
 				record.dirty = false;
 			}
 			this.companyStore.resumeEvents();
                        // fire an event to handle the UI highlight
 			this.fireEvent('update_event', record);

 		},this);

Lastly, the row of the grid is highlighted to show the user that a record was updated. In this case it highlights the record yellow for 2 seconds.

		this.addListener('update_event', function(record) {
			var view = this.getView();
			view.refresh();
			var rowIndex = this.companyStore.indexOf(record);
			var row = view.getRow(rowIndex);
			Ext.fly(row).highlight("FFFF00", {
				attr: 'background-color',
				duration: 2
			});
		});

Here’s what it looks like:

Initial Grid – The application is loaded in two separate browser tabs. Click thumbnail to enlarge.

One of the Grids being updated. Click thumbnail to enlarge.

The grid which was not updated receives the update from the socket event and highlights the changed row yellow. Click thumbnail to enlarge.

Updates

I’ve actually recently implemented this kind of functionality for some clients to provide realtime notification updates for their existing ExtJS applications. This architecture can be implemented into a very robust solution. I’ve been able to easily utilize a wide variety of message queues for both simple use cases with redis, to more sophisticated, scalable notification platforms using both amqp and rabbitmq.

If your ExtJS application would benefit from realtime updates to provide a responsive user interface, please contact us!

Source

The source for this example is available. Connect with me or send me a note and I’ll send you the source.

Contact

Connect with me on LinkedIn

or Contact Us

References

NodeJS + Ext Scheduler = Realtime updates
softwarezman / extjs-socketio

Google+