May 14, 2009

fetching everything selected in dojox.grid.DataGrid

the problem
the dojox.grid.DataGrid can page data and use virtual scrolling so that you can display a grid that can potentially display any of the data without needing to load all of it. the problem that we face with this is that if a user selects a row at the top of your data, quickly scrolls down towards the end of the data, holds shift and selects another row, then all the rows in between will be selected. this is fine - in fact this is desirable. however, since not all the rows in between were displayed then some of the data may not have been loaded. that is the problem. when we call
then it will return an array of items that might have some null items - these were the items that were not yet fetched.

the workaround
is an asynchronous getSelected() that takes a callback as a parameter. a warning should be given that this workaround is not thorough and maybe it should be called a hack! for me it's a proof of concept and would be a "nice to have" if dojo can provide something like this. the steps involved are straight forward
  • get the selected items in a similar manner to how it already happens with getSelected()
  • while getting those results, build an array of rows that need to be loaded
  • walk the array of unloaded items and fetch the missing pages of data
  • as each page is fetched, push the items into the original results
  • when we have all the pages, call the callback with the complete result set

here's the code pulled from a modified version of dojox.grid.DataSelection:
getSelectedAsync: function(cBack){
var result=[];
var load=[];
var requests=0;
for(var i=0, l=this.selected.length; i<l; i++){
var item = this.grid.getItem(i);
item ? result.push(item) : load[i]=true;

for(var j=0, m=load.length; j<m; j++){
var pageIndex = this.grid._rowToPage(j);

var fetchDfd = dojo.connect(this.grid, "_onFetchComplete", function(items){
// for some reason we get called too many times...
dojo.forEach(items, function(i){
if(requests == 0){

// request the missing page
// skip the other items in this page so we don't have extra requests
j = this.grid._pageToRow(pageIndex+1); // -1 maybe? to account for j++ above

the warnings
  • this will defeat part of the purpose of the grid. the grid tries to only load pages on an "as needed" basis so that you don't have to load all of the data at one time. this goes against that idea.
  • for some reason i'm getting extra callbacks from the connect to "_onFetchComplete" and until i take the time to track them down, i would suggest that you use this at your own risk ;)
  • i've used a number of "private" functions to achieve this. that's both good and (potentially) bad. private functions are not promised to be unchanged from one release to the next but using them means that i don't have to repeat a lot of the work that is being done in these functions
i would certainly like to hear any comments about improvements to this and given a little time to live with it, i may at least search out the possibility of having something like this added to dojox.grid.DataSelection since after all DataSelection is for use with stores that implement the api and many of them are largely asynchronous by nature.