Login Register

Sorting and Other Dojo.Data Considerations

By default, when dojo.data datasources feed a Grid, the columns are not user-sortable. That's easy to rectify. Just set clientSort="true" in the tag:

<div dojoType="dojox.grid.data.DojoData" jsId="model"
        rowsPerPage="20" store="jsonStore" query="{ namespace: '*' }"
        clientSort="true">

</div>

Now the user can click on any column to sort it. You may also set the sort order programmatically:

// Sort 4'th field in ascending order
myGrid.setSortIndex(3, true);

Filtering

To filter a list, you can create a new adapter with a different query then attach it to the grid.

//Construct a new model.  The store needs to be passed into the constructor, as the constructor for the
//DojoData model examines the store and configures the model to support various features of the store
//such as Write, Identity, and Notification.  Setting the store after the constructor will leave the
//model in a misconfigured state and be unable to support data writebacks in the case of Write implementing
// datastores.
var newModel = new dojox.grid.data.DojoData(null,null,{rowsPerPage: 20, store: myStore, query: {type: someOtherType}, clientSort: true});
myGrid.setModel(newModel);
// Remember to call newModel.destroy() when you're done.

A popular use of filtering is to display a grid with everything, then let the user chop the list down incrementally. In this case, you can define an initial model with a minimal query. Save it, and then you can setModel back to it for quickly resetting all the filters. But do not keep unused models lying around! They take up memory.

Server Side Sorting and Paging with DojoData and QueryReadStore

EDIT: Fixed to use the full HTML input format, so you get the highlight code.

Out-of-box DojoData only supports sorting/paging on client side. A relatively straight-forward way to pass a query for server side sorting and paging uses dojox.data.QueryReadStore and requires altering few DojoData methods. (This snippet builds on a Dojox.Grid test - see that one first.)

<div dojoType="dojox.data.QueryReadStore"
    jsId="continentStore"
    url="continents.php"
    doClientPaging="false"
    >

<span dojoType="dojox.grid.data.DojoData"
    jsId="dataModel2"
    rowsPerPage="20"
    store="continentStore"
    query="{ name : '*' }"
    >

    <script type="dojo/method" event="requestRows" args="inRowIndex, inCount">
        // creates serverQuery-parameter
        var row  = inRowIndex || 0;
        var params = {
            start: row,
            count: inCount || this.rowsPerPage,
            serverQuery: dojo.mixin(
              { start: row,
                count: inCount || this.rowsPerPage,
                sort:(this.sortColumn || '')
              },
              this.query
            ),
            query: this.query,
            // onBegin: dojo.hitch(this, "beginReturn"),
            onComplete: dojo.hitch(this, "processRows")
        }
        this.store.fetch(params);
    </script>
    <script type="dojo/method" event="getRowCount">
        // should return total count (fetch from server), not "rowsPerPage"
        return 50;
    </script>
    <script type="dojo/method" event="sort" args="colIndex">
        // clears old data to force loading of new, then requests new rows
        this.clearData();
        this.sortColumn = colIndex;
        this.requestRows();
    </script>
    <script type="dojo/method" event="setData" args="inData">
        // edited not to reset the store
        this.data = [];
        this.allChange();
    </script>
    <script type="dojo/method" event="canSort">
        // always true
        return true;
    </script>
</span>

Programmatic version of server-side scrolling?

NOTE: I edited this comment to fix a couple of small errors, and now it has included a buch of weird CSS type stuff. Please ignore it.

Maine, thanks a lot for the sample above! It saved me a ton of work. But as you know, no good deed goes unpunished, so I have a follow up question. After some work, I got the above to work against my JSON-producing servlet and paging works without issue. Now I have two questions. The first concerns server side sorting. I'm not sure if this is not fully implemented in the example above, or if I need to rewrite my server code somehow, though I have tried it a few different ways without any luck. Basically, sorting is fine when going forward, but in reverse order (assuming a page size of 5), records come out in the following order: 5, 4, 3, 2, 1, 10, 9, 8, 7, 6, 15, 14, etc. In other words, each page is sorted properly, but the pages are still arranged in the original order. One question has to do with the "start" parameter that is passed to the server. In the case of reverse sort, should that be considered "end". In other words, should "start" indicate the high or low limit of the page?

The next question regards how to transform the above example from the declarative model to the programmatic. I have written a new class that extends dojox.grid.data.DojoData with the intent of replicating the above markup.

Here's what I have:

// An extension of dojox.grid.data.DojoData that supports server side
// paging and sorting
// ES 11/28/2007

dojo.provide("xyz.data.ServerGridData");

// dojox.grid._data.model is the module with dojox.grid.data.DojoData
dojo.require("dojox.grid._data.model");

// our declared class
dojo.declare("xyz.data.ServerGridData",
        // we inherit from this class
        dojox.grid.data.DojoData,
        // class properties:
        {
       
        // custom override of requestRows()
        requestRows: function (inRowIndex, inCount)
            {
                console.log("in requestRows()");
                // creates serverQuery-parameter
                var row  = inRowIndex || 0;
                var params = {
                    start: row,
                    count: inCount || this.rowsPerPage,
                    serverQuery: dojo.mixin(
                      { start: row,
                        count: inCount || this.rowsPerPage,
                        sort:(this.sortColumn || '')
                      },
                      this.query
                    ),
                    query: this.query,
                    // onBegin: dojo.hitch(this, "beginReturn"),
                    onComplete: dojo.hitch(this, "processRows")
                }
                this.store.fetch(params);
            },

       
        getRowCount: function ()
        {
            console.log("in getRowCount()");
             //should get row count from server
            // for now just return 1000
            return 1000;
        },

        // src: String
        //      src url for AccordionPaneExtension header
        setData: function(inData)
        {
            console.log("in setData()");
            // edited not to reset the store
            this.data = [];
            this.allChange();
        },
        sort: function()
        {
            this.clearData();
            this.sortColumn = colIndex;
            this.requestRows()
        },
        canSort: function ()
        {
            // always true
            return true;
        }

});

I then have an HTML page that includes:

<div dojoType="dojox.data.QueryReadStore"
            jsId="serverStore"
            url="pbgridapi?action=getLineItems"
            doClientPaging="false"
            />


       <span dojoType="xyz.data.ServerGridData"
        jsId="serverModel"
        rowsPerPage="20"
        store="serverStore"
        query="{ value : '*' }"
        />


        <div id="grid2" dojoType="dojox.Grid" elasticView="2"
            model="serverModel" structure="simpleLayout">

        </div>

This sort of works in the sense that it loads my custom class; the table appears and the first 20 rows are fetched from the server, but none of my overridden functions get called. Breakpoints in Firebug never get hit, the console statements never execute, etc.

So maybe this is just me misunderstanding how to extend a class with Dojo, but I've tried to follow the examples and can't see what is wrong.

