Skip to content

Commit

Permalink
Use a proposed sliceable query results interface to interact with Doj…
Browse files Browse the repository at this point in the history
…o object stores, refs dojo#305
  • Loading branch information
kriszyp committed Nov 13, 2012
1 parent 40f339e commit b234c63
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 62 deletions.
114 changes: 60 additions & 54 deletions List.js
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,61 @@ function(arrayUtil, kernel, declare, listen, has, miscUtil, TouchScroll, hasClas
}while((next = next.nextSibling) && next.rowIndex != rowIndex);
}
},
_observeResults: function(results, rows, options){
// add an observer to results
var self = this;
var observerIndex = this.observers.push(results.observe(function(object, from, to){
var firstRow, nextNode;
// a change in the data took place
if(from > -1 && rows[from]){
// remove from old slot
row = rows.splice(from, 1)[0];
// check to make the sure the node is still there before we try to remove it, (in case it was moved to a different place in the DOM)
if(row.parentNode == options.container){
firstRow = row.nextSibling;
if(firstRow){ // it's possible for this to have been already removed if it is in overlapping query results
if(from != to){ // if from and to are identical, it is an in-place update and we don't want to alter the rowIndex at all
firstRow.rowIndex--; // adjust the rowIndex so adjustRowIndices has the right starting point
}
}
self.removeRow(row); // now remove
}
// the removal of rows could cause us to need to page in more items
if(self._processScroll){
self._processScroll();
}
}
if(to > -1){
// Add to new slot (either before an existing row, or at the end)
// First determine the DOM node that this should be placed before.
nextNode = rows[to];
if(!nextNode){
nextNode = rows[to - 1];
if(nextNode){
// Make sure to skip connected nodes, so we don't accidentally
// insert a row in between a parent and its children.
nextNode = (nextNode.connected || nextNode).nextSibling;
}
}
row = self.newRow(object, nextNode, to, options);

if(row){
row.observerIndex = observerIndex;
rows.splice(to, 0, row);
if(!firstRow || to < from){
// the inserted row is first, so we update firstRow to point to it
var previous = row.previousSibling;
// if we are not in sync with the previous row, roll the firstRow back one so adjustRowIndices can sync everything back up.
firstRow = !previous || previous.rowIndex + 1 == row.rowIndex || row.rowIndex == 0 ?
row : previous;
}
}
options.count++;
}
from != to && firstRow && self.adjustRowIndices(firstRow);
}, true)) - 1;
return observerIndex;
},
renderArray: function(results, beforeNode, options){
// summary:
// This renders an array or collection of objects as rows in the grid, before the
Expand All @@ -412,59 +467,6 @@ function(arrayUtil, kernel, declare, listen, has, miscUtil, TouchScroll, hasClas
if(!beforeNode){
this._lastCollection = results;
}
if(results.observe){
// observe the results for changes
var observerIndex = this.observers.push(results.observe(function(object, from, to){
var firstRow, nextNode;
// a change in the data took place
if(from > -1 && rows[from]){
// remove from old slot
row = rows.splice(from, 1)[0];
// check to make the sure the node is still there before we try to remove it, (in case it was moved to a different place in the DOM)
if(row.parentNode == container){
firstRow = row.nextSibling;
if(firstRow){ // it's possible for this to have been already removed if it is in overlapping query results
if(from != to){ // if from and to are identical, it is an in-place update and we don't want to alter the rowIndex at all
firstRow.rowIndex--; // adjust the rowIndex so adjustRowIndices has the right starting point
}
}
self.removeRow(row); // now remove
}
// the removal of rows could cause us to need to page in more items
if(self._processScroll){
self._processScroll();
}
}
if(to > -1){
// Add to new slot (either before an existing row, or at the end)
// First determine the DOM node that this should be placed before.
nextNode = rows[to];
if(!nextNode){
nextNode = rows[to - 1];
if(nextNode){
// Make sure to skip connected nodes, so we don't accidentally
// insert a row in between a parent and its children.
nextNode = (nextNode.connected || nextNode).nextSibling;
}
}
row = self.newRow(object, nextNode, to, options);

if(row){
row.observerIndex = observerIndex;
rows.splice(to, 0, row);
if(!firstRow || to < from){
// the inserted row is first, so we update firstRow to point to it
var previous = row.previousSibling;
// if we are not in sync with the previous row, roll the firstRow back one so adjustRowIndices can sync everything back up.
firstRow = !previous || previous.rowIndex + 1 == row.rowIndex || row.rowIndex == 0 ?
row : previous;
}
}
options.count++;
}
from != to && firstRow && self.adjustRowIndices(firstRow);
}, true)) - 1;
}
var rowsFragment = document.createDocumentFragment();
// now render the results
if(results.map){
Expand All @@ -478,14 +480,18 @@ function(arrayUtil, kernel, declare, listen, has, miscUtil, TouchScroll, hasClas
rows[i] = mapEach(results[i]);
}
}
if(results.observe && !options.dontObserve){
// observe the results for changes
var observerIndex = this._observeResults(results, rows, options);
}
var lastRow;
function mapEach(object){
lastRow = self.insertRow(object, rowsFragment, null, start++, options);
lastRow.observerIndex = observerIndex;
return lastRow;
}
function whenDone(resolvedRows){
container = beforeNode ? beforeNode.parentNode : self.contentNode;
container = options.container = beforeNode ? beforeNode.parentNode : self.contentNode;
if(container){
container.insertBefore(rowsFragment, beforeNode || null);
lastRow = resolvedRows[resolvedRows.length - 1];
Expand Down
49 changes: 41 additions & 8 deletions OnDemandList.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,12 @@ return declare([List, _StoreMixin], {
// Creates a preload node for rendering a query into, and executes the query
// for the first page of data. Subsequent data will be downloaded as it comes
// into view.
var rows = [];
var preload = {
query: query,
count: 0,
node: preloadNode,
rows: rows,
options: options
};
if(!preloadNode){
Expand All @@ -70,6 +72,7 @@ return declare([List, _StoreMixin], {
//topPreloadNode.preload = true;
query: query,
next: preload,
rows: rows,
options: options
};
preload.node = preloadNode = put(this.contentNode, "div.dgrid-preload");
Expand Down Expand Up @@ -106,12 +109,28 @@ return declare([List, _StoreMixin], {
// Establish query options, mixing in our own.
// (The getter returns a delegated object, so simply using mixin is safe.)
options = lang.mixin(this.get("queryOptions"), options,
{start: 0, count: this.minRowsPerPage, query: query});
{query: query});
// execute the query
var results = query(options);
if(this.store.sliceable){
// sliceable query, so do the query first and then slice for each page
var results = query(options);
this._observeResults(results, rows, options);
preload.queryResults = results;
if(topPreload){
topPreload.queryResults = results;
}
options.dontObserve = true;
options.queryResults = results;
results = results.slice(0, this.minRowsPerPage);
}else{
options.start = 0;
options.count = this.minRowsPerPage;
var results = query(options);
}
var self = this;
// render the result set
Deferred.when(this.renderArray(results, preloadNode, options), function(trs){
rows.push.apply(rows, trs);
return Deferred.when(results.total || results.length, function(total){
// remove loading node
put(loadingNode, "!");
Expand Down Expand Up @@ -152,10 +171,11 @@ return declare([List, _StoreMixin], {
this.inherited(arguments);
if(this.store){
// render the query
var self = this;
var self = this,
store = self.store;
this._trackError(function(){
return self.renderQuery(function(queryOptions){
return self.store.query(self.query, queryOptions);
return store.query(self.query, queryOptions);
});
});
}
Expand Down Expand Up @@ -231,6 +251,10 @@ return declare([List, _StoreMixin], {
lastObserverIndex = currentObserverIndex;
// we just do cleanup here, as we will do a more efficient node destruction in the setTimeout below
grid.removeRow(row, true);
if(preload.rows[row.rowIndex] == row){
// delete it from our array of rows to free memory
delete preload.rows[row.rowIndex];
}
toDelete.push(row);
}
// now adjust the preloadNode based on the reclaimed space
Expand Down Expand Up @@ -365,15 +389,24 @@ return declare([List, _StoreMixin], {
// Query now to fill in these rows.
// Keep _trackError-wrapped results separate, since if results is a
// promise, it will lose QueryResults functions when chained by `when`
var results = preload.query(options),
var results = preload.queryResults ?
// try to use slice() if we query results that are sliceable
preload.queryResults.slice(options.start, options.start + options.count) :
preload.query(options),
trackedResults = grid._trackError(function(){ return results; });

if(trackedResults === undefined){ return; } // sync query failed

// Isolate the variables in case we make multiple requests
// (which can happen if we need to render on both sides of an island of already-rendered rows)
(function(loadingNode, scrollNode, below, keepScrollTo, results){
Deferred.when(grid.renderArray(results, loadingNode, options), function(){
(function(loadingNode, scrollNode, below, keepScrollTo, results, rows){
Deferred.when(grid.renderArray(results, loadingNode, options), function(trs){
if(rows){
// if we are tracking all the rows for the query, splice it in
for(var i = 0, l = trs.length; i < l; i++){
rows[i + options.start] = trs[i];
}
}
// can remove the loading node now
beforeNode = loadingNode.nextSibling;
put(loadingNode, "!");
Expand Down Expand Up @@ -405,7 +438,7 @@ return declare([List, _StoreMixin], {
// make sure we have covered the visible area
grid._processScroll();
});
}).call(this, loadingNode, scrollNode, below, keepScrollTo, results);
}).call(this, loadingNode, scrollNode, below, keepScrollTo, results, preload.rows);
preload = preload.previous;
}
}
Expand Down

0 comments on commit b234c63

Please sign in to comment.