PDA

View Full Version : Scrolling (rather than paging) grid



elygre
6 Mar 2007, 4:55 AM
We have created a popup-dialog using a grid backed by a JsonReader and a ScriptTagProxy. The amount of data is too large for a single page display, but we would still like to avoid the paging bar.

Is it possible to set up the grid such that data is automatically read when the user scrolls towards the bottom, without having to use paging?

BernardChhun
6 Mar 2007, 5:04 AM
We have created a popup-dialog using a grid backed by a JsonReader and a ScriptTagProxy. The amount of data is too large for a single page display, but we would still like to avoid the paging bar.

Is it possible to set up the grid such that data is automatically read when the user scrolls towards the bottom, without having to use paging?

hmm i'm not sure this is possible...and the data would have to be in memory so that it shows up when you scroll down I guess. That would be laggy as well.

Why do you want to avoid the paging bar?

elygre
6 Mar 2007, 5:28 AM
hmm i'm not sure this is possible...and the data would have to be in memory so that it shows up when you scroll down I guess. That would be laggy as well.

Why do you want to avoid the paging bar?

We were thinking along the lines of having the data being loaded on demand whenever the user scrolls, in chunks of e.g. 50 rows at a time. This way, only the data that has actually been viewed needs to be in memory. A user who scrolls way down will experience lagging, but this is not the usual use case.

One benefit is a smoother user experience, with only one navigational concept (scroll). Most users will not need to pass the first page (through either scrolling or paging), and then they don't need to worry about "that paging bar thing" they don't use anyway.

Another benefit in scrolling is that we would not have to count the rows (to render "Page x of y") -- the user don't really need to know, and computing this is fairly expensive.

PortraitPainter
6 Mar 2007, 5:44 AM
A variation would be JIT Ajax paged data fetching as a user scrolls down a DIV who's scroll bar is scaled to the total server-side record count.

This type of grid paging is particularly useful in situations where most users will only have a single page of grid data but provision needs to made to avoid downloading an excessive data chunk in exceptional cases. The benefit is that most users, with a reasonable amount of data, will not be troubled by the presence of the paging toolbar.

A feature for the Ext 2.0 wish list I suggest.

BernardChhun
6 Mar 2007, 5:48 AM
One benefit is a smoother user experience, with only one navigational concept (scroll). Most users will not need to pass the first page (through either scrolling or paging), and then they don't need to worry about "that paging bar thing" they don't use anyway.

I'm not sure about the being smoother thing. The data would have to be fetched anyway during the scrolling which means waiting time to me. and when the user scrolls from line 2500 to line 500 and back to 2500, would it make 40 ( (2500 - 500) divided by 50 ) data requests? I guess making the request when the scrolling stops is your best bet here but then again how would you know on what line it has stopped?

elygre
6 Mar 2007, 6:06 AM
One benefit is a smoother user experience, with only one navigational concept (scroll). Most users will not need to pass the first page (through either scrolling or paging), and then they don't need to worry about "that paging bar thing" they don't use anyway.

I'm not sure about the being smoother thing. The data would have to be fetched anyway during the scrolling which means waiting time to me. and when the user scrolls from line 2500 to line 500 and back to 2500, would it make 40 ( (2500 - 500) divided by 50 ) data requests? I guess making the request when the scrolling stops is your best bet here but then again how would you know on what line it has stopped?

Well, if the user were to scroll to line 2500, then my rationale would be off :-). In any case, the way I imagined the mechanism, the automated scrolling would never generate more data loads then using the paging toolbar.

I imagined that the number of lines in the grid grew for each scroll, so that the fetching would be different than you expected:

* Initially, we load 50 rows
* When the user scrolls to row 51, we load another 50 rows (51-100), for a total of 100 rows in the grid (1-100). Navigation in this set would not generate a data load.
* Scrolling to row 2500 would generate a lot of data loads, and build a grid of 2500 rows.
* Scrolling back to row 500 would generate zero loads, and going forward to 2500 again would generate zero loads.

So, the trade-offs:

* Paging has a constant grid size (e.g. 50 rows), whereas scrolling has a dynamic grid size (rows 1 through "last seen")
* Paging has constant memory use, whereas scrolling may grow "out of bounds" if the user scrolls very far
* They require equally many dataloads when displaying "new" data pages, but scrolling would not load data for pages already loaded once.
* Scrolling has a single user interface metaphore, whereas paging might have two (e.g. when the grid is displayed in a fixed-height dialog box)
* Paging tells the user explicitly when an expensive data load occurs, wherease scrolling does this automagically.

Eirik

BernardChhun
6 Mar 2007, 6:23 AM
Well, if the user were to scroll to line 2500, then my rationale would be off :-). In any case, the way I imagined the mechanism, the automated scrolling would never generate more data loads then using the paging toolbar.

I imagined that the number of lines in the grid grew for each scroll, so that the fetching would be different than you expected:

* Initially, we load 50 rows
* When the user scrolls to row 51, we load another 50 rows (51-100), for a total of 100 rows in the grid (1-100). Navigation in this set would not generate a data load.
* Scrolling to row 2500 would generate a lot of data loads, and build a grid of 2500 rows.
* Scrolling back to row 500 would generate zero loads, and going forward to 2500 again would generate zero loads.

So, the trade-offs:

* Paging has a constant grid size (e.g. 50 rows), whereas scrolling has a dynamic grid size (rows 1 through "last seen")
* Paging has constant memory use, whereas scrolling may grow "out of bounds" if the user scrolls very far
* They require equally many dataloads when displaying "new" data pages, but scrolling would not load data for pages already loaded once.
* Scrolling has a single user interface metaphore, whereas paging might have two (e.g. when the grid is displayed in a fixed-height dialog box)
* Paging tells the user explicitly when an expensive data load occurs, wherease scrolling does this automagically.
Eirik

adding one line at a time? hmm sounds nice to me but the only thing I can't figure out here is when the grid has loaded the 2500 lines, the DOM would be freaking loaded and it seems like lagging will not be an option. but then again...who's crazy enough to scroll all the way down except me?!

By the way, I like such discussion where we just imagine a situation and the pros and cons of it...and it's really nice talking to you Eirik! :)

Bernard

Animal
6 Mar 2007, 7:17 AM
elgyre has a good point.

Before I used yui-ext (as it then was), I had a "lazy loading" grid which filled itself as you scrolled.

It worked OK, but it did jump a little. The XHR, even though executing asynchronously, would interrupt the scrolling for a bit.

This would be a good idea for Ext 2.0. I agree that it would be unusual for a human reader of a Grid to scroll down to the end of such a large list. Usually after a few pages, the user would go back and refine their search, so the overhead shouldn't actually grow that much..

seanfell
6 Mar 2007, 7:18 AM
I use this

http://www.dowdybrown.com/dbprod/

alot.
Typically talking to oracle backend with perl scripts generating the needed xml.
Works a treat.
Now if this formed part of yui-ext, we'd be in grid heaven..

amackay11
6 Mar 2007, 8:25 AM
adding one line at a time? hmm sounds nice to me but the only thing I can't figure out here is when the grid has loaded the 2500 lines, the DOM would be freaking loaded ....

Bernard

I'd agree. Before looking at dynamically loading, see how your browser reacts with 2500 lines in a grid. I've found it just bogs the browser and client to a crawl. I do a check, if XHR response is less than 3000 bytes, use grid, otherwise build a table with domhelper...but even building a table in dom is slow when there is lots of data.

MrKurt
6 Mar 2007, 11:40 AM
Paging is really sort of a bad way to flip through data. It's remarkably difficult to "jump to the middle" of a huge set of results using paging.

