Live Data 2: A WebSocket Data Source

Damyan Petev / Tuesday, June 26, 2012

Not so long ago, I showed you how to create a Live data application using the WebSocket protocol and some KnockoutJS magic to make the jQuery Grid come alive!

jQuery Grid upon receiving an update via WebSocket connection.

It’s been a busy following months and adding the demo to that blog totally got forgotten. I decided it’s time to set that one right, but then again why not change things up a little bit? The demo you will find below will contain both the original demo, along with one extra. See, that demo was very (and I do mean that – VERY!) useful and appropriate for when a single value was updating – like it would with ticking stock values, for example. We used knockout’s model to modify just those values, then the grid and data source extensions would react to that change and make it all happen. This would also work if you modify the whole source by adding more records. Alright, but if you don’t need / want to use Knockout? The thing is our jQuery Data Source is already a quite capable control on it’s own and our Product Architect Angel Todorov had an awesome idea some time ago that I feel is just right for sharing:

A WebSocket data source

It seems pretty logical, doesn’t it? The widget’s main job is handling data and it can be extended (even the package offers a few data sources that extend it for more comfortable usage – the API reference can show you the many faces of the jQuery data source). So if the case where you want to have an ever updating data on the server and a client that is always in sync this would work just fine too. What I mean by that are feeds – Twitter, Facebook feeds, Google+ activity, news and mails via atom feeds, etc. All of those are candidates for the following solution.

So ho do you extend the data source? Well it somewhat depends on how you serialize your data on the server. The Data Source can connect to many things, but in this specific case you are probably down to a choice between JSON (which is native to the client) and XML (which is supported by the data source control, so it is possible to use that too). Say you want to use JSON – it’s just three steps to go for:

  • Override the ‘init’ method to provide settings and initialize the widget as if it was a data source using a remote URL to make a call for JSON data.
  • With the above step you need to intercept when the control makes that call, so the in _processRequest open the Websocket connection and assign a handle to the ‘onmessage’ event (keep the context in mind)
  • The method that processes the request usually calls _dataFilter internally and this is where you can handle you newly received JSON and add it to the data:
  1. (function ($) {
  2.             $.ig.WebSocketsDataSource = $.ig.WebSocketsDataSource || $.ig.DataSource.extend({
  3.  
  4.                 init: function (options) {
  5.                     options.responseDataType = "json";
  6.                     options.type = "remoteUrl";
  7.                     this._super(options);
  8.                     return this;
  9.                 },
  10.                 _processRequest: function (options) {
  11.                     if (!("WebSocket" in window)) {
  12.                         throw new Error("There doesn't seem to be WebSocket support in this browser.");
  13.                     }
  14.                     //options.dataSource is the one to initiate the connection to
  15.                     var ws = new WebSocket(options.url);
  16.                     this.context = this;
  17.                     ws.onmessage = $.proxy(this._dataFilter, this);
  18.                 },
  19.                 _dataFilter: function (evt, type) {
  20.                     var items = JSON.parse(evt.data);
  21.                     notify('Reveived data: ' + items.length + " new items.");
  22.                     if (this.settings.responseBehavior === "append") {
  23.                         for (var i = 0; i < items.length; i++) {
  24.                             var value = items.pop();
  25.                             this._data.push(value);
  26.                         }
  27.                     }
  28.                     else {
  29.                         this._data = items;
  30.                         this._dataView = items;
  31.                     }
  32.                     this._completeCallback();
  33.                 }
  34.             });
  35.         } (jQuery));

Note the ‘responseBehavior’ setting (line 22). This is something we will add to the options when we create our grid to be able to control if newly received items should override the current data or be added to it (appended). More on that to follow.

How would you use the WebSocket Data Source

Since I’m doing two in one demo (link above, remember?) I’ll reuse the model and WebSocket server described in that blog and the only thing I need to do now that I have this WebSocketDataSource is to add a recourses and a grid to use it!

  1. //recources
  2.     $.ig.loader({
  3.         scriptPath: "@Url.Content("~/Scripts/Infragistics/js/")",
  4.         cssPath: "@Url.Content("~/Content/Infragistics/css/")",
  5.         resources: "igGrid.*"
  6.     });

In when the loader has done loading:

  1. <table id="grid"></table>
  1. var data = new $.ig.WebSocketsDataSource({ dataSource: "ws://localhost:2011/", responseBehavior: 'append'}).dataBind();
  2.  
  3. $("#grid").igGrid({
  4.     dataSource: data, width: 650, height: 500,
  5.     primaryKey: 'ProductName',
  6.     features: [
  7.     {
  8.         name: 'Filtering',
  9.         enabled: true,
  10.         type: 'local',
  11.         filterTimeout: 0
  12.     }],
  13.     autoGenerateColumns: false,
  14.     columns: [
  15.         { key: 'CategoryID', headerText: 'Category ID', width: 100, dataType: 'number' },
  16.         { key: 'CategoryName', headerText: 'Category Name', width: 200, dataType: 'string' },
  17.         { key: 'ProductName', headerText: 'Product Name', width: 130, dataType: 'string' },
  18.         { key: 'ProductSales', headerText: 'Sales', width: 170, dataType: 'number' }
  19.     ],
  20.     dataRendered: function (e) {
  21.         $("#grid_scroll").scrollTop(10000);
  22.     }
  23. });

Simple as that. The grid definition is almost the same as the one with the Knockout, and while the server feed isn’t all too appropriate (as it somewhat adds more of the same records…but with one value changed), it still demonstrates pretty well what you can accomplish! It’s truly wonderful to sit and watch you data move.

Conclusion

Creating live data applications with WebSocket and the Infragistics jQuery controls can be both fun and extra productive – be it by using our recent Knockout support or by extending the Data Source control! And after all, it is still a client-side jQuery widget and it is still present in both scenarios and that means you can really on it to aid with data handling of all sorts – parsing, conversions, requests, local paging, filtering….. And it’s not just this scenario I showed, the whole point is you can do more – different server sides ( I mentioned before the WebSocker server I’m using is not the only solution and it doesn’t even have to be .NET – there are examples wit Node.js and probably much more fitting your preferred platform), different connections (not just WebScockets!) - if it doesn’t already support it, you can extend the data source to fit your specific needs. Having options and possibilities is always a great thing in my book.

Now download the demo and see for yourself. As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!