Real-time Updates with dgrid

By on September 24, 2012 10:46 am

The new dgrid is designed to work with object stores, which makes it easy to create grids that are driven by real-time updates. This is a great feature for rapidly changing data like stock prices or game scores.

To make a dgrid update in real-time, we just need to back it with a real-time object store. This can be accomplished by simply wrapping it with the Observable store wrapper. If we start with a JsonRest object store that retrieves data through REST calls, we can wrap that with Observable:

var stockStore = new Observable(new JsonRest({
    target:"./data/stocks.json" 
}));

Now our stockStore is real-time ready. Next we have to write some code to notify listeners of changes in data. Observable stores provide a notify() method that we can call to notify the store of changes in data. Typically, for a real-time application with rapidly changing data, we would create a Comet connection to a server, and as we receive data from the server, we could notify our store, which would automatically trigger updates in the grid. This might look something like:

// create a socket or long-polling connection to a server and then listen for messages
mySocket.on("message", function(message){
   // got a message   
   var data = message.data;
   // assuming the data is the object to update, we can update the store with:
   stockStore.notify(data, data.id);
});

For our example, we will make some code that emulates a server with constantly changing data so you can get started with real-time updates before you get your server wired up. Here, we get the set of all the objects in a query result that is used by the server, and after a random amount of time (up to one second), we randomly choose one of the stock objects, and randomly update the price. Once the stock object is modified we notify the store of the change, and again, this automatically triggers an update in the grid:

stockStore.query({}).then(function(results){
	function nextUpdate(){
		var stock = results[Math.floor(Math.random() * results.length)];
		stock.price += Math.random();
		stockStore.notify(stock, stock.id);
		setTimeout(nextUpdate, Math.random() * 1000);
	}
	nextUpdate();
});

Check out the real-time dgrid example.


The key method that Observable stores provide, notify(), has two arguments that allow us to not only update existing objects, but signal the removal or addition of objects. The signature of notify() is:

notify(object, id);

If you provide both arguments, this indicates an object has been updated. If the first argument is provided, but the id is not, this indicates a new object has been added. If the first argument is undefined, but the id is provided this indicates an object has been removed. With the combination of different arguments, any type of data notification can be communicated.

It is also important to note the difference between notify() and the data modification methods like put(), add(), and remove(). The data modification methods indicate a request to change data, but the notify() indicates that data has already been modified (elsewhere) and the store is just being notified of the change. The data modification methods generally trigger an action (like a request to the server in the form of PUT, POST, or DELETE), whereas notify doesn’t require any such action.

We can use DojoX’s socket module to communicate with the server via WebSockets (with a fallback to XHR). This can be done by constructing a socket, and then listening for messages. Combining socket messaging with the Observable notify(), we could use an “object” property to define the object that is being modified (absent for deleted objects), and an “id” property to identify objects (absent for new objects):

require(["dojox/socket"], function(Socket){
  // provide the path to construct the URL
  var socket = new Socket("/comet"); 
  // now listen for messages
  socket.on("message", function(message){
    var data = message.data;
    // data provides the object and id
    store.notify(data.object, data.id);
  });
});

Another consideration of real-time updates is that it is more involved if paging is used. While paging (only downloading a set of results at time for the grid) can be an important tool for scaling with large data sets, the real-time updates tend to be more complicated since it involves coordination with the server on which pages of data are being monitored (and these pages may be changing in size as the rows are inserted or removed). While it is certainly doable, with small to medium datasets it is often easier to simply disable paging by setting large values for the minRowsPerPage and maxRowsPerPage properties of the grid.

The combination of Observable stores and the dgrid component make it incredibly easy to build real-time updating grids of data. dgrid’s excellent support for real-time object stores with intuitive visual indicators of data changes makes this a great solution for rapidly changing data.

Comments

  • gerhard

    when using a observable-jsonrest-store do you realy have to ‘reimplement’ the server-side query-logic as ‘queryEngine’?
    if I set a query on the dgrid, to filter out data – and a notification for a new item arrives – should the client filter out the new item (with the query-engine) or should the server-side know, the new item is not applicable for this or that client.

  • Andrew

    When using JsonRest Store w/Observable, It definitely miss behaves with the Pagination extension. It appears that the notify event when on pages > 1 doesn’t compute the correct place to insert/remove the row.