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
grid.selection.getSelected()
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++){
if(this.selected[i]){
var item = this.grid.getItem(i);
item ? result.push(item) : load[i]=true;
}
}

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

requests++;
var fetchDfd = dojo.connect(this.grid, "_onFetchComplete", function(items){
dojo.disconnect(fetchDfd);
// for some reason we get called too many times...
if(requests--){
dojo.forEach(items, function(i){
result.push(i);
});
}
if(requests == 0){
cBack(result);
}
});

// request the missing page
this.grid._requestPag(pageIndex);
// 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 dojo.data api and many of them are largely asynchronous by nature.

2 comments:

  1. Thanks Ben for this post. Though this posting is bit old, it talks about a very useful requirement that I have not seen in any other forums. I have been searching for a solution to this Dojox Grid problem of "Select all" for quite sometime. But no luck yet with a solid solution [which is guaranteed to work in the next versions of Dojo as well]. If you have any updates from the original "hack" that you posted, please help me with that. I am really surprised to see no other posting on this topic yet in any forums.

    ReplyDelete
  2. use - rowsPerPage="number" attribute in your table data grid declaration.

    the rowsPerPage attribute will fetch the number rows specified at once, rest will be fetched on scroll

    number can be max+1 of row numbers which you wanna select
    < table dojoType="dojox.grid.DataGrid" jsId="Grid"id="GridId" store="GridStore" rowsPerPage="80">

    ReplyDelete