There's a Rico livegrid component that does the live scrolling, have a look: http://openrico.org/rico/livegrid.page

I like how it works, and I also like how I can jump to any aribtrary chunk of data pretty easily.

brian.moeskau
6 Mar 2007, 11:51 AM
I don't think Jack is too keen on the live scrolling concept, but I guess if enough people requested it he could probably be persuaded to add it in at some point in the future. EDIT: The OpenRico grid does seem to function pretty smoothly. Not bad.

leathekd
6 Mar 2007, 2:11 PM
We use the Open Rico LiveGrid in our product and have had decent results.

It takes a consistent amount of time (a few seconds) to init the grid and load the data. That can be both good and bad as it generally takes the same amount of time even if there is only 1 row. I've also noticed that the LiveGrid performance suffers the more columns you have (it iterates over the rows/cells setting the innerhtml). We often have over 10 columns and that causes the grid to load and scroll a bit slower.

We have had several customers complain about the jitter involved in scrolling with the handle and when clicking the down arrow on the scrollbar. They feel like they are forced to wait every time they must scroll.

I don't know how Ext's layout would fare with a LiveGrid type backend. As I understand it, each cell is absolutely positioned, so it may avoid some of the performance issues of LiveGrid.

We are currently investigating using the Ext Grid for a LiveGrid replacement. The main concern is speed and if it even makes sense for people to be scrolling around 10k worth of data. Like most product decisions it is less about the technology and more about the user requirements/experience.

YMMV

heidtmare
6 Mar 2007, 2:37 PM
one project i worked on required a load on scroll. It didnt work out so well so we ended up implimenting a NOSCROLL view, with page up and page down buttons. On the page up/down we showed said page, but we loaded into memory the previous and next page for the one we were viewing while we were viewing it. This eliminated the loading wait.

jack.slocum
7 Mar 2007, 1:47 AM
Personally I hate scroll loading. By everyone has their own preference and most of the Ext components try to accomodate that. In my experience I have found that although developers love to implement cool features like scroll loading, users like to stick with what works. For data over HTTP, that is generally paging.

The one major issue I could see cropping up is implementing it is it would defeat the new flexible row heights since there would be no way to accurately determine what the scrollbar size should be.

On a side-note, I would never load more than 100 records in the grid (or even just a table) with more than 10 columns as the browser performance goes straight downhill.

dualmonger
7 Mar 2007, 8:49 AM
Hi Folks,

(People requesting)++

Personally I don?t care too much for scrolling either ? however I have found some useful cases for the feature, such as ?Requirements? from the boss :P. Currently I?m running into the ? ?We want it to look and feel the same way the installed version works?.

Also, my data source only understands:
GetData ? initial request for data
GetNextData ? get additional request data
GetPrevData ? get previously requested data
There is no way of knowing how records in total until the last GetNextData returns an ?end of records found?.

I understand a huge amount of rows will grind the browser to a holt? however, I don?t believe the record count is as much of a deal if the data is not actually rendered ? but rather left in a data / buffer state

I see a use as elygre mentioned, but for performance purposes, be able to set the amount of rendered rows as to allow data coming in to be stored in a buffer with only a window / portion of the buffer actually being rendered into the grid. Also ? the scroll bar would be based on the rendered row count, and not on the full row count.

I was put into an environment using Infragistics ? but it has turned out to be bloated and difficult to extend, and have been looking for an alternative. These YUI.EXT controls have been the cleanest I have found in my search thus far and am hopeful to see a data on demand scrolling ? or at least a way to extend as to allow for this functionality.

Anyhow,
keep up the great work!

brian.moeskau
7 Mar 2007, 9:31 AM
Also, my data source only understands:
GetData ? initial request for data
GetNextData ? get additional request data
GetPrevData ? get previously requested data
There is no way of knowing how records in total until the last GetNextData returns an ?end of records found?.
Actually, this is a good point. While I don't have the need for this personally, the back end system at work has a similar issue -- in our case, the API is "GetMore" :) -- and to use a UI grid on the front end, I guess it would have to have dynamic loading to work correctly (without additional manual coding to work around it).

So while I don't personally like it, sometimes it is a legitimate necessity for reasons out of the developer's control.

Animal
7 Mar 2007, 9:34 AM
I always performs a "select count(*)" on the query before issuing the actual select so that it knows how big the dataset is.

brian.moeskau
7 Mar 2007, 9:41 AM
I just wanted to add too that in our case, we don't have access to run SQL or do anything other than using the API we're given. The back end is a huge Tandem database (totally inaccessible from our front end applications) and our API is provided by COM proxy DLLs. I suspect that many people putting front ends on legacy systems like this probably deal with similar issues. And changing the back end to add in paging/record count functionality is a non-starter.

hcervantes
7 Mar 2007, 11:09 AM
I have run into the same problem with paging as some of you have expressed. RicoLiveGrid fetches the data while you scroll and does so smoothly. An approach like this might work for EXT. Both paging and scrolling have their advantages and disadvantages, it is up to the the preference of the developer. I personally prefer scrolling because if you are searching records near the middle of the table, it is hard to guess what page it is on. Scrolling makes it easy to sort the data in any way and scroll up or down to find what you are looking for.

Hopefully this feature will be implemented into EXT.

Cheers.
-Hector

dualmonger
7 Mar 2007, 11:16 AM
I?m in the same situation bmoeskau ? bolting on a web front to an ancient data provider and have no control outside of the given API.

A couple notes about not adding functionality for reasons of performance?

- as pc?s get bigger and badder, performance will increase.
- one users ?slow? performance is another person?s ?OMG this is freekn? flying? :P
This can be especially true when reviving old aps / data with new revamped front ends.

This is all ASSuming the addition functionality does not cost a performance hit when not utilized.

BernardChhun
7 Mar 2007, 12:11 PM
Scrolling makes it easy to sort the data in any way and scroll up or down to find what you are looking for.

Using the grid's filter functionnality does that pretty well too.

brian.moeskau
7 Mar 2007, 12:43 PM
Slightly off the topic of Ext grid scrolling specifically, but I just happened to run across Google books and they actually handle both paging and live scrolling to retrieve scanned book pages dynamically. It's actually pretty slick -- par for the course for those guys I suppose. Scrolling with the mouse wheel to zip through a book is pretty nice actually -- I think this is one of those examples where manual paging would be quite annoying.

http://books.google.com/books?vid=ISBN0596000480&id=xn5aJpEJSEYC&pg=PP1&lpg=PP1&ots=FJ1PNgMKKC&dq=javascript&sig=j4pJ8k2cYKtjfPpY6zrvMWSAMTY#PPA21,M1

TommyMaintz
7 Mar 2007, 12:55 PM
Yeah and in googles case they have to deal with big images for those books. Really looking good :)

Young
7 Mar 2007, 7:11 PM
I think it's possible, just another kind of realizing of paging. If you have ever used PLSQL developer, you can see they handles data like what you say.

KimH
8 Mar 2007, 1:12 AM
The one major issue I could see cropping up is implementing it is it would defeat the new flexible row heights since there would be no way to accurately determine what the scrollbar size should be.
Maybe the grid could take a parameter of fixedRowHeight to make out for this. I guess if it is implemented you can say that it is "working as designed" :lol:

Normally I would use the paging, but I see scenarios where I would rather have the long-scrolling list.

fponticelli
8 Mar 2007, 3:36 AM
I think not this is really a problem. You can set a default height parameter for rows and adjust visually that value when new records are loaded. Practically the default height parameter is just needed to set the height of the scrollbar.