Thanks in advance for any suggestions.

Rhyme & Reason
Online tools for poets and lyricists

Inheriting DojoData-model Requires Overriding Markup Factory

No problem. I'm not quite sure I understood your sorting/paging problem. However, I can help you with the other problem. Classes that inherit DojoData need to override method called markupFactory. In DojoData it's defined:

markupFactory: function(args, node){
    return new dojox.grid.data.DojoData(null, null, args);
},

MarkupFactory is sort of a constructor for object instances created from markup. If it's not overridden in a subclass, it will only create instances of DojoData, not the subclass.

calls superclass constructor

More generally: if a class is instantiated from tags ("declaratively") and doesn't have a markupFactory defined, then the constructor of its superclass will be used -- not its own constructor!

Great, thanks.

I was under the false impression that markupFactory was required for visible components; didn't realize that it was required to be created from a tag, visible or not.

I'll figure out the sorting issue and post the code here later. I really appreciate your help.

Rhyme & Reason
Online tools for poets and lyricists

Grid editors don't update the model, and where does paging ...

...actually come from? I've implemented this same code moving the markup to custom DojoData as nihou did. The grid gets data from the server at load like it should. I added a cell editor, and can edit a row. However, a model observer for ModelRowChange never fires and therefore I can't do an xhrPost back to the server b/c I don't know the data's changed.

An additional problem is that after the initial data load to the grid (5 rows for example), what do I do to trigger getting the next set of rows from the server via QueryReadStore? Scrolling the grid doesn't do it. Am I supposed to provide a button or something? I tried coding a button that tells the store to fetch more data, and then a grid.update() command. I saw that the url was called and data brought back, but nothing updated in the grid.

I'm looking more for procedure here than code. I can see where the QueryReadStore url call provides start and count for it's initial call. It seems like the grid should be triggering new QueryReadStore url calls where the count and start are automatically figured out. What am I missing?

auto-scrolling, etc.

Lance,

You don't need a button. I was able to get the auto scrolling to work against the server when the scroll bar is used. Not sure what the issue is for you, but you should make sure that the grid thinks the total number of rows is large (I hard coded 1000 to test). Also, try making the page size larger. I used 20. I don't have my code in front of me right now, but will try to post it later.

As for editing, I haven't implemented that, so I don't know how to solve it, but I'm not surprised it doesn't work with the sample given; I don't believe it is implemented.

Rhyme & Reason
Online tools for poets and lyricists

Virtual scrolling triggers requestRows and loads new pages

As scrollpane already pointed out above, scrolling the table (Virtual scrolling) triggers requestRows and fetches data from server. Just check and try the example on this page.

This call stack gives you a fairly good picture of the procedure of turning scrolling into loading new pages from the server (grid->model->store->server):
scroller.base.scroll
scroller.base.needPage
scroller.base.buildPage
scroller.columns.renderPage
views.renderRow
GridView.renderRow
GridView.buildRow
GridView.buildRowContent
contentBuilder.generateHtml
cell.format
VirtualGrid.get
data.DojoData.getDatum
data.Dynamic.getRow
data.Dynamic.preparePage
data.Dynamic.needPage
data.Dynamic.requestPage
data.DojoData.requestRows
QueryReadStore.fetch
QueryReadStore._fetchItems

Thanks for helping, have it working now!

I took a look at the 2 variables that influence this. If you have DojoData's rowsPerPage field set higher than the rowCount returned by getRowCount, you end up with duplicated data in the grid. Not good.

When you have rowCount>rowsPerPage, the scroller now performs as expected and QueryReadStore requests are made to the backend automatically. The count=30 it asks for is equal to rowsPerPage=30 setting I'd put in for DojoData.

So, problem resolved. However, I'm not comfortable with the getRowCount function as the comment in it says to replace the hardcoded value with that of the QueryReadStore's fetch. That's not correct either. It assumes the fetch will be returning the entire data set from the server at once. As a working matter, this would probably result in an initial setting that is sufficiently large to guarantee paging, and then at some point an AJAX call has to be made to see what the actual size of the entire data set is.

Anyway, minor issue. Thanks for getting me over the hurdle.

Problematic getRowCount

To clarify, in my example the "fetch from server" means that you would make a separate query to get the row count (data set size) - just as Lance pointed out. To get the grid render nicely you'd postpone rendering until you have this number. It seems like DbTable and yahooSearch models also fetch row count on a separate query (see Grid Mysql table editing).

However, this technique works nicely as long as you're only sorting. When you add server side filtering, it gets complicated as different filters cause the data set size to vary. As a consequence, you'd always make two queries to fetch the data - one to get the count and second one to fetch the actual row content. (Plus, if you're fetching stuff from different db tables, you'd also query for the grid structure.)

getRowCount

In case anybody might find this helpful, here's the implementation of getRowCount I'm using. It works, but is synchronous. Have not yet tried to get it to work asynch...

getRowCount: function () //calls the server to get the number of rows
        {
            console.log("in getRowCount()");
            var rowCount = 0;

            dojo.xhrGet( {

                preventCache: true,
                url: "/gridapi?action=getNumLineItems",
                handleAs: "text",
                sync: true, //may be a bad idea, but for now make this synchronous
                timeout: 5000, // Time in milliseconds

                // The LOAD function will be called on a successful response.
                load: function(response, ioArgs) {
                  console.log("in getNumLineItems callback");
                  var result = eval('(' + response + ')');
                  rowCount = result.numLineItems;
                   console.log("rowCount = " + rowCount);
                },

                // The ERROR function will be called in an error case.
                error: function(response, ioArgs) {
                  console.error("HTTP status code: ", ioArgs.xhr.status);
                  return response;
                  }
                });
           
            return rowCount;
        }

Rhyme & Reason
Online tools for poets and lyricists

I just cannot get this to work, any help would be great

/ext/js/dj102 is the path to my install base of dojo

I have for my main html page


	
	Test my inherited Grid
	
		@import "/ext/js/dj102/dojox/grid/_grid/tundraGrid.css";
		@import "/ext/js/dj102/dijit/themes/tundra/tundra.css";
		@import "/ext/js/dj102/dojo/resources/dojo.css";
		#DataGrid {border: 1px solid #333;width: 49em;height: 30em;}
 	

	
	
		var gridLayout = [{
			cells: [[
					{name: 'Title' , width: "25em", field: "title"},
					{name: 'Style', width: "10em", field: "style"},
					{name: 'Finish', width: "10em", field: "finish"}
			]]
		}];     
		dojo.require("php.dynamicGridData");
	



	

And have a class defined in /ext/js/dj102/php/dynamicGridData.js that looks like this

