PDA

View Full Version : how to make a grid read from my object?



bmf1972
6 Apr 2008, 9:37 AM
Hi all,

Suppose that you have an instance of:



function SearchResults(/*Array*/ searchResults) {
this.getResultsCount = /*Number*/ function() {
return searchResults.length;
}
this.getResult = /*Result*/ function(/*Number*/ index) {
return searchResults[index];
}
}

function Result(/*Object*/ rawResult) {
this.getTitle = function() { return rawResult.title; }
this.getSummary = function() { return rawResult.summary; }
...
}
and you want to bind it to an Ext.grid.GridPanel.

One way is to construct an Ext.data.Store and populate it from the given SearchResults instance. But this seems wrong...

In Swing, one can implement his own TableModel, make it contain the SearchResults instance and then override the accessors to fit the SearchResults interface. I am searching for something similar in ExtJS, starred at the Ext.data.Store class & Co. for a few hours and I still cannot see the right way to do it... :(

Please help,
Adrian.

6 Apr 2008, 11:43 AM
where is your grid code? Have you tried yourGrid.store.loadData(yourObj)?

bmf1972
7 Apr 2008, 12:18 AM
Hi jgarcia!

Here is a GridPanel instance which I believe it works:


var searchData = [["t1","s1"],["t2","s2"],["t3","s3"]];

var SearchResultsGridPanel = new Ext.grid.GridPanel({
store: new Ext.data.SimpleStore({
fields: [
{name: 'title'},
{name: 'summary'},
],
data: searchData
}),
columns: [
{id:'title', header: "Title", dataIndex: 'title'},
{header: "Summary", dataIndex: 'summary'}
],
title: "Search Results",
sm: new Ext.grid.RowSelectionModel({singleSelect:true})
});

I could write a conversion function to create the searchData array from a SearchResults instance. However, this conversion requires duplication of data which is already in memory. Hence I am looking for a performance effective approach.

I still do not understand what the Store is doing with the data. It seems that the data is duplicated into some other object structure inside the Store... If so, then adding another conversion makes it even slower. After reading few dozens of post about Stores, Readers & Co., it seems to me that a better approach could be to provide a custom Reader, say SearchResultsReader, this way only the conversion required by the Store will be done.

Any insights? Advices?
Adrian.

bmf1972
7 Apr 2008, 8:20 AM
Here is a modified array-grid.js that works:



/*
* Ext JS Library 2.0.2
* Copyright(c) 2006-2008, Ext JS, LLC.
* [email protected]
*
* http://extjs.com/license
*/

Ext.onReady(function(){

Ext.state.Manager.setProvider(new Ext.state.CookieProvider());

function CompanyResults() {
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']
];

this.getCount = function() {
return myData.length;
}

this.getCompanyResult = function(companyIndex) {
return new CompanyResult( myData[companyIndex] );
}

this.getRawData = function() {
return myData;
}
}

function CompanyResult(rawData) {
this.getName = function() {
return rawData[0];
}

this.getPrice = function() {
return rawData[1];
}

this.getChange = function() {
return rawData[2];
}

this.getPctChange = function() {
return rawData[3];
}

this.getLastChange = function() {
return rawData[4];
}
}

function CompanyResultsReader() {
function RecordsObject() {
this.success = true;
this.records = [];
this.totalRecords = 0;
}

var CompanyResultRecord =
Ext.data.Record.create([
{name: "company"},
{name: "price"},
{name: "change"},
{name: "pctChange"},
{name: "lastChange", type: 'date', dateFormat: 'n/j h:ia'}
]);

Ext.data.DataReader.call(this, null, CompanyResultRecord);

this.read = function(companyResults) {
// no preprocessing required here
return this.readRecords(companyResults);
}

this.readRecords = function(companyResults) {
var companyIndex, lastCompanyIndex, recordsObject;

lastCompanyIndex = companyResults.getCount();

recordsObject = new RecordsObject();

for(companyIndex = 0; companyIndex < lastCompanyIndex; companyIndex++) {
var companyResult = companyResults.getCompanyResult(companyIndex);
var rawData = {
company: companyResult.getName(),
price: companyResult.getPrice(),
change: companyResult.getChange(),
pctChange: companyResult.getPctChange(),
lastChange: companyResult.getLastChange()
};

recordsObject.records.push(new CompanyResultRecord(rawData));
}

recordsObject.totalRecords = lastCompanyIndex;

return recordsObject;
}
}

// 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;
}

// create the data store
var simpleStore = new Ext.data.SimpleStore({
fields: [
{name: 'company'},
{name: 'price', type: 'float'},
{name: 'change', type: 'float'},
{name: 'pctChange', type: 'float'},
{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
]
});
simpleStore.loadData(new CompanyResults().getRawData());

var store = new Ext.data.Store({
reader: new CompanyResultsReader()
});
store.loadData(new CompanyResults());

// create the Grid
var grid = new Ext.grid.GridPanel({
//store: simpleStore,
store: store,
columns: [
{id:'company',header: "Company", width: 160, sortable: true, dataIndex: 'company'},
{header: "Price", width: 75, sortable: true, renderer: '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'}
],
stripeRows: true,
autoExpandColumn: 'company',
height:350,
width:600,
title:'Array Grid'
});

grid.render('grid-example');

grid.getSelectionModel().selectFirstRow();
});
One issue though... The dates are not formatted as in the original array-grid.js, and their column cannot be sorted -- any idea why?