fponticelli
8 Mar 2007, 3:44 AM
Jack: your combo-box examples on the new alpha are really amazing ... and I think that scrolling grid will fit very nicely in them :D

Herm
8 Mar 2007, 5:37 AM
Talk of the 'scrolling grid', reminded me of the 'pageless' approach. google for 'pageless ajax' and you get some stuff like this http://unspace.ca/discover/pageless/

Cheers,
Peter

BillEisner
18 Mar 2007, 11:31 PM
Jack:

I have a strong need for a non-paging grid that can display a great deal of information. For this reason, I have subclassed a number of the grid-related Ext classes in order to add Rico-like functionality to the Ext.grid.Grid class. I am introducing the term "Virtual Grid" to describe a grid that displays data from a sparcely populated array, although I would welcome any other term for that capability. I developed the code on top of the array-grid example, and I have two files that can be dropped into a new ext-1.0-alpha3\examples\virtualgrid subdirectory to demonstrate the capability.

The following is ext-1.0-alpha3\examples\virtualgrid\virtual-grid.html:


<html>
<style type="text/css">
#custom {
cursor:move;
}
#custom-rzwrap{
z-index: 100;
}
#custom-rzwrap .yresizable-handle{
width:11px;
height:11px;
background:transparent url(resizable\square.gif) no-repeat;
margin:0px;
}
#custom-rzwrap .yresizable-handle-east, #custom-rzwrap .yresizable-handle-west{
top:45%;
}
#custom-rzwrap .yresizable-handle-north, #custom-rzwrap .yresizable-handle-south{
left:45%;
}
</style>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Virtual Array Grid Example</title>

<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="../examples.css" />

<script type="text/javascript" src="../../yui-utilities.js"></script>
<script type="text/javascript" src="../../ext-yui-adapter.js"></script>

<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="virtualgrid.js"></script>

</head>
<body>
<h1>Virtual Array Grid Example</h1>


This example shows how a grid can display data from a sparse array. The grid has been 'virtualized' so it can show very large lists of data.

The grid contains an HTML table that is only large enough to show the visible data. As the scrollbar moves, different data is moved into the table.

The data currently comes directly from the JavaScript, which contains delays to simulate a host. Eventually, data will be requested as it is needed

from servers using AJAX, and the user will see "loading..." until the data has been received. The new beta mail client provided by Yahoo,

which inspired this implementation, is the best current example of this technique on the Internet. </p>


This is still a work in progress, so many of the current grid features, such as keyboard interactions, are not working. It has only been tested using Firefox.

The current host simulation simply loads the specific rows that have been requested. A paging technique would eliminate some of the jumpiness, and and multiple

buffers could be used to preload data to provide a smoother experience.</p>


In the current code, only preloaded data is used when the number of rows is specified as '30' (that is the only mode where selection and sorting are currently working).

When the host delay is specified as '0', the host request simulation is bypassed in order to assess the performance of the grid, itself.</p>


Note that the js source is not minified so it is readable. See virtualgrid.js.

</p>

<div id="grid-example" style="overflow: hidden; width: 535px; height: 225px;"></div>






Select the number of rows:
<select id=NumberOfRows onclick="refreshGrid()">
<option value="10">10</option>
<option value="30">30</option>
<option value="100">100</option>
<option value="1000" selected>1000</option>
<option value="10000">10000</option>
<option value="100000">100000</option>
<option value="1000000">1000000</option>
</select>
Select the simulated host delay:
<select id=HostDelay onclick="refreshGrid()">
<option value="0">no delay</option>
<option value="10">10 milliseconds</option>
<option value="50">50 milliseconds</option>
<option value="100">1/10 second</option>
<option value="200" selected>2/10 second</option>
<option value="500">1/2 second</option>
<option value="750">3/4 second</option>
<option value="1000">1 second</option>
<option value="2000">2 seconds</option>
</select>
</body>
</html>


The following is ext-1.0-alpha3\examples\virtualgrid\virtualgrid.js:


/*
* Ext - JS Library 1.0 Alpha 3 - Rev 1
* Copyright(c) 2006-2007, Jack Slocum.
*/