dojo.require("dojox.data.QueryReadStore");
dojo.require("dojox.grid.Grid");
dojo.require("dojox.grid._data.model");
dojo.provide("php.dynamicGridData");
dojo.declare("php.dynamicGridData", dojox.grid.data.DojoData, 
{
	markupFactory: function(args, node)
	{
		return new dojox.grid.data.DojoData(null, null, args);
	},

	requestRows: function (inRowIndex, inCount) 
	{
		console.log("request");
		var row  = inRowIndex || 0;
		var params = 
		{
			//	onItem:gotone,
			start: row,
			count: inCount || this.rowsPerPage,
			serverQuery: dojo.mixin(
			{ start: row,
			count: inCount || this.rowsPerPage,
			sort:(this.sortColumn || '')
			}, this.query ),
			query: this.query,

			onComplete: dojo.hitch(this, "processRows")
		}
		this.store.fetch(params);
	},

	getRowCount: function () 
	{
		return 7000;
	},

	setData: function(inData) 
	{
	    this.data = [];
	    this.allChange();
	},

	sort: function(colIndex) 
	{
	    this.clearData();
	    this.sortColumn = colIndex;
	    this.requestRows(); 
	},

	canSort: function () 
	{
	    return true;
	}

} );

The class I created just does not seem to come alive (instantiated). I get the first pull of data from the server and nothing else happens. Its like I have the default behavior of dojox.grid.data.DojoData. I have no errors or indications reported in firebug. I have a feeling it may have to do with the order of loading of the js but as I am very new to dojo and looked everywhere at every relevant object and method call without success I hope someone out there might be able to assist

I have had great results and love the grid when using inline html. The problem for me is that doing it that way will result in horrible maintenance but just so other people that may be viewing this thread can see it works and works so well its amazing THANK YOU DOJO PEOPLE!!

<head>
        <title>Test dojox.Grid PHP on demand</title>
        <style type="text/css">
                @import "/ext/js/dj102/dojox/grid/_grid/tundraGrid.css";
                @import "/ext/js/dj102/dijit/themes/tundra/tundra.css";
                @import "/ext/js/dj102/dojo/resources/dojo.css";
                #grid {border: 2px solid #333;width: 49em;height: 30em;}
        </style>

        <script type="text/javascript" src="/ext/js/dj102/dojo/dojo.js" djConfig="parseOnLoad: true"></script>
        <script type="text/javascript">
        dojo.require("dojox.data.QueryReadStore");
        dojo.require("dojox.grid.Grid");
        dojo.require("dojox.grid._data.model");
               
        var layoutitems = [{
                cells: [[
                                {name: 'Title', width: "25em", field: "title"},
                                {name: 'Style', width: "10em", field: "style"},
                                {name: 'Finish', width: "10em", field: "finish"}
                ]]
        }];   
        </script>
</head>

<body class="tundra">

       
<div dojoType="dojox.data.QueryReadStore"       jsId="continentStore"  url="dojogridresp.php"    doClientPaging="false">
<div dojoType="dojox.grid.data.DojoData"        jsId="dataModel2"       rowsPerPage="20"       store="continentStore" query="{ id : '*' }">
<div id="grid" dojoType="dojox.Grid" elasticView="2" model="dataModel2" structure="layoutitems"> </div>
   
    <script type="dojo/method" event="requestRows" args="inRowIndex, inCount">
        // creates serverQuery-parameter
        var row  = inRowIndex || 0;
        var params = {
            start: row,
            count: inCount || this.rowsPerPage,
            serverQuery: dojo.mixin(
              { start: row,
                count: inCount || this.rowsPerPage,
                sort:(this.sortColumn || '')
              },
              this.query
            ),
            query: this.query,
            // onBegin: dojo.hitch(this, "beginReturn"),
            onComplete: dojo.hitch(this, "processRows")
        }
        this.store.fetch(params);
    </script>
   
    <script type="dojo/method" event="getRowCount">
        // should return total count (fetch from server), not "rowsPerPage"
        return 7000;
    </script>
   
    <script type="dojo/method" event="sort" args="colIndex">
        // clears old data to force loading of new, then requests new rows
        this.clearData();
        this.sortColumn = colIndex;
        this.requestRows();
    </script>
   
    <script type="dojo/method" event="setData" args="inData">
        // edited not to reset the store
        this.data = [];
        this.allChange();
    </script>
   
    <script type="dojo/method" event="canSort">
        // always true
        return true;
    </script>
       
       
</body>

AND THE PHP dojogridresp.php

<?
// Sort out some constants
define("CONFIG_DATABASE_NAME"		,"somedb");
define("CONFIG_DATABASE_USER"		,"someuser");
define("CONFIG_DATABASE_PASSWORD"	,"somepassword");
define("CONFIG_DATABASE_HOST"		,"localhost");

// A basic function to connect to the databse
function dbconnect()	
{
	$db = mysql_connect(CONFIG_DATABASE_HOST, CONFIG_DATABASE_USER, CONFIG_DATABASE_PASSWORD);
	if (!$db)
	{
		echo "function dbconnect() failed at mysql_connect() " . mysql_error() ;
		exit;
	}
	mysql_select_db(CONFIG_DATABASE_NAME, $db);
	return $db;
}
// Read the incoming get / post vars from the dojo widgets requestRows method (names are determined by your own scripts)

$start = $_REQUEST["start"];
$count = $_REQUEST["count"];
$query = $_REQUEST["id"];
$sort = $_REQUEST["sort"];

// Make some simple decisions on what they mean

// are we looking for specific titles ?
if ($query !="*")
	$filter = " WHERE title LIKE '" . $query . "%' ";
else
	$filter = " WHERE title > ' ' ";
	
// How are we going to sort the results ?
if ($sort == 2)
	$order = "style";
elseif ($sort == 3)
	$order = "finish";
elseif ($sort == -1)
	$order = "title DESC";
elseif ($sort == -2)
	$order = "style DESC";
elseif ($sort == -3)
	$order = "finish DESC";
else
	$order = "title";

// Finally lets do it.. connect to the db
$db		= dbconnect();
// Form the squirrel query
$sql 		= "SELECT title, style, finish, description  FROM inventory " . $filter . " ORDER BY " . $order . " LIMIT " . $count . " OFFSET " . $start;

// build the response data in an array (its easy that way cause PHP can transform arrays to JSON in a one liner)
$i		=0;
$result	= mysql_query($sql,$db);

while ($row = mysql_fetch_array($result)) 
{
	$node = array("title"=> $row["title"] , "style" => $row["style"], "finish" => $row["finish"]);
	$returnArray[$i] = $node;
	$i++;
}
//Send the data back to the dojo widget

header("Content-Type", "text/json");
echo  '/*'.json_encode(array('items'=>$returnArray)).'*/'; 
?>

with regards

Inheriting DojoData-model Requires Overriding Markup Factory

Sounds familiar... Check my comment above: Classes that inherit DojoData need to override method called markupFactory.

Would a real world example be possible ?

Hi Maine,
I had checked your info above before my initial posting. I have in my script above the following. This is how I understood to declare the class based on the information on declaring a class in the The book of Dojo.