Adrian.

7 Apr 2008, 12:31 PM
they all say 9/1 00:00

now i know what you mean by 'obj'. it seems like an awful amount of work for that functionality. The data store gives you all of these functions, albeit not the exact same method labels.

bmf1972
7 Apr 2008, 1:09 PM
they all say 9/1 00:00In my browsers (FF2, IE7, Opera9, Safari3) they all say NaN/NaN/NaN ...

NOTE: the grid seems broken on Opera9.


now i know what you mean by 'obj'. it seems like an awful amount of work for that functionality. The data store gives you all of these functions, albeit not the exact same method labels.What if you get such objects from an external library? You will somehow need to adapt them to Ext.data.Store. Do you see a better way to do this?

BTW, I wander what happens if two grids are bound to the same store and the user sorts some column in one of the grids...

7 Apr 2008, 1:31 PM
if the store is bound to two grids, both grids would get the same sort data.

I see what you're saying about objects.

Why not just set the store data to yourObj.getData() yourObj.data :). are you just trying to make a uniform wrapper?

bmf1972
8 Apr 2008, 2:31 AM
if the store is bound to two grids, both grids would get the same sort data.

I see what you're saying about objects.

Why not just set the store data to yourObj.getData() yourObj.data :). are you just trying to make a uniform wrapper?

I tried to bind the same store to two grids. Sorting one grid makes the second one sort too. Sorting is an presentation activity, like applying a different style, or reordering the columns, and as such it must not alter the data model. Hence, the store object is a part of the presentation, not of the data model (despite its "class" name).
...and there is no infrastructure for data models in ExtJS, therefore application designers must build it by hand from zero.

The CompanyResults and CompanyResult "classes" in my toy are data model entities. (The CompanyResults#getRawData() method was added just to enable the construction of the Ext.data.SimpleStore -- a real data model does not provide direct access to its internal data structures)

8 Apr 2008, 4:01 AM
sorting is a presentation activity only when local.

when we talk about stores, i always think of remote stores. If you tie two grids to one remote store and attempt a sort operation on the grid, if the sort is remote, then both grids will get the same sorted data when it's reloaded.

bmf1972
8 Apr 2008, 5:40 AM
sorting is a presentation activity only when local.

when we talk about stores, i always think of remote stores. If you tie two grids to one remote store and attempt a sort operation on the grid, if the sort is remote, then both grids will get the same sorted data when it's reloaded.


Hmmm... I believe that your "sorting"-s are not persistent... That is, if Joe logs-in to his StockTracer server and sorts the CompanyResultsGrid, then Mia, who is also logged-in and starring at the CompanyResultsGrid, will not get the her view sorted (although it could be funny :-).

The point of separating the data model from view model is to enable having different views on the same data at the same time. For example, having a grid and a list bound to the same data. However, the Ext.data.Store mechanism was not designed to support this.

I still do not understand why the dates are not recognized by the formatter when the formatting is applied by the Record constructor :(