function refreshGrid(){
Ext.onReady(Example.init, Example, true);
};
var hostDelay; // simulate delay from the host. When the value is 0, the code completely bypasses the simulated host load.
var Example = {
init : function(){
// The following value controls the height of the virtual grid. When '30' is selected, the code uses preloaded data (for debugging purposes)
var numberOfRows = parseInt(document.getElementById('NumberOfRows').value);
hostDelay = parseInt(document.getElementById('HostDelay').value);
// some data yanked off the web
var myData = [
['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
['Altria Group Inc',83.81,0.28,0.34,'9/1 12:00am'],
['American Express Company',52.55,0.01,0.02,'9/1 12:00am'],
['American International Group, Inc.',64.13,0.31,0.49,'9/1 12:00am'],
['AT&T Inc.',31.61,-0.48,-1.54,'9/1 12:00am'],
['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
['Caterpillar Inc.',67.27,0.92,1.39,'9/1 12:00am'],
['Citigroup, Inc.',49.37,0.02,0.04,'9/1 12:00am'],
['E.I. du Pont de Nemours and Company',40.48,0.51,1.28,'9/1 12:00am'],
['Exxon Mobil Corp',68.1,-0.43,-0.64,'9/1 12:00am'],
['General Electric Company',34.14,-0.08,-0.23,'9/1 12:00am'],
['General Motors Corporation',30.27,1.09,3.74,'9/1 12:00am'],
['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
['Honeywell Intl Inc',38.77,0.05,0.13,'9/1 12:00am'],
['Intel Corporation',19.88,0.31,1.58,'9/1 12:00am'],
['International Business Machines',81.41,0.44,0.54,'9/1 12:00am'],
['Johnson & Johnson',64.72,0.06,0.09,'9/1 12:00am'],
['JP Morgan & Chase & Co',45.73,0.07,0.15,'9/1 12:00am'],
['McDonald\'s Corporation',36.76,0.86,2.40,'9/1 12:00am'],
['Merck & Co., Inc.',40.96,0.41,1.01,'9/1 12:00am'],
['Microsoft Corporation',25.84,0.14,0.54,'9/1 12:00am'],
['Pfizer Inc',27.96,0.4,1.45,'9/1 12:00am'],
['The Coca-Cola Company',45.07,0.26,0.58,'9/1 12:00am'],
['The Home Depot, Inc.',34.64,0.35,1.02,'9/1 12:00am'],
['The Procter & Gamble Company',61.91,0.01,0.02,'9/1 12:00am'],
['United Technologies Corporation',63.26,0.55,0.88,'9/1 12:00am'],
['Verizon Communications',35.57,0.39,1.11,'9/1 12:00am'],
['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am'],
['Walt Disney Company (The) (Holding Company)',29.89,0.24,0.81,'9/1 12:00am']
];

var ds = new Ext.data.VirtualStore({
length: numberOfRows,
proxy: new Ext.data.MemoryProxy(),
reader: new Ext.data.VirtualArrayReader({id: 0,length: numberOfRows}, [
{name: 'company'},
{name: 'price', type: 'float'},
{name: 'change', type: 'float'},
{name: 'pctChange', type: 'float'},
{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
])
});
ds.load();
ds.data.dummyData = new Ext.data.Store({
proxy: new Ext.data.MemoryProxy(myData),
reader: new Ext.data.ArrayReader({id: 0}, [
{name: 'company'},
{name: 'price', type: 'float'},
{name: 'change', type: 'float'},
{name: 'pctChange', type: 'float'},
{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
])
});
ds.data.dummyData.load();
if (numberOfRows == 30) {
ds = ds.data.dummyData;
}else{
for (i = 0; i < ds.data.dummyData.data.items.length; i++){
ds.data.items = ds.data.dummyData.data.items[i];
}
}

// example of custom renderer function
function italic(value){
return '[i]' + value + '';
}

// example of custom renderer function
function change(val){
if(val > 0){
return '<span style="color:green;">' + val + '</span>';
}else if(val < 0){
return '<span style="color:red;">' + val + '</span>';
}
return val;
}
// example of custom renderer function
function pctChange(val){
if(val > 0){
return '<span style="color:green;">' + val + '%</span>';
}else if(val < 0){
return '<span style="color:red;">' + val + '%</span>';
}
return val;
}

// the DefaultColumnModel expects this blob to define columns. It can be extended to provide
// custom or reusable ColumnModels
var colArray = [
{header: "Company", width: 200, sortable: true, locked:false, dataIndex: 'company'},
{header: "Price", width: 75, sortable: true, renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
{header: "Change", width: 75, sortable: true, renderer: change, dataIndex: 'change'},
{header: "% Change", width: 75, sortable: true, renderer: pctChange, dataIndex: 'pctChange'},
{header: "Last Updated", width: 85, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
];

// allocate excess width after resizing to the first column
var excessWidth = document.getElementById("grid-example").clientWidth;
for (i = 0; i < colArray.length; i++){
excessWidth -= colArray[i].width;
}
if (excessWidth > 0){
colArray[0].width += excessWidth;
}
var colModel = new Ext.grid.ColumnModel(colArray);

// create the Grid
var grid = new Ext.grid.VirtualGrid('grid-example', {
ds: ds,
cm: colModel
});
grid.render();

grid.getSelectionModel().selectFirstRow();
}
};

// This subclass of ArrayReader overloads the readRecords method to return an empty array and the specified value for totalRecords
Ext.data.VirtualArrayReader = function(meta, recordType){
Ext.data.VirtualArrayReader.superclass.constructor.call(this, meta, recordType);
};

Ext.extend(Ext.data.VirtualArrayReader, Ext.data.ArrayReader, {
readRecords : function(o){
return {
records : [],
totalRecords : this.meta.length
};
}
});

// This subclass of Store overloads several methods so that existing code continues to function with data that is not completely loaded
Ext.data.VirtualStore = function() {
Ext.data.VirtualStore.superclass.constructor.apply(this, arguments);
this.data = new Ext.util.VirtualMixedCollection(false);
this.data.length = this.length;
};

Ext.extend(Ext.data.VirtualStore, Ext.data.Store, {
getAt : function(index){
var row;
if (!this.data.itemAt(index)){
row=this.data.dummyData.data.itemAt(index % this.data.dummyData.data.length);
row.loadNeeded = index;
return row;
}else{
row = this.data.itemAt(index);
return row;
}
},
loadRow : function(row, view){
if ((','+this.rowLoadSpec.toBeLoaded.join(',')+',').indexOf(','+row+',') == -1){ // avoiding array.IndexOf because it is not supported in IE
var rowLoadSpec = this.rowLoadSpec;
rowLoadSpec.toBeLoaded[rowLoadSpec.toBeLoaded.length] = row;
if (!rowLoadSpec.requestProcessUnderway){
rowLoadSpec.requestProcessUnderway = true;
rowLoadSpec.ds = this;
rowLoadSpec.view = view;
setTimeout(function(){
rowLoadSpec.requestProcessUnderway = false;
rowLoadSpec.requestRows(rowLoadSpec.toBeLoaded.join(','));
rowLoadSpec.toBeLoaded = [];
}, this.loadDelay || 10);
}
}
},
rowLoadSpec : {
toBeLoaded : [],
requestProcessUnderway : false,
requestRows : function(rows){
ds = this.ds;
view = this.view;
setTimeout(function(){
requests = rows.split(',');
for (i = 0; i < requests.length; i++){
ds.data.items[requests[i]] = ds.data.dummyData.data.items[requests[i] % ds.data.dummyData.data.length];
}
view.refresh();
//alert("requesting rows " + rows);
},hostDelay || 1000);
}
},
getCount : function(){
return this.length;
}
});

// This subclass of MixedCollection overloads the clear and getRange methods so they work correctly with data that is not completely loaded
// It currently contains a dummyData property that is used solely for testing purposes
Ext.util.VirtualMixedCollection = function() {
Ext.util.VirtualMixedCollection.superclass.constructor.apply(this, arguments);
};

Ext.extend(Ext.util.VirtualMixedCollection, Ext.util.MixedCollection, {
clear : function(){
//this.length = 0; We need to retain the length
this.items = [];
this.keys = [];
this.map = {};
this.fireEvent("clear");
},
getRange : function(start, end){
var items = this.items;
start = start || 0;
end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
var r = [];
if(start <= end){
for(var i = start; i <= end; i++) {
if (!items[i]){
r[r.length] = this.dummyData.data.itemAt(i % this.dummyData.data.length);
r[r.length-1].loadNeeded = i;
}else{
r[r.length] = items[i];
delete r[r.length-1].loadNeeded;
}
}
}else{
for(var i = start; i >= end; i--) {
if (!items[i]){
r[r.length] = this.dummyData.data.itemAt(i % this.dummyData.data.length);
r[r.length-1].loadNeeded = i;
}else{
r[r.length] = items[i];
delete r[r.length-1].loadNeededed;
}
}
}
return r;
}
});

// This subclass of Grid substitutes the VirtualGridView and VirtualRowSelectionModel. It chains a renderer that detects when new data is needed
// This mechanism might not be needed if the grid supported a custom row renderer. A rowLoading template would also be useful.
Ext.grid.VirtualGrid = function() {
Ext.grid.VirtualGrid.superclass.constructor.apply(this, arguments);
var config = this.colModel.config;
for (i = 0; i < config.length; i++){
var grid = this;
config[i].origRenderer = config[i].renderer;
config[i].renderer = function(value, p, r, rowIndex, i, ds) {
if (r.loadNeeded){
if (hostDelay != 0){
ds.loadRow(rowIndex, grid.getView());
return (i == 0) ? "loading "+r.loadNeeded+" ..." : "";
}else{
if (typeof(config[i].origRenderer) == "undefined"){
return Ext.grid.ColumnModel.defaultRenderer(value);
}else{
return config[i].origRenderer((i==2)? r.loadNeeded : value, p, r, rowIndex, i, ds);
}
}
}else{
if (typeof(config[i].origRenderer) == "undefined"){
return Ext.grid.ColumnModel.defaultRenderer(value);
}else{
return config[i].origRenderer(value, p, r, rowIndex, i, ds);
}
}
}
}
};

Ext.extend(Ext.grid.VirtualGrid, Ext.grid.Grid, {
getSelectionModel : function(){
if(!this.selModel){
this.selModel = new Ext.grid.VirtualRowSelectionModel();
}
return this.selModel;
},
getView: function(){
if(!this.view){
this.view = new Ext.grid.VirtualGridView();
}
return this.view;
}
});

// This subclass of RowSelectionModel overrides the onRefresh method to handle selections relative to the value of view.topRow
// It currently only works correctly when the data has been preloaded (number of rows == 30)
Ext.grid.VirtualRowSelectionModel = function(){
Ext.grid.VirtualRowSelectionModel.superclass.constructor.apply(this, arguments);
}

Ext.extend(Ext.grid.VirtualRowSelectionModel, Ext.grid.RowSelectionModel, {
onRefresh : function(){
var ds = this.grid.dataSource, i, v = this.grid.view;
var s = this.selections;
s.each(function(r){
if((i = ds.indexOfId(r.id)) != -1){
i -= v.topRow;
if (i >= 0 && i < (v.topRow + v.viewRows - 1)){
v.onRowSelect(i);
}
}else{
s.remove(r);
}
});
}
});

// This subclass of GridView overrides several methods to virtualize the grid. The
Ext.grid.VirtualGridView = function() {
Ext.grid.VirtualGridView.superclass.constructor.apply(this, arguments);
var tpls = this.templates || {};
tpls.master = new Ext.Template(
'<div class="x-grid" hidefocus="true" style="overflow:hidden">',
'<div class="x-grid-topbar"></div>',
'<div class="x-grid-hscroller" style="overflow-x:auto;overflow-y:hidden"><div></div></div>',
'<div class="x-grid-locked">',
'<div class="x-grid-header">{lockedHeader}</div>',
'<div class="x-grid-body">{lockedBody}</div>',
'</div>',
'<div class="x-grid-viewport">',
'<div class="x-grid-header">{header}</div>',
'<div class="x-grid-body">{body}</div>',
// the following div contains an image that is resized to the pixel height of the virtual grid (row height * number of rows). The overflow style creates a scrollbar that accurately reflects the ratio of displayed text to the total height.
'<div class="x-grid-vscroller" tabindex="-1" style="scrollbar-base-color:#D4D5CB; height:100%; top:0; position:absolute; overflow: auto; left: auto; width: 17px; outline-color: invert; outline-style: none; outline-width: medium; right: -1px;">spacer.gif</div>',
'</div>',
'<div class="x-grid-bottombar"></div>',
'[/url]',
'<div class="x-grid-resize-proxy"> </div>',
"</div>"
);
tpls.master.disableformats = true;
this.templates = tpls;
};

Ext.extend(Ext.grid.VirtualGridView, Ext.grid.GridView, {
initElements : function(){
var E = Ext.Element;
var el = this.grid.container.dom.firstChild;
var cs = el.childNodes;

this.el = new E(el);
this.headerPanel = new E(el.firstChild);
this.headerPanel.enableDisplayMode("block");

this.scroller = new E(cs[1]);
this.scrollSizer = new E(this.scroller.dom.firstChild);

this.lockedWrap = new E(cs[2]);
this.lockedHd = new E(this.lockedWrap.dom.firstChild);
this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);

this.mainWrap = new E(cs[3]);
this.mainHd = new E(this.mainWrap.dom.firstChild);
this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
this.vscroller = new E(this.mainWrap.dom.lastChild);
this.vscroller.on("scroll", this.handleScroll, this);

this.footerPanel = new E(cs[4]);
this.footerPanel.enableDisplayMode("block");

this.focusEl = new E(cs[5]);
this.focusEl.swallowEvent("click", true);
this.resizeProxy = new E(cs[6]);

this.headerSelector = String.format(
'#{0} td.x-grid-hd, #{1} td.x-grid-hd',
this.lockedHd.id, this.mainHd.id
);

this.splitterSelector = String.format(
'#{0} div.x-grid-split, #{1} div.x-grid-split',
this.lockedHd.id, this.mainHd.id
);

if (typeof this.topRow == "undefinded") {
this.topRow = -1;
}
},
syncScroll : function(){
var sb = this.scroller.dom;
var vsb = this.vscroller.dom;
var sh = this.mainHd.dom;
var bs = this.mainBody.dom;
var lv = this.lockedBody.dom;
sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
if (!this.rowPixelHeight || parseInt(vsb.scrollTop/this.rowPixelHeight) != this.topRow){
this.refresh();
}else{
lv.scrollTop = bs.scrollTop = vsb.scrollTop % this.rowPixelHeight;
}
},
handleScroll: function(e){
this.syncScroll();
var sb = this.scroller.dom;
var vsb = this.vscroller.dom;
this.grid.fireEvent("bodyscroll", sb.scrollLeft, vsb.scrollTop);
e.stopEvent();
},
renderBody : function(){
var bs = this.mainBody.dom;
var lv = this.lockedBody.dom;
var bt = this.templates.body;
var vsb = this.vscroller.dom;
if (!this.rowPixelHeight){
markup = this.renderRows(0,0);
this.mainBody.update(bt.apply({rows: markup[1]}));
this.rowPixelHeight = bs.firstChild.clientHeight;
this.viewRows = parseInt(bs.clientHeight/this.rowPixelHeight)+2;
vsb.firstChild.style.height = ((this.ds.data.length + 1) * this.rowPixelHeight) + "px";
}
this.topRow = parseInt(vsb.scrollTop/this.rowPixelHeight);
lv.scrollTop = bs.scrollTop = vsb.scrollTop % this.rowPixelHeight;
var markup = this.renderRows(this.topRow, this.topRow + this.viewRows);
return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
}
});

var ResizableExample = {
init : function(){
var dwrapped = new Ext.Resizable('grid-example', {
wrap:true,
pinned:true,
width:535,
height:225,
minWidth:200,
minHeight: 50,
dynamic: true
});
dwrapped.on("resize", function(){
refreshGrid();
});
}
};
Ext.EventManager.onDocumentReady(ResizableExample.init, ResizableExample, true);
Ext.onReady(Example.init, Example, true);


The code makes a reference to spacer.gif, which is a 1x1 pixel image. You can find one of those at [url]http://us.i1.yimg.com/us.yimg.com/i/spacer.gif (#) and place it in the virtualgrid subdirectory.

I would very much like to have this incorporated into Ext, and I would be happy to change it to meet your architectural guidelines. Along those lines, I think that it would be helpful to have Ext provide for a custom row renderer that could be used to display the "loading..." line using a different template. Other than that, I have found that your architecture is remarkable and very easily extended.

-- Bill Eisner

jack.slocum
19 Mar 2007, 12:04 AM
Hi Bill,

I have ran the code and it is definitely a good start. There are a few things that need some tweaking and a few (undocumented) things built in I think could be leveraged as well.

The design pattern I considered for the store portion was a Decorator, rather than inheritance. This way, it can be used along with the existing store/readers and just provide an alternative interface/functionality such as virtual buffering. I haven't had a chance to play with it much though.

Thanks for sharing your code. Having a functioning example will definitely help when adding support for it in 1.1.

JeffHowden
19 Mar 2007, 12:04 AM
@Bill

I've got to say, I'm quite impressed with this. I'm sure it'll gain some additional ground after being slocumized, but geeze, it's already quite good. What surprises me is how little performance difference there is between 1,000 records and 1,000,000 records. I look forward to seeing what Jack thinks and can do with this.

amackay11
19 Mar 2007, 3:26 AM
@Bill,

Thanks for your work. I'm not sure the example works properly for me. I see data and the grid looks good but no scroll bar or way to scroll when I select 1000 rows etc. Am I missing something? I'm using FF, and get 'row has no properties' errors when I click on rows which I guess is what you mean by keyboard functions not working?

Andy

BillEisner
19 Mar 2007, 5:16 AM
Andy:

Did you download the yahoo gif file? The code needs that image in order to resize it (guess where the idea for the scrollbar came from!) I have only run the code in Firefox (I've used 1.5 and 2.0), so I can't vouch for IE. Obviously, the files all have to be in the right place, but strangely, enough, when they are incorrect, all I get is the scrollbar.

If you have Firebug, you can look at any errors it generates, but you are correct that the keyboard could cause other problems.

-- Bill

BillEisner
19 Mar 2007, 11:11 AM
Jack:

So what is a "decorator"? I certainly like the fact that you separate the grid from the grid data and the grid view, although I have a sense that there is a one-to-one relationship between a grid and a grid view. With the current implementation, it should be possible to have multiple grids working off of the same data. But can you have two views off of the same grid?

If you like the way the scrollbar works, you might want to consider using that universally. When I run with real data (number of rows=30) or without a host simulation (no delay), it behaves just like a regular scrolling table. But now you have much more control over the scrolling.

Do you agree that you should have a row renderer? I think that might have some applicability when all of the data is present, but it would be invaluable when you have to display something like "loading". Of course, when you build that functionality into the grid, it is more a matter of having a separate template for the "loading" row.

I'm anxious to find out about the undocumented features. Let me know how I SHOULD have coded it.

Cheers,

-- Bill

brian.moeskau
19 Mar 2007, 11:21 AM
http://en.wikipedia.org/wiki/Decorator_pattern

Used to add functionality at run-time as opposed to design-time (via inheritance). Using inheritance, you are locked into only having your subclass to work with to get your new functionality. Using a decorator, virtual loading functionality could easily be added generically to all existing (and future) data store classes without having to make them all inherit from your class.

BillEisner
20 Mar 2007, 10:59 PM
This is a bit off topic, but I was totally unaware of the notion of overriding methods at run time as "decorating". This seems to me a bit like multiple inheritance. In any case, I've found two interesting articles on Decorator patterns in JavaScript: http://beppu.lbox.org/articles/2006/08/22/the-decorator-pattern-for-javascript and an update http://beppu.lbox.org/articles/2006/09/06/actsasaspect. These seem to wrap existing methods, where all of my overrides are replacements. In JavaScript, it is easy to replace the functions of a specific method in a prototype or a class at runtime, and the second link provides a clever means of wrapping a subclassed method. But I'm afraid that I'm a little lost as to why, in this case, it would be better to decorate a Store with different buffering characteristics rather than just creating another Store class. Are there other classes or objects that would be decorated with buffering?

As an exercise, I've been trying to build an Ext.AbstractDecorator class based on the code in the second link and a derived Ext.data.StoreVirtualBufferDecorator class that would contain the methods that are currently in my Ext.data.VirtualStore class. Am I on the right track? Are there any examples of a decorator in the existing yui-ext?

-- Bill

jack.slocum
20 Mar 2007, 11:26 PM
The Decorator doesn't actually override any methods - it simple wraps it and provides new functionality. In most cases, the interface remains the same. A decorator that doesn't implement the original interface is not truly a decorator (in the traditional sense) but is very similar.

An example in Ext of a non-traditional decorator would be an Editor. It takes a field and converts it into a floating editor field, and doesn't care what kind of field it contains.

More traditional Decorators can be found in the Java programming language. e.g.

InputStream - An interface for reading streams

FileInputStream -> InputStream - An inputStream for reading files

BufferedInputStream -> InputStream - An InputStream the wraps around another InputStream to provide buffering functionality. It can wrap any other InputStream and implements the InputStream interface so it can be used as an InputStream itself.

Constructing a decorator generally consists of passing in an instance to be augmented. e.g.

var fis = new FileInputStream('/etc/passwd');
var bs = new BufferedInputStream(fis); <-- pass it existing stream to buffer

so in our case:

var bs = new Ext.data.BufferedStore(new Ext.data.Store(...));

"bs" can now be used as a store with the grid, although a VirtualGridView would require a BufferedStore so would be calling it's new functions which be (probably) all callback based.

Hopefully this makes sense.

BillEisner
22 Mar 2007, 1:07 PM
I guess I'm still confused about the plumbing. When you say that "'bs' can now be used as a store with the grid", that would only be the case if it exposed all of a Store's methods, right?

I get the idea of a run-time instance wrapper. I've done that sort of thing with languages that don't support inheritance. The decoration and AOP code that I linked to in my previous post wraps individual methods rather than the entire instance. Is there a conventional approach for wrapping an entire instance of a class? The only technique I can think of is to enumerate the class methods and attach each method to the wrapping class. That seems a great deal like subclassing, although I see the benefit of being able to wrap any instance rather than a specific class in the hierarchy.

All of this is quite relevant to my current activity (and gets back to the subject of this topic), since I need yet another store. I'm trying to add expandable/collapsible rows to your grid, and I want to do it in the VirtualGrid. My new ds, which I'm tentatively calling a VisibleStore, needs to be able to return only visible rows, based on which ones ones are collapsed or expanded. The new store would wrap a BufferedStore containing all of the rows plus a collection of BufferedStores -- one for each of the collapsible columns. I have figured out the algorithm for representing the data and returning the proper rows, but I need to know how to structure it. Before this discussion, I would have simply subclassed the Store yet again.

-- Bill

jack.slocum
26 Mar 2007, 2:56 PM
That is exactly the purpose of Decorators. You can add VisibleStore functionality to the any store, including a BufferedStore or vice versa. It is basically plug and play and can be "Extended" at runtime rather than having a subclasses that could end up like BufferedVisibleJsonGroupingStore.

The Decorator implements the full interface for the class it is extending. In many cases, it may be as simple as (assuming this.store is the wrapped store):

getCount : function(){
return this.store.getCount();
}

In others it may make sense to iterate with for...in and create a bound function on the wrapper instance. This would probably reduce code size as well.

BillEisner
26 Mar 2007, 6:50 PM
I think I got it.

So here's an idea. Why not generalize decoration with an Ext method that is comparable to Ext.extend. Something like:



Ext.decorate(Ext.data.VirtualArrayReader, Ext.data.ArrayReader, {
readRecords : {
before : function() {
alert("I'm about to read some records!");
return(arguments);
},
after : function(o){
return {
records : [],
totalRecords : this.meta.length
}
}
}
});


I really like the simplicity of extend, and I think there are lots of places where it would be useful to have a generalized mechanism to iterate over an existing instance to modify the behavior of a few methods while preserving the rest. Just a thought.

-- Bill

lstroud
26 Mar 2007, 7:06 PM
The problem is that when there are any more than four or five pages, then paging with a page number is useless too. I don't suppose there is anyway to support chunking the data by the type of the sorted column? For instance, if it where a table that listed employees you might sort by last name and then provide a paging index by first letter. I think that the same result can be accomplished with the table filtering that we talked about on another thread, but it sure would be nice to be able to offer a model that allows you to index the result set by something other than page number. Maybe in 1.1 :)

Thanx
LES

methodz
7 Jun 2007, 9:20 PM
I can tell you that the organization I am with would gladly pay the yearly top tier support fee if it meant you could help us add this feature. The same goes to anyone that has done it successfully, I am highly motivated to have this feature. Motivated enough to spend money out of my own pocket to benefit my organization. If anybody has a solution, please PM me.

B)

[Edit]

My motivation has driven me to a solution, it's working pretty nicely just need to get control over when the ajax events get fired (bodyscroll callback). If people are interested I'd love to show my example.

dnixon
16 Jul 2007, 12:15 PM
Where does this VirtualGrid functionality stand? Is the code in Bill's post from March 19th the place to start or is there a later version anyplace? Any issues using it with 1.1rc1?

Dave

LiXin
16 Jul 2007, 2:14 PM
I used "scrolling grid" instead of "paging grid" at http://www.publicschoolportal.com site, and the results are good.

Below is demo page which load best 100 US high schools into grid and show on google map:

http://www.publicschoolportal.com/BestSchools.aspx

brian.moeskau
16 Jul 2007, 2:55 PM
"scrolling grid" is not the same as "virtual grid" :) The previous posters mean loading paged data on demand via scrolling, not just using a scrollbar.

Nicholas
21 Jul 2007, 6:48 AM
This would be an increadibly sweet feature to offer.
Has there been any development on this?

knneth
21 Jul 2007, 10:58 AM
Siebel NetBusiness (popular CRM software) has been using scrollable grids for ages. I have grown accustomed to this feature, so my first disappointment when I saw the extjs grid component was its lack of support for just this feature.

For a html component suite with such a carefully designed architecture like extjs, I personally consider its lack of support for "scrolling grids" a flaw from a User Experience point of view. Pagination has always been a *****, not even Acrobat Reader (or Word for that sake) forces you to use it its pagination buttons by default - they allow you to scroll page by page. Think how painful it would be if you had to press a button each time you finished reading a page? Anything from 10 pages and up would make most people insane.

Bottom line is: Scrolling grids provide smoother workflows by allowing the use of the scroll wheel and removing the user distraction of eye-locating and pressing the pagination button. They will in most cases have a shorter learning curve (anyone with 6 months of computer mileage know how to scroll, but most are not accustomed to paging at the same time) and could for more experienced users provide a "feel" of where you are in the dataset. Pressing Next and seeing the blue marker move to row 1 could destroy the cozy user feeling of location in the dataset.

sjivan
21 Jul 2007, 11:15 AM
I think that users of the web are accustomed to paging though tabular data but there are special cases where having a virtual grid can be very handy. I believe Siebel uses Open Rico or rather the Open Rico virtual grid was donated by Siebel. The Open Rico virtual grid appears and works well but unfortunately the API isn't the best.

Sanjiv

cpantel
24 Jul 2007, 6:18 AM
We would also definitely use an Ext live-scrolling grid.

A live-scrolling grid that seems to perform well is the Isomorphic SmartClient Grid. See an example (http://www.smartclient.com/#_Grids_Data.operations_Databound.fetch).

They have a drawAheadRatio (http://www.smartclient.com/docs/5.6/a/b/c/go.html#attr..ListGrid.drawAheadRatio) parameter that controls how many rows are retrieved from the server before they need to be displayed. This allows you to control the trade-off between smooth scrolling (how often scrolling is interrupted to fetch more rows from the server) vs. initial load time and the amount of rows the browser has to deal with. They also manage caching rows and unloading rows when there are too many for the browser to deal with efficiently.

johnny
8 Aug 2007, 7:27 PM
Jack:

I have a strong need for a non-paging grid that can display a great deal of information. For this reason, I have subclassed a number of the grid-related Ext classes in order to add Rico-like functionality to the Ext.grid.Grid class. I am introducing the term "Virtual Grid" to describe a grid that displays data from a sparcely populated array, although I would welcome any other term for that capability. I developed the code on top of the array-grid example, and I have two files that can be dropped into a new ext-1.0-alpha3\examples\virtualgrid subdirectory to demonstrate the capability.
-- Bill Eisner


I have error then move cursor down under bottom :

_25.rows[_22] has no properties
chrome://firebug/content/blank.gifExt.grid.GridView=function(_1){Ext.grid.GridView.superclass.constructor.call(thi...
ext-all.js (line 240)

May be it dont work in because i have ext 1.1 or by any other reason?

Can you help?

violinista
16 Sep 2007, 11:35 PM
Is anyone interested here for developing some sort of scrolling grid? I am presently in deep work for that stuff and will, undoubtedly, publish that as extension for Ext. I am using ActiveWidgets (http://www.activewidgets.com/grid/) as model, since it contains powerful scrolling grid control.

Troy Wolf
12 Oct 2007, 9:26 AM
If a scrolling (rather than paging) grid can be made to work relatively smoothly, I think it's a great idea. I'm thinking specifically of a log viewer application I have. I really think users would find it natural to simply keep scrolling back in history to see log events rather than using standard pagination. I think you'd only keep 3 page views worth of records in memory -- current, previous, next. Whenever the previous or next page is scrolled into current view, you drop a page and load the next. Sure, if a user tried to fast scroll several pages in one direction, you'd have lag while data loads--depending on how fast your server and connection are.

8string
16 Oct 2007, 2:42 PM
I think what you want is a simple "double buffering" concept.

The table has 100 rows of data in memory, the user sees 50, as the user scrolls more rows are buffered to stay ahead of the user... I would like to know how to implement something like this too, or (better yet) make a feature request for such a mechanism in the framework.

Troy Wolf
25 Oct 2007, 1:01 PM
The more I think about this endlessly scrolling grid, the more I want it. My current Log Viewer app would benefit from this type of interface.

Pete Forde at Unspace is calling this "Endless Pageless"
http://unspace.ca/discover/pageless/

Bill Scott sheds light on Rico's LiveGrid in his blog:
http://looksgoodworkswell.blogspot.com/2005/06/death-to-paging-rico-livegrid-released.html

This Rico LiveGrid example seems to work pretty smooth for me. I like it better than 50 pages of "Next" to click.
http://www.dowdybrown.com/dbprod/rico-test9/php/ex2.php

Here is an example of a site using "pageless pagination". Scroll to the bottom of the articles, then watch the scrollbar move up as more articles are loaded into the page.
http://www.humanized.com/reader/

I'm told that Flash has a grid object that provides pagination through endless scrolling backwards and forward. It makes the page calls to the server, but the interface appears to just scroll endlessly until you hit the real end of the data. Anybody seen this?

Here's what I am confident about--if anybody can make a javascript based grid object with pageless pagination that works well and looks great, it's the Ext folks! As of right now, there does not seem to be much of a movement behind this, but I'd like to see that change. Maybe (EVENTUALLY) I'll get comfortable enough with Ext to develop this as a user extension. I kind of doubt it, though. ~o)

violinista
26 Oct 2007, 12:00 AM
I agree with you.

I started similar project a while ago, but just because lack of time, I stopped with development. Hope will continue soon.

For me, the best implementation of endless-scrolling (or "live" grid, in web2.0 terms) is done by Active widgets. Here is the demo:
http://www.activewidgets.com/general.demos/virtual-mode.html


Here is my list of known live-grid implementations and solutions and tutorials:
http://del.icio.us/violinista/largeGrid

brian.moeskau
26 Oct 2007, 1:42 AM
FYI, something like this will likely be coming in Ext in the future. Definitely not in 2.0, but we have started looking into it for a future release. There is no time frame or any specifics yet.

violinista
26 Oct 2007, 2:02 AM
Glad to hear it, ExtJs grid with this feature will be all-round player for many purposes!

Keep on good work, greetz

Troy Wolf
26 Oct 2007, 6:06 AM
FYI, something like this will likely be coming in Ext in the future. Definitely not in 2.0, but we have started looking into it for a future release. There is no time frame or any specifics yet.
The only downside to this otherwise great news is that it may prevent some eager developer from creating an InfiniteGrid user extension in the meantime. B)

Notice I am claiming credit for the term "Ext.grid.InfiniteGrid" and declare it the official name of this new, promised-by-Brian, just-around-the-corner Ext feature. ;)

Seriously, though, I'm glad to see that this is a feature the Ext team feels would be beneficial to the product. I'm encouraged by the community interest so far.

kenshin
27 Oct 2007, 7:03 AM
I think that Infinite Grid is very usefull! :)

I am evaluating to buy a license of a good JS UI Framework and Ext is on the top of the list, but at the moment in my applications i need to display tables with 3-4000 rows (without paging) ....and using Ext Grid 1.1.1 this is a problem.

Is there any workaround for display large tables (with column click sort) using ext 2.0?

For TroyWolf:
in your Extension you will implement also column click sort?

mdelanno
28 Oct 2007, 9:07 AM
I've a problem in one of my application where users want to select an entire dataset (200 to 300 rows). With a paging dataset, they can only select one page at a time.
It would be nice to have a scrolling grid in this case, but manage big selection sets can be problematic...:-?
Do you know one implementation that handle selection ?

As usual, sorry for my English... :D

mdissel
28 Oct 2007, 12:25 PM
I've a problem in one of my application where users want to select an entire dataset (200 to 300 rows). With a paging dataset, they can only select one page at a time.
It would be nice to have a scrolling grid in this case, but manage big selection sets can be problematic...:-?
Do you know one implementation that handle selection ?


You could save the selection between each page and add a shortcut/button to quickly (de-)select all records in every page.

Thanks
Marco

Troy Wolf
29 Oct 2007, 4:59 AM
For TroyWolf:
in your Extension you will implement also column click sort?
Uh, yeah, and it will also leverage synergy across all technological platforms with a single click. In fact, I just finished this plugin, and it's available for download now at my website.

Just kidding. I did not say I would write an extension. I mean, I wish I was in a position with Ext to do so, but at this point, I'm lucky to code myself out of a paper bag. So don't pin your hopes on me quite yet. ;)

sfrancolla
29 Oct 2007, 10:55 AM
FYI, something like this will likely be coming in Ext in the future. Definitely not in 2.0, but we have started looking into it for a future release. There is no time frame or any specifics yet.
Excellent to hear. I've seen iterations of "live scrolling" developed into various grids over the last 2 years. It seems that some of the mature js solutions are building it in and I think it's going to become, if it hasn't already, a staple amongst grid components globally. In fact, I'm shopping around for a good gwt wrapping solution and have been the Ext proponent in my group but the lack of this feature may give me less to work with as far as this otherwise impressive grid implementation goes. Keep up the great work.

Another we're looking at:
http://www.turboajax.com/products/turbogrid/

Which is an earlier version of what is anticipated to be released on Oct 31 with Dojo .9. We need this feature. :)

akannu
29 Oct 2007, 1:47 PM
You could save the selection between each page and add a shortcut/button to quickly (de-)select all records in every page.

Thanks
Marco

Does this mean that saving selections between pages would require additional code? Is it not a config option for grid paging behavior? Thanks!

kenshin
30 Oct 2007, 5:06 AM
Thank's for the reply Troy Wolf.

...I hope that Ext developers will help me (and not only me) working soon to this very interesting feature!!! :)

Kenshin

PS:turboajax grid demos are very impressive!

ThorstenSuckow
7 Nov 2007, 5:23 AM
Just wanted to let you all know that I have been working on a Live Grid for the last weeks.
It's almost done and I plan to release the first version within this week.

To add fuel to the fire: It works really, really good ;)