dojo.declare("php.dynamicGridData", dojox.grid.data.DojoData,
{
        markupFactory: function(args, node)
        {
                return new dojox.grid.data.DojoData(null, null, args);
        }
});

Thanks

Override markup factory to return instance of your own class

A little confusion here - I meant to provide that markupFactory as sample that should be overridden to return an instance of your own class. So, in your case the markupFactory would be:

dojo.declare("php.dynamicGridData", dojox.grid.data.DojoData,
{
        markupFactory: function(args, node)
        {
                return new php.dynamicGridData(null, null, args);
        }
});

Hope this helps!

Plus, when looking at markup factory example on parser page it seems like the class is unnecessarily hard-coded in DojoData-class. The following markupFactory should do it for everyone (Haven't tested yet):

markupFactory: function(params, domNode, constructorFunction){
{
  return new constructorFunction(null, null, params);
}

Thank you

Maine,
Thanks very much your a star. They both seemed to work !!
I also want to thank scrollpane you both put some very useful examples up. I now have a working editable data grid (its got its issues) but very cool. I will be happy to paste the scripts but don't want to clutter this page with too much. I wonder if it is possible to replace my old posts

Thanks again everybody

You can edit your posts

Glad that I could help. It's a good practice to share your working code here for the others. You can always edit your posts.

Hi Maine regarding editing content

It seems once someone has replied to your post the edit feature goes away. Oh well, will just have to clutter all these pages with my stuff.
Have a great weekend

Didn't get it...

Hi!

I've read all the thread but I'm sure there's something I don't get because my grids pagination does not work. When I use the scrollbar I can see in Firebug that the QueryReadStore store is fetching new records from the DB, but they aren't passed to the DojoData instance so the grid never show them. Here's my code (it's a little messy because of all the dirty thing I'm trying with it!):

// creates serverQuery-parameter var row = inRowIndex || 0; var params = { start: row, count: inCount || this.rowsPerPage, serverQuery: dojo.mixin( { start: row, count: inCount || this.rowsPerPage, sort:(this.sortColumn || '') }, this.query ), query: this.query, // onBegin: dojo.hitch(this, "beginReturn"), onComplete: dojo.hitch(this, "processRows") } var dataurl = "/cake/products/dojoindex/" + (this.sortColumn || 'title') +"/asc/"+row+"/"+inCount || this.rowsPerPage; this.store.url = dataurl; this.store.fetch(params); console.log("in getRowCount()"); // should return total count (fetch from server), not "rowsPerPage" return 300; console.log("in sort()"); // clears old data to force loading of new, then requests new rows this.clearData(); this.sortColumn = colIndex; this.requestRows(); console.log("in setData()"); // edited not to reset the store this.data = []; this.allChange(); console.log("in canSort()"); // always true return true;

The URL change in the requestRows method is there because I'm working with CakePHP and it uses this nice URLs to pass parameters to the server methods. Could be that the problem?

Thanks a lot in advance!!

Malformatted data?

Hi Maine!Sorry I didn't

Hi Maine!

Sorry I didn't reply earlier. I read those two threads some time ago (in fact, I opened the second one!), and I know my data is well formatted because the grid loads it with no problem. The problem is that it only loads the data returned by the very first query, not all the rest. Firebug tells me that when I scroll down dojo asks for more data, and that it's loaded right in the store, but not in the grid.

Thanks a lot anyway!!

Update to my data grid using php and mysql

So I have been working with this for a couple of days now and seem to have got an editable data grid working on a LAMP setup. A special thanks to Maine for answering all my silly questions. I must warn you all I am probably like the blind leading the blind as far as dojo and javascript so you may see things I have done that work but I probably should not have done or there are more syntactically correct ways in dojo / javascript. Of course I had to jump head first into dojo using the grid control as my starting point..... There are some strange things happening for instance I have two combobox editors that on occasion just stop accessing the datastore. I have not done any test cases with this code just clicked around here or there and for the most part it works. I hope that someone might find this useful and if anyone has any comments or suggestions that would be great !!

So there are four files. Two are extensions of existing classes which i placed in a php subdirectory off the dojo stuff
dojox.grid.editors.ComboBox newComboBox.js
dojox.grid.data.DojoData newGridData.js
The main html file dojogrid.html
The PHP stuff dojogridresp.php

newComboBox.js

dojo.provide("php.newComboBox");
dojo.declare("php.newComboBox", dojox.grid.editors.ComboBox,
{
        getEditorProps: function(inDatum)
        {
                if(typeof this.cell.options != "undefined")
                {
                        var items=[];
                        dojo.forEach(this.cell.options, function(o) {items.push({name: o, value: o});});
                        var store = new dojo.data.ItemFileReadStore({data: {identifier:"name", items: items}});
                        this.cell.editor.editorClass.superclass.searchAttr = "name";
                }
                else    //if no options set, check for a store
                {
                        if(typeof this.cell.store != "undefined")       //check that a store is specified
                        {
                                var store = this.cell.store;
                                this.cell.editor.editorClass.superclass.searchAttr = (typeof this.cell.searchAttr != "undefined") ? this.cell.searchAttr : "name";      //set the searchAttr based on user specified searchAttr if set; otherwise, default to name
                        }
                        else
                        {
                                console.error("Error setting store for dojox.grid.editors.ComboBox...");
                        }
                }
                return dojo.mixin({}, this.cell.editorProps||{}, { value: inDatum, store: store});
        }
}
);

newGridData.js

dojo.provide("php.newGridData");
dojo.declare("php.newGridData", dojox.grid.data.DojoData,
{
        testparam: "",                    // default parameter from markup
        markupFactory: function(params, domNode, constructorFunction)   // Used to create the object instance rather than the constructor
        {
                var instance = new php.newGridData(null, null, params);
                // Do any initialisation here
                return instance;
        },

        requestRows: function (inRowIndex, inCount)                     // Fetch rows of the grid
        {                                                                                    // Return from the server is in JSON format
               
                var row  = inRowIndex || 0;
                var params =
                {
                        start: row,
                        count: inCount || this.rowsPerPage,
                        serverQuery: dojo.mixin(
                        { start: row, count: inCount || this.rowsPerPage, sort:(this.sortColumn || '')},
                        this.query ),
                        query: this.query,
                        onComplete: dojo.hitch(this, "processRows")
                }
                this.store.fetch(params);
        },

        getRowCount: function ()                                                  // Returns the total number of records for the query   
        {                                                                             // The return value is a simple number
                var qtype = "";        // As you can see I have no idea of what I am doing.
                var qvalue = "";                                                                // I think this.query is some kind of assosiative array but dont know any dojo methods to access it cleanly     
                var serverURL = this.store.url + "?action=getTotalRowCount&";
                for (ent in this.query)
                {
                        qtype = ent;
                        qvalue = this.query[ent];
                }
                serverURL +=qtype + "=" + qvalue;
                               
                dojo.xhrGet(
                {
                        preventCache: true,
                        url: serverURL  ,
                        handleAs: "text",
                        sync: true,
                        timeout: 5000,
                        load: function(response, ioArgs)                // The LOAD function will be called on a successful response.
                        {
                                rowCount = parseInt(response);
                        },
                        error: function(response, ioArgs)                         // The ERROR function will be called in an error case.
                        {
                                console.error("HTTP status code: ", ioArgs.xhr.status);
                                return response;
                        }
                }
                );
       
                return rowCount;
        },
       
        setData: function(inData)                                           // Called at startup to Initialize the grids data                    
        {
            this.data = [];
            this.allChange();
        },

        sort: function(colIndex)                                                  // Called when user clicks on grid header to change sort order
        {
            this.clearData();                        // Clear the grids data
            this.sortColumn = colIndex;      // set the sort order
            this.requestRows();                                                         // fetch the new data
        },

        canSort: function ()                         // Determines if clicking on the grid header will resort the data
        {
            return true;
        },
       

       
       
} );

dojogrid.html


	
	Test a Dojo Grid
	
		@import "/ext/js/dj102/dojox/grid/_grid/tundraGrid.css";
		@import "/ext/js/dj102/dijit/themes/tundra/tundra.css";
		@import "/ext/js/dj102/dojo/resources/dojo.css";
		#DataGrid {width: 59em;height: 28em;}
 	

	
	
		dojo.require("dojo.parser");
		dojo.require("dojox.data.QueryReadStore");
		dojo.require("dijit.form.ComboBox");
		dojo.require("dojox.grid.Grid");
		dojo.require("dojox.grid._data.model");
		dojo.require("dojox.grid._data.editors");
		dojo.require("dojox.grid._data.dijitEditors");
		dojo.require("php.newGridData");
		dojo.require("php.newComboBox");
		dojo.require("dojo.data.ItemFileReadStore");
		
		var isGridInserting = false;	// Global to track the insertion state of the grid. Yep its a kludge I am sure there are better ways
		var recIdToDelete = 0;	 	// global that stores the recid of the row we are going to delete (issue with me or Dojo)
		var theGrid;				// Reference to the dojo grid widget
		var theForm;				// Reference to a hidden html form
					
		var styleStore = new dojo.data.ItemFileReadStore({jsId: 'styleStore', url: "/dojogridresp.php?action=getStyleTypes"});
		var finishStore = new dojo.data.ItemFileReadStore({jsId: 'finishStore', url: "/dojogridresp.php?action=getFinishTypes"});
		var gridLayout = [{
			cells: [[
					{name: 'Title' , width: "25em", styles: 'text-align: left;', field: "title", editor: dojox.grid.editors.Dijit, trim: true, propercase: true, required: true} ,
					{name: 'Style', width: "15em", styles: 'text-align: left;', field: "style",  editor: php.newComboBox, searchAttr: "style", store: styleStore },
					{name: 'Finish', width: "15em", styles: 'text-align: left;', field: "finish", editor: php.newComboBox, searchAttr: "finish", store: finishStore},
					{name: 'RecId', width: "0px",  styles: 'display:none;', field: "id"}
					
				]]
			}];     

		// Fired by button new item
		function addRow() 	
		{
			theGrid.addRow([ "Unknown", "", "", 0]); // The defaults dont get set like this. Needs some more investigation.
		}
	
		// Fired by button delete selected item
		function deleteRow()
		{
			var theSelectedRow =  theGrid.selection.getSelected(); // An array of rowid items
			if (theSelectedRow.length > 0 ) 
			{
				if (theSelectedRow.length > 1 ) 
				{
					alert("you have selected several items to delete. I cant do that !"); // would use sql in() if going to put it to use
				}
				else
				{
					recIdToDelete = theGrid.model.data[theSelectedRow].id
					if (confirm("Delete item " + theGrid.model.data[theSelectedRow].title + " recId=" + recIdToDelete) )
						theGrid.removeSelectedRows();
						
					theGrid.refresh();
				}
			}
			else
			{
				alert("Please select an item to delete first");
			}
			
		}


		/*
		Fired by button Set Filter simple test to see if we can requery the sql table with a where clause. 
		Appears to work however the grids internal structure on occasion seems to get destroyed as get several
		errors when trying to edit a field on the grid such as i has no properties (VirtualGrid.js line 351)
		*/
		function changeFilter() 
		{
			var filterData =  dojo.byId("filter");
			theGrid.model.query.filter= filterData.value;
			theGrid.model.clearData();
			theGrid.model.requestRows();
		}

		// blocking call  to post the form to the server and wait for response in JSON
		function submitForm() 
		{
		retval = "";
			dojo.xhrPost( 
				{	
				url: theGrid.model.store.url,
				form: 'updateFieldForm',
				handleAs: "json",
				sync: true,	// block until done or timeout
				timeout: 5000,
				load: function(response) { retval = response;},
				error: function (error) { retval = error; }
				}	
			);
			return retval;
		}
		
		
		function processResponse(response)
		{
			isGridInserting = false;
			recIdToDelete=0;
	
			if (response.name == "Error")	// Problem at the transport level
			{
				// now we have to do something to sort out the problem and not make the user have to retype the data and or resync the table 
				// but for today we will let the user curse us
				alert ("opps something went wrong " + response.message);
			}	
			else
			{
				if (response.result.error)	// Problem from the app
				{
					alert("opps could not do that " + response.result.message);
				}
				else
				{
					if (response.result.action == "insertRow")	// We need to populate the grid with the recid of the newly created row
						theGrid.model.data[response.result.rowId].id = response.result.recId;	
					
					//alert(response.result.message); // Silence is golden
				}
			}
			theGrid.update(); 
		}

	
		// Fired when a key such as the enter key has been pressed on a cell being edited
		// It is probably wise to check if the data on the server has changed since the grid might contain stale data. ahh too much trouble for now.
		function dataGridApplyCellEdit(value,rowIndex,columnIndex)
		{
			theForm.dataFieldName.value	= theGrid.layout.cells[columnIndex].field;
			theForm.dataFieldValue.value	= value;
			theForm.recId.value 			= theGrid.model.data[rowIndex].id;
			theForm.rowId.value 			= rowIndex;
			if (isGridInserting)
				theForm.action.value = "insertRow";
			else
				theForm.action.value = "updateField";
								
			response= submitForm();
			processResponse(response);
		}	
			
		//Fired when a row is deleted
		// Maybe its just me but rowIndex seems to return the new row that has taken the place of the deleted row. I wonder if there is a beforeRemoval event ??
		function dataGridDeleting(rowIndex)
		{
			theForm.recId.value = recIdToDelete;
			theForm.action.value="deleteRow";
			isGridInserting = false;
			response = submitForm();
			processResponse(response);
		}	
	
		// Fired when a row is intially created  just keeping track of the editor state. I am sure the grid must have this in it somewhere
		function dataGridInserting(rowIndex)
		{
			isGridInserting = true;
		}
		// Fired when the editing of the cell is cancelled just keeping track of the editor state. I am sure the grid must have this in it somewhere
		function dataGridCancelCellEdit(rowIndex)
		{
			isGridInserting = false;
		}
	
		// Hook up to some events of the grid. seems I should be using observers but cannot find any concrete examples so this seems to work !!	
		function init() 
		{
			theGrid = dijit.byId("DataGrid");
			theForm = dojo.byId("updateFieldForm");
			dojo.connect(theGrid,'onApplyCellEdit',dataGridApplyCellEdit);
			dojo.connect(theGrid.model,'insertion',dataGridInserting);
			dojo.connect(theGrid.model,'removal',dataGridDeleting);
			dojo.connect(theGrid,'onCancelEdit',dataGridCancelCellEdit);
		}
	
		dojo.addOnLoad(init);
	




	
		
		
		
		
		
	


	

New Item   Delete Selected Item       Set Filter

One silly question...

Hi again!!

I'm trying to write a store class (derivative of the QueryReadStore) to work with CakePHP, witch uses non-standard URLs. The question is that my new store can retrieve data from server, but I'm not sure what it should do with them. Explanation and example: it asks for the first page (let's say 5 records) and works ok. Now it asks for the second page (records 6 to 10, both included). Now I see that the records of the first page are replaced with this new ones in the store. Is this what it should do? If yes, I guess that the model should keep all the records so it's not necessary get them from the server again. I'm a little confused with all these things (all new for me...)

Thanks in advance!!

Not a silly question

My observations are at least with the code I presented above. that the store, at least on the first pass reads ahead so when the page starts up I see

GET http://192.168.0.3/dojogridresp.php?start=0&count=20&sort=&filter=
GET http://192.168.0.3/dojogridresp.php?start=20&count=20&sort=&filter=

Seems to go ahead and pull two lots of data. That might be an issue with my code not had time to figure it out.

The rest of it seems to be as you say. As you page through your grid you will see GET requests for pages that have not yet been pulled from the server. Going straight back to the top will not result in a reload of the data.

If you note however in my code above for instance in newGridData.js when you issue a this.clearData() then you have removed all the data from the store so it has to go back and get it all again which seems to make sense.

Hope that helps

(Partially) Solved.

Hi!

Finally I've been able to get it working. It was my fault: I wasn't setting the doClientPaging parameter to false. Now I understand (I think ;-P). If you set the rowsPerPage parameter to, let's say, 3, you'll see that it does more requests so it gets more or less the same number of rows, witch varies automatically depending on the size of the grid.

Now the new problem is to sort. There's something wrong with the colIndex variable in the sort method in the xyz.data.ServerGridData class. Any ideas?

This (the grid and the whole Dojo thing) is definitively great!! I love it!!

Sorting using your example of 01/04/2008 12:09

clicking on a col header of the data grid will result in the colIndex arg having the value of the index (starting from 1) so if you have the following cols

firstname = 1
lastname = 2

If you click on the same header again it will reverse the order so you will get for descending
firstname = -1
lastname = -2

It's upto your webserver to return the data in the order you want it displayed. The sort does not actually happen within the grid itself.

Look in firebug you should see something like

GET http://192.168.0.3/dojogridresp.php?start=20&count=20&sort=2&filter=
Take a look at my example dojogrid.php at the function processList() specifically at $sort arg
Hope that helps

The sort method was wrong.

Hi rhonda!

Now I understand how the whole thing works, and it's wonderful (I love this job!). Anyway there's something something more that's wrong in the xyz.data.ServerGridData class wrote by scrollpane: the sort method. This is how he/she wrote it:

sort: function()
        {
            this.clearData();
            this.sortColumn = colIndex;
            this.requestRows(); 
        },

but this doesn't work (at least for me). Here's my proposal:

sort: function(inColIndex)
        {
            this.clearData();
            this.sortColumn = inColIndex;
            this.requestRows(); 
        },

This way it works fine (at least for me).

Thanks a lot for you precious help!!

Opps forgot to mention that

Glad to be able to help. Yes, I should have mentioned that the argument colIndex was missing from that example. Glad to see you figured it out

Grid doesn't know how to look ahead - problem with rowsPerPage

I dug into this as the behavior seemed slightly strange - there's no functionality for loading rows ahead (even though maybe there should?). It turned out that the grid (VirtualGrid) has it's own "rowsPerPage" variable independent from the model, and the default value for that is 25. When the model and the grid have have different values for "rowsPerPage", it leads to described behavior.

A simple solution is to add rowsPerPage attribute for grid declaration:

<div id="grid" jsId="grid" dojoType="dojox.Grid" elasticView="2" rowsPerPage="15" model="model" structure="structure"></div>

Thanks again Maine

I noticed the number 25 in the DOM while debugging a seperate issue and found it a little weird. As always THANKS AGAIN for a quick and clear answer. That puts that quirk to rest ! Read ahead might be a good thing if you had a generic grid where you wanted it to figure out the optimum fetch strategy, even then I wonder if that might not end up being a complex and somewhat hit or miss algorithm to implement.

Ideally you would be looking at the totalRows the grid might have to deliver and the average transit time across the network for each page before you could determine such ? You might need the user to be paging through the data a few times before you could derive a semi accurate sample...

I am interested in the idea of a noCache type of scenario ( I am sure I read it somewhere on this site but did not keep track of it) where by the datastore only holds whats in view or have a setting to purge the datastore after it reaches a certain size. Or even purge the x amount of oldest reads from the datastore..... The rational behind that is I am guessing that the benefit of the queryReadStore eventually is outweighed by its memory consumption on the client. What would happen if a user paged through a grid with 50,000 rows!! It might be ok on the average desktop but on a mobile device with limited resources would that not cause some real issues with the queryReadStore holding all of the data within the client (browser)?

Food for thought or maybe just fodder :-)

Are you sure the data is being cached?

It's been a month now, but I remember digging in with firebug to see just what exactly the store was keeping in it, and if I recall correctly, with each call it makes to the server it wipes the current dataset out and starts over. So I think noCache is the DEFAULT behavior and if you wanted it to cache you'd have to overwrite code in QueryReadStore.

However, I could be wrong, I've slept since. A quick scan of QueryReadStore.js shows that in the fetch function there doesn't appear to be a clearing of the current store, and _fetchHandler is certainly doing a .push call for adding new items into the store.

I'll be debugging some other things in the next few days, I'll take a look at this as well as I agree with you about your concerns. I have a 40K entry set that could get ugly if the store is retaining it all.

Lance Spellman

Having trouble with programmatic approach, declaritive works

I have been working on subclassing QueryReadStore to use a JsonService for data retrieval. I have a declarative example that works, but I have not been able to get the programmatic version working (no matter how many times I read this thread :).

I can see the JsonService being called for both approaches but the Programmatic version does not update the Grid after the first fetch.

Any help is appreciated:

Here is my subclass of the QueryReadStore, JSONRPCQueryReadStore:

dojo.provide("emory.data.JSONRPCQueryReadStore_04");
dojo.require("dojox.data.QueryReadStore");
dojo.require("dojo.rpc.JsonService")
dojo.declare("emory.data.JSONRPCQueryReadStore", dojox.data.QueryReadStore, {
  
    /*
    // Extends dojox.data.QueryReadStore to use dojo.rpc.JsonService
    // for retrieving Items.
    // The result of the Json Service call is expected to be
    // an array of items. 
    */
    constructor: function(/* Object */ params){
    	console.log("constructor");
    	this.service = new dojo.rpc.JsonService("/CourierApp/RPCAdapter/jsonrpc/EventLookup");
	},
	
	
	/*
    // overrides superclass method and calls getJSONService 
    // to call the JsonService and add Callbacks for populating Items
    */ 
    _fetchItems: function(request, fetchHandler, errorHandler){
		var serverQuery = typeof request["serverQuery"]=="undefined" ? request.query : request.serverQuery;
		//Need to add start and count
		if(!this.doClientPaging){
			serverQuery = serverQuery||{};
			serverQuery.start = request.start?request.start:0;
			// Count might not be sent if not given.
			if (request.count) {
				serverQuery.count = request.count;
			}
		}
		// Compare the last query and the current query by simply json-encoding them,
		// so we dont have to do any deep object compare ... is there some dojo.areObjectsEqual()???
		if(this.doClientPaging && this._lastServerQuery!==null &&
			dojo.toJson(serverQuery)==dojo.toJson(this._lastServerQuery)
			){
			fetchHandler(this._items, request);
		}else{
		
		    /*
		    // call getJSONService(request) to invoke JsonService
		    */
		    var serviceHandler = this.getJSONService(request)

            /*
            // Add Callbacks to JsonService
            // Expect service result to contain array of items.
            */		
			serviceHandler.addCallback(dojo.hitch(this, function(result){
			
				console.log("serviceHandler.callback, logging result");
				console.log(result);
			    
				this._items = [];

				// Store a ref to "this" in each item, so we can simply check if an item
				// really origins form here (idea is from ItemFileReadStore, I just don't know
				// how efficient the real storage use, garbage collection effort, etc. is).
				for(var i in result){
					console.log(result[i]);
					this._items.push({i:result[i], r:this});
				}
				
				var identifier = result.identifier;
				
				this._itemsByIdentity = {};
				if(identifier){
				    console.log("Identifier found");
					this._identifier = identifier;
					for(i = 0; i < this._items.length; ++i){
						var item = this._items[i].i;
						var identity = item[identifier];
						if(!this._itemsByIdentity[identity]){
							this._itemsByIdentity[identity] = item;
						}else{
							throw new Error("dojo.data.QueryReadStore:  The json data as specified by: [" + this._url + "] is malformed.  Items within the list have identifier: [" + identifier + "].  Value collided: [" + identity + "]");
						}
					}
				}else{
				    console.log("No identifier, using number");
					this._identifier = Number;
					for(i = 0; i < this._items.length; ++i){
						this._items[i].n = i;
					}
				}
				
				// TODO actually we should do the same as dojo.data.ItemFileReadStore._getItemsFromLoadedData() to sanitize
				// (does it really sanititze them) and store the data optimal. should we? for security reasons???
				fetchHandler(this._items, request);
			}));
			serviceHandler.addErrback(function(error){
				errorHandler(error, request);
			});
			
			// Generate the hash using the time in milliseconds and a randon number.
			// Since Math.randon() returns something like: 0.23453463, we just remove the "0."
			// probably just for esthetic reasons :-).
			this.lastRequestHash = new Date().getTime()+"-"+String(Math.random()).substring(2);
			this._lastServerQuery = serverQuery;
		}
	},
    
    /*
    // Provided for subclasses to override
    */
    getJSONService: function(request){
    	console.log("in getJSONService inside JSONRPCQueryReadStore, logging request");
		return this.service.lookupEvents('PRD','ADAPTER','MIL.ADT.OUT.ADAPTER');
    }
    
});

Here is my declarative approach (which works):



        Test dojox.Grid Basic
        
        
		        @import "dojox/grid/_grid/tundraGrid.css";
		        @import "dijit/themes/tundra/tundra.css";
		        @import "dojo/resources/dojo.css"
                body {
                        font-size: 0.9em;
                        font-family: Geneva, Arial, Helvetica, sans-serif;
                }
                .heading {
                        font-weight: bold;
                        padding-bottom: 0.25em;
                }
                
                #grid, #grid2 {
						width: 65em;
						height: 25em;
						padding: 1px;
				}
                
        
        
        
        



Query Read Store Paging Grid
// creates serverQuery-parameter var row = inRowIndex || 0; var params = { start: row, count: inCount || this.rowsPerPage, serverQuery: dojo.mixin( { start: row, count: inCount || this.rowsPerPage, sort:(this.sortColumn || '') }, this.query ), query: this.query, // onBegin: dojo.hitch(this, "beginReturn"), onComplete: dojo.hitch(this, "processRows") } this.store.fetch(params); // should return total count (fetch from server), not "rowsPerPage" return 5000; // clears old data to force loading of new, then requests new rows this.clearData(); this.sortColumn = colIndex; this.requestRows(); // edited not to reset the store this.data = []; this.allChange(); // always true return true;
var grid = dijit.byId('grid'); // Debug the Event console.debug(event); // Get the Row console.debug("getting rowIndex(" + event.rowIndex + ")"); thisRow = myModel.getRow(event.rowIndex); console.debug(thisRow); // Get the EventId from the Row Object //eventId = thisRow.eventId; //console.debug('eventId: ' + eventId);

Here is my programatic approach:



        Test dojox.Grid Basic
        
        
		        @import "dojox/grid/_grid/tundraGrid.css";
		        @import "dijit/themes/tundra/tundra.css";
		        @import "dojo/resources/dojo.css"
                body {
                        font-size: 0.9em;
                        font-family: Geneva, Arial, Helvetica, sans-serif;
                }
                .heading {
                        font-weight: bold;
                        padding-bottom: 0.25em;
                }
                
                #grid, #grid2 {
						width: 65em;
						height: 25em;
						padding: 1px;
				}
                
        
        
        
        