sfrancolla
7 Nov 2007, 5:33 AM
Just wanted to let you all know that I have been working on a Live Grid for the last weeks.
It's almost done and I plan to release the first version within this week.

To add fuel to the fire: It works really, really good ;)

...I've been testing the new Dojo 1.0 grid the last couple days and find it to be clunky and error-prone. It is feature-rich, but what's the point if simple events break it. Also, I don't want to give up Ext's strong MVC implementation.

I presume your implementation is being coded to support all variations of the grid? :)

ThorstenSuckow
7 Nov 2007, 5:48 AM
...I've been testing the new Dojo 1.0 grid the last couple days and find it to be clunky and error-prone. It is feature-rich, but what's the point if simple events break it. Also, I don't want to give up Ext's strong MVC implementation.

I presume your implementation is being coded to support all variations of the grid? :)

I needed a grid that's capable of virtually showing >10.000 rows at once without the need of paging (i.e. without using buttons), keeping selections between buffer-requests and selecting multiple ranges at once. I am introducing various events such as "versionchange" and "selectiondirty" which all should help in telling the user that the underlying data model has changed in any way and selections (represented by view indexes, not records) may have become inproper (i.e. pointing to records the user did not want to keep in the selection) while the user browses through the grid, thus constantly re-requesting data from the remote data model.

I have extended the GridView class that controls the scrolling of the user and tells the attached data store to re-load data, if needed. I also needed to extend the RowSelectionModel, since keeping selections in a data grid that only shows a subset of records available in the data model is somewhat different from the original implementation.
Only thing that will change is the way you delete records in the grid and request selections from the selection model. The way you set up the component does not differ from the current way you set up a grid. Oh, well - a few extra config options are there, but that's it.


Edit: Component is based upon Ext2.0 (beta1)

sfrancolla
7 Nov 2007, 5:52 AM
I needed a grid that's capable of virtually showing ~10.000 rows at once without the need of paging (i.e. without using buttons), keeping selections between buffer-requests and selecting multiple ranges at once. ...


You have no idea how timely this is. Looking forward to seeing it.

ThorstenSuckow
7 Nov 2007, 5:55 AM
You have no idea how timely this is. Looking forward to seeing it.

Working on the version property of the data store and deleting records in the viewable rect of the grid this evening. I should have set up an example until friday.

akannu
7 Nov 2007, 6:23 AM
You have no idea how timely this is. Looking forward to seeing it.

I second that. Is this being built on 1.1 or 2.0?

ThorstenSuckow
7 Nov 2007, 7:18 AM
I second that. Is this being built on 1.1 or 2.0?