Query Read Store Paging Grid
// creates serverQuery-parameter var row = inRowIndex || 0; var params = { start: row, count: inCount || this.rowsPerPage, serverQuery: dojo.mixin( { start: row, count: inCount || this.rowsPerPage, sort:(this.sortColumn || '') }, this.query ), query: this.query, // onBegin: dojo.hitch(this, "beginReturn"), onComplete: dojo.hitch(this, "processRows") } this.store.fetch(params); // should return total count (fetch from server), not "rowsPerPage" return 5000; // clears old data to force loading of new, then requests new rows this.clearData(); this.sortColumn = colIndex; this.requestRows(); // edited not to reset the store this.data = []; this.allChange(); // always true return true;
var grid = dijit.byId('grid'); // Debug the Event console.debug(event); // Get the Row console.debug("getting rowIndex(" + event.rowIndex + ")"); thisRow = myModel.getRow(event.rowIndex); console.debug(thisRow); // Get the EventId from the Row Object //eventId = thisRow.eventId; //console.debug('eventId: ' + eventId);
The programmatic approach does not update the grid after the first fetch but the service is being called when the scroll bar is activated. Any help is greatly appreciated. Thanks, Tom

Grid paging bug in Dojo 1.0.2

There's maybe nothing wrong in your code. There's a bug in Dojo 1.0.2 release that breaks grid paging. We discussed it a while ago at Problem with grid and QueryReadStore, and now it's fixed in the SVN head that you can get from Trac or as a nightly build.

Same Behavior with nightly build

Thanks for the quick reply!

I updated all (dijit, dojo, dojox, util) to the nightly build (01/21/2008) and problem still exists.

I'm new to this, but I can't see any reason why the programmatic approach is failing to update the grid.

Any help is appreciated.
Thanks,
Tom

doClientPaging should be boolean

Alright, another attempt: Firstly, you're passing the constructor a string value "false" for doClientPaging - this evaluates to true. It should be boolean, i.e. without quotes. Parser performs a "lightweight type conversion", so it sets the value type correctly to boolean. Plus, you seem to be overriding the QueryReadStore constructor with a one that doesn't do anything with the arguments/params. Is this intended?

Passing Boolean solved issue

yeah, i'm the nube. Thanks for seeing that.

Passing doClientPaging correctly as a boolean solved the issue. I appreciate the help.

I'm planning to use params.url to set the JsonService for populating the store.

The goal of JSONRPCQueryStore is to extend QueryStore to use a JsonService for data retrieval.
Approach:
1. Provide constructor to set the JsonService from params.url

constructor: function(/* Object */ params){
    	service = new dojo.rpc.JsonService(params.url);
    },

2. Provide extension point for instances to define the service call. I'm not sure about the best way to do this yet. I'm working on an example that creates a new instance of JSONRPCQueryStore and overrides the invokeJSONService method.