It's based on 2.0. Works flawless with the RC1.

Troy Wolf
7 Nov 2007, 7:23 AM
Just wanted to let you all know that I have been working on a Live Grid for the last weeks.
It's almost done and I plan to release the first version within this week.

To add fuel to the fire: It works really, really good ;)
This sounds great, MindPatterns. I look forward to a demo. I started to develop this, but ran into a couple roadblocks quickly then had to move onto other production demands. I'm personally not that good at the cool OO javascript ways, so I'm really not the guy to build this. I'm very glad you are doing this extension. ~o)

ThorstenSuckow
10 Nov 2007, 11:29 AM
Here you go! :)

http://extjs.com/forum/showthread.php?p=84902#post84902

vivid-planet
15 Nov 2007, 9:38 AM
amazing! this work really great!
(even in konqueror!)

thanks for sharing...

rodehav
25 Mar 2008, 7:04 AM
Hi there!

I'm currently using Tibco GI but is considering Ext JS instead. I think Ext provides a much slicker/sexier look-and-feel. This is something I feel is lacking in Tibco GI. However, like so many others in these foras, we are very dependent on a high performant grid control.

The one in Ext seems to suffer from serious design flaws. As a comparison, our users (on an intranet) often retrieve > 10 000 rows which we render using GI's Matrix control (similar to Ext's Grid control). The loading from the server takes very little time, then it takes a few seconds for GI to cache the data on the client side. The actual rendering takes almost no time since only the currently visible page is actually rendered in HTML. To us this is definetely a show stopper. It seems like the only alternative in Ext is to use pagination (which we don't want - we want a single scrollable control making access to all rows seamless to the user) in combination with extra server calls for each page. This is not good enough.

I've seen references to something called Live Grid and to Rico 2.0. It seems like at least Rico 2.0 supports what I'm talking about. However, we would really want this kind of functionality to be supported in Ext JS so that we don't have to mix several Ajax libraries. In all other aspects Ext seems like a really slick framework and I cannot understand why there seems to be such resistance to providing a grid control that works for large volumes of data.

I