// Instances will provide implementation (perhaps like this)
invokeJSONService: function(request){
   return this.service.lookupEvents(request.start,request.count);
}

3. Override QueryStore _fetchItems to call extension point for data retrieval.

_fetchItems: 
...
if(this.doClientPaging && this._lastServerQuery!==null &&
   dojo.toJson(serverQuery)==dojo.toJson(this._lastServerQuery)
   ){
        fetchHandler(this._items, request);
    }else{
	// call JSONService
	var serviceHandler = this.invokeJSONService(request)

        // Add Callbacks to JsonService
	serviceHandler.addCallback(dojo.hitch(this, function(result){
...

I would appreciate any feedback that you may have on this approach.

Thanks again for the help.
<nube>Tom</nube>

Superclass constructors, plus the role of the store layer

Glad that I could help. Tom, you know, you were right about the constructor - superclass constructors are always called automatically, and always before the subclass constructor.

Someone raised the topic of a custom store a while ago on the forums. I pointed out that:

You might want to consider doing it without a store. This level of abstraction is not necessary for the grid - the model alone can serve the grid. So, unless you're planning to use the same data to populate a tree or a combobox, you should probably pick dojox.grid.data.Table and build your solution on top of that one.

I didn't quite get that what are the advantages of dojo.rpc.JsonService... Would you explain? Maybe we could continue this discussion on the grid forum.

Forum Topic Created: QueryReadStore using JsonService

I created the Forum topic to continue our discussion: http://www.dojotoolkit.org/forum/dojox-dojox/dojox-grid-support/queryrea...

I would like to understand more about using a dojox.grid.data.Table. Please have a look at the post and let me know your thoughts.

Thanks again,
Tom

dojo grid server side paging not working in 1.1b2

I have this version of code working in 1.0, but after I switch to 1.1b2, the grid just fetch the first batch (20 rows) after sorting. Anybody has this server-side paging working in 1.1? Any help would be greatly appreciated. Thanks.

constructModel : function() {
        this.model = new dojox.grid.data.DojoData(null, this.store,{rowsPerPage: 20, 
                         query:{ type: VisualizationPresentation.AnalysisWindow,
  			 features: this.features.toString() },
            requestRows:function(inRowIndex, inCount){
		// creates serverQuery-parameter
		var row  = inRowIndex || 0;
		var params = {
		    start: row,
		    count: inCount || this.rowsPerPage,
		    serverQuery: dojo.mixin(
		        { start: row,
			    count: inCount || this.rowsPerPage,
			    sort:(this.sortColumn || '')
			},
		        this.query
	            ),
	            query: this.query,
	            // onBegin: dojo.hitch(this, "beginReturn"),
	           onComplete: dojo.hitch(this, "processRows")
	        };
	        this.store.fetch(params);
	    },
			
	    sort:function(colIndex){
		// clears old data to force loading of new, then requests new rows
		this.clearData();
		if (colIndex > 0) {
		    this.sortColumn = this.fields.values[colIndex - 1].name;
		} else {
		    this.sortColumn = "-" + this.fields.values[-colIndex - 1].name;			        	
		}
		this.requestRows();
	    },
			
	    setData:function(inData){
		// edited not to reset the store
		this.data = [];
		this.allChange();
	    },
			
	    canSort:function(colIndex){
	     	return true;
	    },
			
	    refreshAll: function() {
		this.clearData();
		this.requestRows();
	    },
	});
		
	this.model.getRowCount = dojo.hitch(this, this.dataset.getRowCount);
    }

QueryReadStore Grid demo illustrates changes

There have been some changes from 1.0.x to 1.1, and sorting and filtering are now easier to do. See QueryReadStore Grid demo source - it uses a simpler custom DojoDataSortable-class that extends standard DojoData. See also QueryReadStore breaking Grid scrolling for discussion.

I think there are still some issues with paging and sorting

Hi, I think server side paging is getting better and better, but I still had to do some (at least for me) questionable steps to make paging working after a sort as I described it in this forum post:

http://www.dojotoolkit.org/forum/dojox-dojox/dojox-grid-support/queryrea...

Of course it is possible that I am missing something.

Thanks for your quick

Thanks for your quick response, look like we don't have a formal fix for it in 1.1, except the passthrough patch?