PDA

View Full Version : Dynamic grid columns?



SteveEisner
23 Mar 2007, 7:23 AM
Hi, two quick questions:

1) Is it possible to set up a grid so that its columns are based on the contents of the records you pass it? In other words, if the records are {X:1,Y:2} the columns will be X and Y.

2) Is it possible to configure the "start" and "limit" parameters to change them to "startIndex" and "endIndex" (or does limit = "length"?)

mike1993
23 Mar 2007, 8:04 AM
I started using yui-ext recently and I am facing the very same problems.
dynamic grid and paging params names (they are clashing w/ current code I have in place).

and one more question:
How can I force "auto-size" for cell content w/o specifying its 'width'?
" 123 " vs "123"

Thanks!

dfenwick
23 Mar 2007, 8:48 AM
Hi, two quick questions:

1) Is it possible to set up a grid so that its columns are based on the contents of the records you pass it? In other words, if the records are {X:1,Y:2} the columns will be X and Y.

2) Is it possible to configure the "start" and "limit" parameters to change them to "startIndex" and "endIndex" (or does limit = "length"?)

I don't think so, and for many reasons. One reason is that the data model provides a set of data that you can then provide a view of. The column model is that view. The column model says "With these 12 data elements returned per row, which ones do I want to display in my viewport and which ones are there for support purposes?"

There's nothing preventing you from flattening the view to match the data source though. You could just write a mini adapter that goes out and discovers your column model and dynamically creates it for your grid.

Animal
23 Mar 2007, 8:52 AM
Well, if you fist find out what the records are going to contain, you can create an appropriate ColumnModel programatically by simply assembling a column config Array.

My wrapper of the Grid sends an encoded query to the server, receives back info about the columns, builds the ColumnModel and Record definition on the fly from this recieved info, creates the Grid and loads it.

I only have one Grid class which will handle all my possible queries.

genius551v
23 Mar 2007, 10:13 AM
esta grilla se construye onfly vacia y se re-construyen tambien onfly con datos del # de columnas despues de una consulta del numero de elementos



grid_editor_modelo_medidas = function(){
var myData;
var dataModel;
var colModel;
var sm;
var grid;
var contenedor = 'editor-grid-modelo-medidas-confeccion';

var responseText;

return {
init : function(){
responseText = '';

myData = [];
dataModel = new YAHOO.ext.grid.DefaultDataModel(myData);
colModel = new YAHOO.ext.grid.DefaultColumnModel([
{header: "id_caracteristica", width: 30, hidden: true},
{header: "id_stcaracteristica", width: 30, hidden: true},
{header: "Subtipo Caracteristica", width: 200},
{header: "Tallas de la Familia", width: 464}
]);
//sm = new YAHOO.ext.grid.EditorAndSelectionModel();
grid = new YAHOO.ext.grid.Grid(contenedor,dataModel,colModel/*,sm*/);
grid.render();
},

reRenderGrid : function(id_fam_talla){
//tstart = new Date().getTime();
var cb = {success: function(o){
responseText = o.responseText;

var vec_tallas = responseText.split(',');

var myColumns = new Array();
myColumns[0] = new Objeto('id_caracteristica',30,true);
myColumns[1] = new Objeto('id_stcaracteristica',30,true);
myColumns[2] = new Objeto('Subtipo Caracteristica',200,false);
myColumns[3] = new Objeto('Margen (+/-)',70,false,'NumberEditor');

var c=4;
for(var i=0; i<vec_tallas.length; i++){
myColumns[c] = new bjeto(vec_tallas,30,false,'NumberEditor');
c++;
}
grid.destroy();

colModel = new YAHOO.ext.grid.DefaultColumnModel(myColumns);

dataModel = new YAHOO.ext.grid.DefaultDataModel(myData);

sm = new YAHOO.ext.grid.EditorAndSelectionModel();

grid_editor_modelo_medidas.reFullgrid(); //convierte algunos datos de la grilla a 0

grid = new YAHOO.ext.grid.Grid(contenedor,dataModel,colModel,sm);

grid.render();

//var tend = new Date().getTime();
//alert('Rendered in: ' + ((tend-tstart)/1000) + ' seconds');
},
failure: function(o){
alert('Fail...'+o.responseText);
}
};
YAHOO.util.Connect.asyncRequest('POST','AjaxGetColumns.php',cb,'id_familia_talla='+id_fam_talla);
},

addrow : function(valor2,valor3,valor4){
grid.stopEditing();

var row = new Array();

row[0] = valor2;
row[1] = valor4;
row[2] = valor3;

if(responseText!=''){
row[3] = '0';
var vec_col = responseText.split(',');
var c=4;
for(var i=0; i<vec_col.length; i++){
row[c] = '0';
c++;
}
}else{
row[3] = '[i]<span style="color:red;">- Debe escoger para crear las columnas y poder adicionar datos a la grilla -</span>';
}
dataModel.addRow(row);
},

addData : function(id_fam_talla,cadData){
//tstart = new Date().getTime();

grid.stopEditing();

myData = [];

if(cadData){
var vec_medidas_mod = cadData.split('#');

for(var i=0; i<vec_medidas_mod.length; i++){
var vec_data_row = vec_medidas_mod[i].split(',');

var row1 = new Array();

var c1=0;

for(var j=0; j<vec_data_row.length; j++){
row1[c1] = vec_data_row[j];
c1++;
}
myData[i] = row1;
}
}

var cb = {success: function(o){
responseText = o.responseText;

var vec_tallas = responseText.split(',');

var myColumns = new Array();
myColumns[0] = new Objeto('id_caracteristica',30,true);
myColumns[1] = new Objeto('id_stcaracteristica',30,true);
myColumns[2] = new Objeto('Subtipo Caracteristica',200,false);
myColumns[3] = new Objeto('Margen (+/-)',70,false,'NumberEditor');

var c=4;
for(var i=0; i<vec_tallas.length; i++){
myColumns[c] = new Objeto(vec_tallas[i],30,false,'NumberEditor');
c++;
}
grid.destroy(false,true);

colModel = new YAHOO.ext.grid.DefaultColumnModel(myColumns);

dataModel = new YAHOO.ext.grid.DefaultDataModel(myData);

sm = new YAHOO.ext.grid.EditorAndSelectionModel();

grid = new YAHOO.ext.grid.Grid(contenedor,dataModel,colModel,sm);

grid.render();

//var tend = new Date().getTime();
//alert('Rendered in: ' + ((tend-tstart)/1000) + ' seconds');
},
failure: function(o){
alert('Fail...'+o.responseText);
}
};
YAHOO.util.Connect.asyncRequest('POST','domain/AjaxGetColumns.php',cb,'id_familia_talla='+id_fam_talla);
},

reFullgrid : function(){
grid.stopEditing();

var rows = dataModel.getRowCount();

for(var i=0; i<rows; i++){
var row = dataModel.getRow(i);
row[3] = '0';
var vec_col = responseText.split(',');
var c=4;
for(var j=0; j<vec_col.length; j++){
row[c] = '0';
c++;
}
}
},

savedm : function(){
var rows = dataModel.getRowCount();
var col = colModel.getColumnCount();
var vec_tallas = responseText.split(',');
var medidas_modelo = '';

for(var i=0; i<rows; i++){
var row1 = dataModel.getRow(i);
var value = '';

value = row1[0]+','+row1[1]+','+row1[2]+','+row1[3]+',';

for(var j=4; j<col; j++){
value = value+row1[j];

if(j+1<col)
value=value+',';
}
if(i+1<rows)
value = value+'~';
medidas_modelo = medidas_modelo+value;
}
return medidas_modelo;
}
};
}();

function Objeto(header, width, hidden, editor)
{
this.header = header;
this.width = width;
this.hidden = hidden;
if(editor){
if(editor=='NumberEditor'){
this.editor = new YAHOO.ext.grid.NumberEditor({allowBlank: false, blankText: 'Este campo no puede ser vacío!', allowNegative: false, allowDecimals: true});
}
}
}
YAHOO.ext.EventManager.onDocumentReady(grid_editor_modelo_medidas.init, grid_editor_modelo_medidas, true);



con algunos cambios porque esta funcion yo la utilizo para crear 2 grillas iguales simultaneamente, espero t ayude en algo.

recomendaciones y sugerencias

ext 0.33

tnks

SteveEisner
23 Mar 2007, 10:46 AM
I was looking through the code and it seems like this might also be done generically by:
1) Creating a "DynamicColumnModel" that, after receiving a record, uses that record to respond to column queries [this would be hard to do for rich field types but could pretty easily build an all-string table]
2) Creating a "DynamicRecord" that, in combination with a Reader, is able to store all the columns of the returned JSON.
After that both the grid and the reader would have to be modified slightly to send the recordset to the columnmodel, but other than the difficult work of building those two classes, I think it wouldn't take much modification of the base code. I'll see if I can take this on.

Other than that, dfenwick/Animal I agree your method for loading is a good one as long as you can make a schema query (unfortunately, we don't have a way to do that, because it's calling methods that return arbitrary data)

SteveEisner
23 Mar 2007, 6:22 PM
Here's what I came up with:

Dynamic Json Reader: this will read whatever data comes back from the JSON call. It doesn't take a record definition because it builds it on the fly. Unfortunately it only has any record information *after* the JSON call has completed, so you can't simply pass it into a Grid, etc. because it won't be initialized at that time.


Ext.data.DynamicJsonReader = function(config){
Ext.data.DynamicJsonReader.superclass.constructor.call(this, config, []);
};
Ext.extend(Ext.data.DynamicJsonReader, Ext.data.JsonReader, {
getRecordType : function(data) {
var i = 0, arr = [];
for (var name in data[0]) { arr[i++] = name; } // is there a built-in to do this?

this.recordType = Ext.data.Record.create(arr);
return this.recordType;
},

readRecords : function(o){ // this is just the same as base class, with call to getRecordType injected
this.jsonData = o;
var s = this.meta;
var sid = s.id;

var totalRecords = 0;
if(s.totalProperty){
var v = parseInt(eval("o." + s.totalProperty), 10);
if(!isNaN(v)){
totalRecords = v;
}
}
var root = s.root ? eval("o." + s.root) : o;

var recordType = this.getRecordType(root);
var fields = recordType.prototype.fields;

var records = [];
for(var i = 0; i < root.length; i++){
var n = root[i];
var values = {};
var id = (n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
for(var j = 0, jlen = fields.length; j < jlen; j++){
var f = fields.items[j];
var map = f.mapping || f.name;
var v = n[map] !== undefined ? n[map] : f.defaultValue;
v = f.convert(v);
values[f.name] = v;
}
var record = new recordType(values, id);
record.json = n;
records[records.length] = record;
}
return {
records : records,
totalRecords : totalRecords || records.length
};
}
});

DynamicColumnModel: this takes a Store as a parameter, and from that it will build a default ColumnModel where all the fields are included and have width: 300.


Ext.grid.DynamicColumnModel = function(store){
var cols = [];
var recordType = store.recordType;
var fields = recordType.prototype.fields;

for (var i = 0; i < fields.keys.length; i++)
{
var fieldName = fields.keys[i];
var field = recordType.getField(fieldName);
cols[i] = {header: field.name, dataIndex: field.name, width:300};
}
Ext.grid.DynamicColumnModel.superclass.constructor.call(this, cols);
};
Ext.extend(Ext.grid.DynamicColumnModel, Ext.grid.ColumnModel, {});


Here's how I use it -


function showGrid(container, url)
{
// create the Data Store
var ds = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({url: url}),
reader: new Ext.data.DynamicJsonReader({root: '...my root, yours will be different...'}),
remoteSort: true
});
ds.on('load', function() {
// Reset the Store's recordType
ds.recordType = ds.reader.recordType;
ds.fields = ds.recordType.prototype.fields;

// Create the grid
var grid = new Ext.grid.Grid(container, {
ds: ds,
cm: new Ext.grid.DynamicColumnModel(ds),
selModel: new Ext.grid.RowSelectionModel({singleSelect:true}),
enableColLock:true
});

// render it
grid.render();
});
ds.load();
}

I haven't really put it through its paces yet but it seems to work for whatever JSON calls I try. If this were more generic I'd want to add some things like:
* Some way to provide column widths and types for known columns - merge with the unknowns. Maybe a callback function of some sort.
* A better way to declare this type of grid that has to wait until the data has been fetched before rendering.
* A DynamicXmlReader to match the JSON reader
* etc.

Note if Jack reads this: the DynamicJsonReader code could be even smaller if you adopt the change I made to the readRecords() method...

Steve

mike1993
28 Mar 2007, 12:02 PM
Thanks guys!

Mohammed
28 Mar 2007, 6:31 PM
plz... Steve,

if you can provide some sample or atleast js and html code...it will help a lot for new bees....dreaming for dynamic grid...

staticboy
15 Aug 2007, 9:05 AM
SteveEisner, I love you!

Well, maybe not love, but an extreme amount of respect and appreciation for your DynamicJsonReader extension. It's solved so many of my problems! I've been trying to find a way to get a handle on a number of dynamic paragraph elements that are returned as part of the response to retrieve a news article. Here's a snippet of the JSON:

JSON snippet



"tailParagraphs": {
"paragraph": [
"The company also hopes its [...] organizations.",
"Oracle is a proponent of [...] SaaS.",
"Phillips foresees gradations [...] and SaaS.",
"\"We have been doing [...] said Phillips.",
"Conventionally, SaaS [...] multi tenant."
]
},

My problem was that the number of elements in the paragraph array is variable (as you might expect for a news article) and I couldn't find a way to setup a Record to handle this (I tried stuff with mapping: and convert:), but your DynamicJsonReader solves this problem perfectly.

It even solved the other problem I had http://extjs.com/forum/showthread.php?p=54791#post54791. This concerns the problem the JsonReader has if the root isn't specified as an array when there is only one object, e.g., if there is only one news article. DynamicJsonReader handles this anomoly in the JSON I am having to work with.

So, thank you once again, SteveEisner for sharing your work. I only hope I'll be able to eventually give as much back to these forums one day.

temporary
12 Nov 2007, 4:53 PM
Has someone ported this to ext-2.0-rc1?

Bye, Stephan

ramonj
1 Feb 2008, 11:57 AM
You might want to check this sample http://www.miamicoder.com/post/2008/01/Defining-ExtJS-GridPanel-columns-at-run-time.aspx. It doesn't exactly solve your columns problem, but maybe it will give you some ideas on the approaches you can take.

hendra1
21 Feb 2008, 12:16 AM
Yeah i looking for a dynamic grid columns in ext 2.0
But i couldnt found it
Does anyone know where i can found tutorial or example code , dynamic grid in ext 2.0

Thanks in advance

extsv
21 Mar 2008, 2:36 AM
It would be very helpful if you could upload the example code with Ext 2.0. Could not find any other examples in the forum also. Please help in this regard.

Thanks in advance.

spextjs
24 Jun 2008, 9:49 AM
Hi,
I need to make a dynamic grid with varying number of columns(based on 1. how many reporting periods for an entity_id and 2.which tab is clicked). I used your code in the following manner. There are no errors in the console but the grid is not rendering. Can you tell me what I am doing wrong. When the debugger is on, I am able to see the data correctly populated in the dynamic cols.

This grid is being called from another panel. The creditStatsPanel and CreditStatsGrid are called from a jason page.

Here is my code for CreditStatsGrid :
Ext.ns('SPD');
SPD.CreditStatsGrid = Ext.extend(SPD.SPDGridPanel, {
initComponent: function() {
this.height = 500;
var ds = new Ext.data.Store({
url: '/spd/getCreditStats.do',
reader: new Ext.data.DynamicJsonReader({root: 'creditStats'}),
remoteSort: true
});
if(!this.colModel) {
this.colModel = new Ext.grid.ColumnModel([]);
}
ds.load();
ds.on('load', function(){
ds.recordType = ds.reader.recordType;
ds.fields = ds.recordType.prototype.fields;
this.colModel= new Ext.grid.DynamicColumnModel(ds);

});
this.store=ds;
this.on("render",this.renderComponent,this);
SPD.CreditStatsGrid.superclass.initComponent.call(this);
},
renderComponent: function(){
this.initComponent();
this.doLayout();
}
});
Ext.data.DynamicJsonReader = function(config){
Ext.data.DynamicJsonReader.superclass.constructor.call(this, config, []);
};
Ext.extend(Ext.data.DynamicJsonReader, Ext.data.JsonReader, {
getRecordType : function(data) {
var i = 0, arr = [];
//debugger;
for (var name in data[0]) { arr[i++] = name; } // is there a built-in to do this?
//alert(arr);
this.recordType = Ext.data.Record.create(arr);
return this.recordType;
},

readRecords : function(o){ // this is just the same as base class, with call to getRecordType injected
this.jsonData = o;
var s = this.meta;
var sid = s.id;
//debugger;
var totalRecords = 34; // need to make this dynamic get the total sort_order or total store.length
if(s.totalProperty){
var v = parseInt(eval("o." + s.totalProperty), 10);
if(!isNaN(v)){
totalRecords = v;
}
}
var root = s.root ? eval("o." + s.root) : o;
var recordType = this.getRecordType(root);
var fields = recordType.prototype.fields;
var records = [];
for(var i = 0; i < root.length; i++){
var n = root[i];
var values = {};
var id = (n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
for(var j = 0, jlen = fields.length; j < jlen; j++){
var f = fields.items[j];
var map = f.mapping || f.name;
var v = n[map] !== undefined ? n[map] : f.defaultValue;
v = f.convert(v);
values[f.name] = v;
}
var record = new recordType(values, id);
record.json = n;
records[records.length] = record;
}
return {
records : records,
totalRecords : totalRecords || records.length
};
}
});

Ext.grid.DynamicColumnModel = function(store){
var cols = [];
var recordType = store.recordType;
var fields = recordType.prototype.fields;
//debugger;
for (var i = 0; i < fields.keys.length; i++)
{
var fieldName = fields.keys[i];
var field = recordType.getField(fieldName);
cols[i] = {header: field.name, dataIndex: field.name, width:300};

}
this.colModel= Ext.grid.DynamicColumnModel.superclass.constructor.call(this, cols);
};
Ext.extend(Ext.grid.DynamicColumnModel, Ext.grid.ColumnModel, {});
Ext.reg('creditstatsgrid', SPD.CreditStatsGrid);


----------------------------------------------------------------
Ext.ns('SPD');

SPD.creditStatsPanel = Ext.extend(SPD.SPDPanel, {

tplMarkup: [
'<tpl for=".">',
'<table width="663" cellspacing="0" cellpadding="0" border="0">',
'<tbody><tr>',
'<td width="225" valign="middle" height="25" align="left">',
'<div class="dirgrey">',
'Select Source',
'</div>',
'</td>',
'<td width="187" valign="middle" height="25" align="left">',
'<div class="dirgrey">',
'Select Period',
'</div>',
'</td>',
'<td width="180" valign="middle" height="25" align="left">',
'<div class="dirgrey">',
'Select Currency ',
'</div>',
'</td>',
'<td width="180" valign="middle" height="25" align="left">',
'<div class="dirgrey">',
'Select Currency Conversion ',
'</div>',
'</td>',
'<td width="140" valign="middle" height="25" align="left">',
'<div class="dirgrey">',
'Select Column Order ',
'</div>',
'</td>',
'</tr>',
'</tbody></table>',
'</tpl>'
],


initComponent: function() {

/*this.store1 = new SPD.SPDJsonStore({
fields: ['YEAR','PERIOD_DESCR','DATADATE'],
url: '/spd/getCreditStats.do',
root: 'columnHeader',
totalProperty: 'total'
});*/

this.tools = [{
id: 'toggle',
handler: this.toggleCollapse,
scope: this
}];

var tpl = new Ext.XTemplate(this.tplMarkup);
//this.entityId = (entityId != null)? entityId.dom.innerHTML : null;
//debugger;
SPD.colOrder = [
['Latest on Left','L'],
['Latest on Right','R']
];
var colOrdds = new Ext.data.SimpleStore({
fields: ['DD_OPTION','DD_VALUE'],
data : SPD.colOrder
});
var sourceStore = new Ext.data.JsonStore({
url: SPD.ServerUrls.loadSourceList,
root: 'sourceList',
fields: ['DD_OPTION','DD_VALUE']

});

var periodStore = new Ext.data.JsonStore({
url: SPD.ServerUrls.loadPeriodList,
root: 'periodList',
fields: ['DD_OPTION','DD_VALUE'],

});
var currencyStore = new Ext.data.JsonStore({
url: SPD.ServerUrls.loadCurrencyList,
root: 'currencyList',
fields: ['DD_OPTION','DD_VALUE']

});
var currConvStore = new Ext.data.JsonStore({
url: SPD.ServerUrls.loadCurrConvList,
root: 'curConvList',
fields: ['DD_OPTION','DD_VALUE']
});
if(null != Ext.get("fs_tab")){
this.fs_tab = Ext.get("fs_tab").dom.innerHTML;
}

if(null != Ext.get("entityId")){
this.entityId = Ext.get("entityId").getValue();
}
var headerItems = [{
xtype: 'spd-panel',
itemId:'crestHeaderPanel',
layout: 'table',
layoutConfig: {
columns: 3
},

items:[{
xtype: 'combo',
itemId: 'sourceList',
store: sourceStore,
displayField:'DD_OPTION',
value:'Adjusted',
valueField:'DD_VALUE',
typeAhead: true,
mode: 'local',
triggerAction: 'all',
selectOnFocus:true,
cls:'searchdrop3',
name:'source'

},{
xtype: 'combo',
itemId: 'periodList',
store: periodStore,
displayField:'DD_OPTION',
value:'Annual',
valueField:'DD_VALUE',
typeAhead: true,
mode: 'local',
triggerAction: 'all',
selectOnFocus:true,
name:'period'

},{
xtype: 'combo',
itemId: 'currencyList',
store: currencyStore,
displayField:'DD_OPTION',
value:'Reported Currency',
valueField:'DD_VALUE',
typeAhead: true,
mode: 'local',
triggerAction: 'all',
selectOnFocus:true,
name:'currency'

},{
xtype: 'combo',
itemId: 'currConvList',
store: currConvStore,
displayField:'DD_OPTION',
value:'Historical',
valueField:'DD_VALUE',
typeAhead: true,
mode: 'local',
triggerAction: 'all',
selectOnFocus:true,
name:'curconvCmb',
colspan:1

},{
xtype: 'combo',
itemId: 'columnOrderList',
store: colOrdds,
displayField:'DD_OPTION',
value:'Latest on Left',
valueField:'DD_VALUE',
typeAhead: true,
mode: 'local',
triggerAction: 'all',
selectOnFocus:true,
name:'colOrdCmb'

},{
xtype: 'button',
text: 'Go',
itemId: 'crestHeaderButton'
}]
}];

this.items = headerItems.concat(this.items);
sourceStore.load({
params: {
start: 0,
limit: 15
}
});
currencyStore.load({
params: {
start: 0,
limit: 15
}
});
currConvStore.load({
params: {
start: 0,
limit: 15
}
});
periodStore.load({
params: {
start: 0,
limit: 15,
entityId:this.entityId,
fs_tab:this.fs_tab

}
});

//this.button = Ext.getComponent('crestHeaderPanel').getComponent('crestHeaderButton');

SPD.creditStatsPanel.superclass.initComponent.call(this);

},
registerListeners: function(){
this.button.on('click',this.refreshComponents, this);
},
refreshComponents: function(){

/*var record = this.sourceList.store.getAt(this.sourceList.selectedIndex);
source = record.get('DD_VALUE');

var record = this.periodList.store.getAt(this.periodList.selectedIndex);
period = record.get('DD_VALUE');

var record = this.currencyList.store.getAt(this.currencyList.selectedIndex);
currency = record.get('DD_VALUE');

var record = this.currConvList.store.getAt(this.currConvList.selectedIndex);
currconv = record.get('DD_VALUE');

var record = this.columnOrderList.store.getAt(this.columnOrderList.selectedIndex);
colorder = record.get('DD_VALUE');


var creditstatsgrid = Ext.getCmp('creditstatsgrid');
creditstatsgrid.source = source;
creditstatsgrid.period = period;
creditstatsgrid.currency = currency;
creditstatsgrid.currconv = currconv;
creditstatsgrid.colorder = colorder;

var creditstatsgrid = Ext.getCmp('creditstatsgrid');
creditstatsgrid.fireEvent('refresh', this);
*/

}
});


Ext.reg('creditStatsPanel', SPD.creditStatsPanel);

spextjs
25 Jun 2008, 9:48 AM
Where did you call showGrid function from? How can this be changed to include GridPanel instead of Grid?






Here's what I came up with:

Dynamic Json Reader: this will read whatever data comes back from the JSON call. It doesn't take a record definition because it builds it on the fly. Unfortunately it only has any record information *after* the JSON call has completed, so you can't simply pass it into a Grid, etc. because it won't be initialized at that time.


Ext.data.DynamicJsonReader = function(config){
Ext.data.DynamicJsonReader.superclass.constructor.call(this, config, []);
};
Ext.extend(Ext.data.DynamicJsonReader, Ext.data.JsonReader, {
getRecordType : function(data) {
var i = 0, arr = [];
for (var name in data[0]) { arr[i++] = name; } // is there a built-in to do this?

this.recordType = Ext.data.Record.create(arr);
return this.recordType;
},

readRecords : function(o){ // this is just the same as base class, with call to getRecordType injected
this.jsonData = o;
var s = this.meta;
var sid = s.id;

var totalRecords = 0;
if(s.totalProperty){
var v = parseInt(eval("o." + s.totalProperty), 10);
if(!isNaN(v)){
totalRecords = v;
}
}
var root = s.root ? eval("o." + s.root) : o;

var recordType = this.getRecordType(root);
var fields = recordType.prototype.fields;

var records = [];
for(var i = 0; i < root.length; i++){
var n = root[i];
var values = {};
var id = (n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
for(var j = 0, jlen = fields.length; j < jlen; j++){
var f = fields.items[j];
var map = f.mapping || f.name;
var v = n[map] !== undefined ? n[map] : f.defaultValue;
v = f.convert(v);
values[f.name] = v;
}
var record = new recordType(values, id);
record.json = n;
records[records.length] = record;
}
return {
records : records,
totalRecords : totalRecords || records.length
};
}
});

DynamicColumnModel: this takes a Store as a parameter, and from that it will build a default ColumnModel where all the fields are included and have width: 300.


Ext.grid.DynamicColumnModel = function(store){
var cols = [];
var recordType = store.recordType;
var fields = recordType.prototype.fields;

for (var i = 0; i < fields.keys.length; i++)
{
var fieldName = fields.keys[i];
var field = recordType.getField(fieldName);
cols[i] = {header: field.name, dataIndex: field.name, width:300};
}
Ext.grid.DynamicColumnModel.superclass.constructor.call(this, cols);
};
Ext.extend(Ext.grid.DynamicColumnModel, Ext.grid.ColumnModel, {});


Here's how I use it -


function showGrid(container, url)
{
// create the Data Store
var ds = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({url: url}),
reader: new Ext.data.DynamicJsonReader({root: '...my root, yours will be different...'}),
remoteSort: true
});
ds.on('load', function() {
// Reset the Store's recordType
ds.recordType = ds.reader.recordType;
ds.fields = ds.recordType.prototype.fields;

// Create the grid
var grid = new Ext.grid.Grid(container, {
ds: ds,
cm: new Ext.grid.DynamicColumnModel(ds),
selModel: new Ext.grid.RowSelectionModel({singleSelect:true}),
enableColLock:true
});

// render it
grid.render();
});
ds.load();
}

I haven't really put it through its paces yet but it seems to work for whatever JSON calls I try. If this were more generic I'd want to add some things like:
* Some way to provide column widths and types for known columns - merge with the unknowns. Maybe a callback function of some sort.
* A better way to declare this type of grid that has to wait until the data has been fetched before rendering.
* A DynamicXmlReader to match the JSON reader
* etc.

Note if Jack reads this: the DynamicJsonReader code could be even smaller if you adopt the change I made to the readRecords() method...

Steve

tidalbobo
11 Oct 2008, 5:41 PM
Quite an old thread.. But can u possibly provide the code for this functionality?
Now that i know u have it, i dont like to reinvent the wheel.
Thank you in advance.



Well, if you fist find out what the records are going to contain, you can create an appropriate ColumnModel programatically by simply assembling a column config Array.

My wrapper of the Grid sends an encoded query to the server, receives back info about the columns, builds the ColumnModel and Record definition on the fly from this recieved info, creates the Grid and loads it.

I only have one Grid class which will handle all my possible queries.

nick saint
16 Nov 2008, 11:30 PM
Please, if it's not hard explain how to use it.
Really need it
Thank you
Nick


Here's what I came up with:

Dynamic Json Reader: this will read whatever data comes back from the JSON call. It doesn't take a record definition because it builds it on the fly. Unfortunately it only has any record information *after* the JSON call has completed, so you can't simply pass it into a Grid, etc. because it won't be initialized at that time.


Ext.data.DynamicJsonReader = function(config){
Ext.data.DynamicJsonReader.superclass.constructor.call(this, config, []);
};
Ext.extend(Ext.data.DynamicJsonReader, Ext.data.JsonReader, {
getRecordType : function(data) {
var i = 0, arr = [];
for (var name in data[0]) { arr[i++] = name; } // is there a built-in to do this?

this.recordType = Ext.data.Record.create(arr);
return this.recordType;
},

readRecords : function(o){ // this is just the same as base class, with call to getRecordType injected
this.jsonData = o;
var s = this.meta;
var sid = s.id;

var totalRecords = 0;
if(s.totalProperty){
var v = parseInt(eval("o." + s.totalProperty), 10);
if(!isNaN(v)){
totalRecords = v;
}
}
var root = s.root ? eval("o." + s.root) : o;

var recordType = this.getRecordType(root);
var fields = recordType.prototype.fields;

var records = [];
for(var i = 0; i < root.length; i++){
var n = root[i];
var values = {};
var id = (n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
for(var j = 0, jlen = fields.length; j < jlen; j++){
var f = fields.items[j];
var map = f.mapping || f.name;
var v = n[map] !== undefined ? n[map] : f.defaultValue;
v = f.convert(v);
values[f.name] = v;
}
var record = new recordType(values, id);
record.json = n;
records[records.length] = record;
}
return {
records : records,
totalRecords : totalRecords || records.length
};
}
});DynamicColumnModel: this takes a Store as a parameter, and from that it will build a default ColumnModel where all the fields are included and have width: 300.


Ext.grid.DynamicColumnModel = function(store){
var cols = [];
var recordType = store.recordType;
var fields = recordType.prototype.fields;

for (var i = 0; i < fields.keys.length; i++)
{
var fieldName = fields.keys[i];
var field = recordType.getField(fieldName);
cols[i] = {header: field.name, dataIndex: field.name, width:300};
}
Ext.grid.DynamicColumnModel.superclass.constructor.call(this, cols);
};
Ext.extend(Ext.grid.DynamicColumnModel, Ext.grid.ColumnModel, {});
Here's how I use it -


function showGrid(container, url)
{
// create the Data Store
var ds = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({url: url}),
reader: new Ext.data.DynamicJsonReader({root: '...my root, yours will be different...'}),
remoteSort: true
});
ds.on('load', function() {
// Reset the Store's recordType
ds.recordType = ds.reader.recordType;
ds.fields = ds.recordType.prototype.fields;

// Create the grid
var grid = new Ext.grid.Grid(container, {
ds: ds,
cm: new Ext.grid.DynamicColumnModel(ds),
selModel: new Ext.grid.RowSelectionModel({singleSelect:true}),
enableColLock:true
});

// render it
grid.render();
});
ds.load();
}I haven't really put it through its paces yet but it seems to work for whatever JSON calls I try. If this were more generic I'd want to add some things like:
* Some way to provide column widths and types for known columns - merge with the unknowns. Maybe a callback function of some sort.
* A better way to declare this type of grid that has to wait until the data has been fetched before rendering.
* A DynamicXmlReader to match the JSON reader
* etc.

Note if Jack reads this: the DynamicJsonReader code could be even smaller if you adopt the change I made to the readRecords() method...

Steve

jayantkolekar
1 Dec 2008, 10:35 PM
Hi Steve,

I am new to ExtJS could you help me by giving some information about dynamic column in grid. Also can you post a sample Json you have created.

alf-solution
8 Jan 2009, 12:29 PM
Hi,

As many of you, I am new to ExtJS and it is not simple to get in. I also faced the problem of having a grid with dynamic columns. Based on the exemple of this thread I managed to get it working, so I thought it would be helpfull to some of you to discover this solution.

The solution is working with PHP5 and JSON.

I order to test with different table, I wrote a small PHP file that will get as parameter the table name. So to see the result use the URL :

http://localhost/List.php5?table=sometablename

All the ExtJS files should be placed into a subdirectory called "scripts".
The file templaces.inc.php (see below) should be into a subdirectory called "includes".
If you prefer to put those files somewhere else, please adapt the files according to the changes.

The file List.php5


<?php
/***********************************************
This exemple use a small template engine, like
smarty, spip,... that I wrote to have some kind
of MVC model.
***********************************************/
include_once('./includes/templates.inc.php');

if (isset($_GET['table'])){
$tablename = $_GET['table'];
} else {
$tablename='box';
}

// load the template
$myTemplate = new template('template/GetMembers.html');

header('Content-Type: text/html; charset=ISO-8859-15');
// Replace the variable {tablename} with the name of the table to retrieve data from.
$myTemplate->variable('tablename', $tablename);
ob_start();
echo $myTemplate->traitement();
ob_end_flush();
?>


The file templates.inc.php
[PHP]
<?php
class template
{
private $template;
private $debug_mode;
private $fichier;
private $blocs;
private $chrono;
private $debut;

function template($template, $mode=0, $chrono=FALSE)
{
if(is_file($template))
{
$this->template=file_get_contents($template);
$this->fichier=$template;
}
else if($mode!='debug')
{
echo 'Template introuvable';
}
else
{
echo 'Template - Erreur. L\'erreur retourn

jayantkolekar
9 Jan 2009, 11:48 AM
Thank you very much for your help.

Judy
20 Jan 2009, 12:18 PM
I like the dynamic grid function. It basically works. But, if I am rendering it into a window container, there is an issue with scope chain. Basically, if I deem the store which generates the grid in the scope that above the window (like if I use a button to put window renderer on button click event), it works - because the grid was already fulfilled and just waiting to be rendered into a container. But, if I put window in the higher scope chain it would not.

I understand why this is happening (although I may have failed to articulate it clearly). I am just wondering if anyone has a suggestion on how to have the grid refer to store's property instead of having it encapsulated within the store event. I could not figure it out.

sambar
30 Jan 2009, 7:58 AM
I get a H is null error, any suggestions ?

kishorebondada
20 Feb 2009, 3:51 AM
Hello spextjs,

Did you manage to develop a DynamicXMLReader too ? Would be very useful for my problem.

Atleast, any pointers/suggestions you would offer, if i were building one ?

stedav
23 Mar 2010, 1:13 AM
Thanks for this, but I'm getting an annoying error when I am using:




new Ext.grid.DynamicColumnModel(ds)'
..as the column model attribute for my grid. However, Firebug is telling me off its showing the following script error:



Ext.grid.DynamicColumnModel is not a constructor
I've followed your code almost to the letter (swapping out variable names obviously), but to no avail. The Constructor to my grid looks like this: (# beign a place holder for my actual names which are company based hence I wish not show them).




var #name = new Ext.grid.EditorGridPanel
(
{
id:'#id',
store:#ds,
plain: true,
colModel:new Ext.grid.DynamicColumnModel(#ds), //firebug error here
autoScroll:true,
stripeRows :true,
autoExpandColumn:'#colname',
width: '70%'
}

);




]I'm probably missing something plainly obvious here, but any suggestions?


:-/

jtpacheco
12 Apr 2010, 9:09 AM
The problem is that Ext.grid.DynamicColumnModel(obj) no longer exists in Ext 3.x, so you'll never be able to use that function. Currently I have no idea how to get grid columns loaded on the fly. My page is used for running queries to a Solr db, and displays the results in a grid. Current, it works for a set of data that I know to be there, and therefore is correct for only that query term. However, if I query other things, it obviously doesn't show up.

I'm not sure how to go about getting this done, as a lot of the forum suggestions are outdated (like this). The most I've thought of is to pass the column data with the JSON returned from query, but even then I don't know how to load that up to the column fields. I'm still tinkering with it, and eventually I'll find something, I'm sure...


Thanks for this, but I'm getting an annoying error when I am using:




new Ext.grid.DynamicColumnModel(ds)'
..as the column model attribute for my grid. However, Firebug is telling me off its showing the following script error:



I've followed your code almost to the letter (swapping out variable names obviously), but to no avail. The Constructor to my grid looks like this: (# beign a place holder for my actual names which are company based hence I wish not show them).




var #name = new Ext.grid.EditorGridPanel
(
{
id:'#id',
store:#ds,
plain: true,
colModel:new Ext.grid.DynamicColumnModel(#ds), //firebug error here
autoScroll:true,
stripeRows :true,
autoExpandColumn:'#colname',
width: '70%'
}

);




]I'm probably missing something plainly obvious here, but any suggestions?


:-/

djoo
30 May 2010, 3:22 AM
ExtJs forum volunteer. No official connection to the Ext Company. I do not speak for them.
ExtJs consultancy offered. £ 50/hour. Evenings+weekends. animal.software@btinternet.com

Search the forum: http://www.google.com/coop/cse?cx=01...%3Az7of1ufqccu (http://www.google.com/coop/cse?cx=011693920879787039234%3Az7of1ufqccu)
Read the docs too: http://extjs.com/deploy/dev/docs/
See Saki's samples: http://examples.extjs.eu/
Build your own Ext: http://extjs.com/products/extjs/build/
Scope: http://extjs.com/forum/showthread.ph...642#post257642 (http://extjs.com/forum/showthread.php?p=257642#post257642)

kalyan1230
25 Jun 2010, 3:36 AM
I need small help in the below mentioned code.

for (var i = 0; i < fields.keys.length; i++)
{
var fieldName = fields.keys[i];
var field = recordType.getField(fieldName);
cols[i] = {header: field.name, dataIndex: field.name, width:300};
}

I need Sorting on the column so i have added sortable:true, but the sorting is not working.

Can u please help me out for Sorting in DynamicColumnModel



Here's what I came up with:

Dynamic Json Reader: this will read whatever data comes back from the JSON call. It doesn't take a record definition because it builds it on the fly. Unfortunately it only has any record information *after* the JSON call has completed, so you can't simply pass it into a Grid, etc. because it won't be initialized at that time.


Ext.data.DynamicJsonReader = function(config){
Ext.data.DynamicJsonReader.superclass.constructor.call(this, config, []);
};
Ext.extend(Ext.data.DynamicJsonReader, Ext.data.JsonReader, {
getRecordType : function(data) {
var i = 0, arr = [];
for (var name in data[0]) { arr[i++] = name; } // is there a built-in to do this?

this.recordType = Ext.data.Record.create(arr);
return this.recordType;
},

readRecords : function(o){ // this is just the same as base class, with call to getRecordType injected
this.jsonData = o;
var s = this.meta;
var sid = s.id;

var totalRecords = 0;
if(s.totalProperty){
var v = parseInt(eval("o." + s.totalProperty), 10);
if(!isNaN(v)){
totalRecords = v;
}
}
var root = s.root ? eval("o." + s.root) : o;

var recordType = this.getRecordType(root);
var fields = recordType.prototype.fields;

var records = [];
for(var i = 0; i < root.length; i++){
var n = root[i];
var values = {};
var id = (n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
for(var j = 0, jlen = fields.length; j < jlen; j++){
var f = fields.items[j];
var map = f.mapping || f.name;
var v = n[map] !== undefined ? n[map] : f.defaultValue;
v = f.convert(v);
values[f.name] = v;
}
var record = new recordType(values, id);
record.json = n;
records[records.length] = record;
}
return {
records : records,
totalRecords : totalRecords || records.length
};
}
});DynamicColumnModel: this takes a Store as a parameter, and from that it will build a default ColumnModel where all the fields are included and have width: 300.


Ext.grid.DynamicColumnModel = function(store){
var cols = [];
var recordType = store.recordType;
var fields = recordType.prototype.fields;

for (var i = 0; i < fields.keys.length; i++)
{
var fieldName = fields.keys[i];
var field = recordType.getField(fieldName);
cols[i] = {header: field.name, dataIndex: field.name, width:300};
}
Ext.grid.DynamicColumnModel.superclass.constructor.call(this, cols);
};
Ext.extend(Ext.grid.DynamicColumnModel, Ext.grid.ColumnModel, {});Here's how I use it -


function showGrid(container, url)
{
// create the Data Store
var ds = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({url: url}),
reader: new Ext.data.DynamicJsonReader({root: '...my root, yours will be different...'}),
remoteSort: true
});
ds.on('load', function() {
// Reset the Store's recordType
ds.recordType = ds.reader.recordType;
ds.fields = ds.recordType.prototype.fields;

// Create the grid
var grid = new Ext.grid.Grid(container, {
ds: ds,
cm: new Ext.grid.DynamicColumnModel(ds),
selModel: new Ext.grid.RowSelectionModel({singleSelect:true}),
enableColLock:true
});

// render it
grid.render();
});
ds.load();
}I haven't really put it through its paces yet but it seems to work for whatever JSON calls I try. If this were more generic I'd want to add some things like:
* Some way to provide column widths and types for known columns - merge with the unknowns. Maybe a callback function of some sort.
* A better way to declare this type of grid that has to wait until the data has been fetched before rendering.
* A DynamicXmlReader to match the JSON reader
* etc.

Note if Jack reads this: the DynamicJsonReader code could be even smaller if you adopt the change I made to the readRecords() method...

Steve

k0smita
14 Jul 2010, 3:04 AM
To get DynamicJsonReader working I had to add two more lines to getRecordType (to call buildExtractors). Now the current version looks like that:


Ext.ns('Ext.data.DynamicJsonReader');

Ext.data.DynamicJsonReader = function(config)
{
Ext.data.DynamicJsonReader.superclass.constructor.call(this, config, []);
};

Ext.extend(Ext.data.DynamicJsonReader, Ext.data.JsonReader, {
/**
* Create Record automatically based on json data
* Private, called by readRecords()
* @param {Object} data Json data
*/
getRecordType: function(data) {
var i = 0, arr = [];

for (var name in data[0]) { arr[i++] = name; } // is there a built-in to do this?

this.recordType = Ext.data.Record.create(arr);

this.ef = false;
this.buildExtractors();

return this.recordType;
},

/**
* Create a data block containing Ext.data.Records from a JSON object.
* @param {Object} o An object which contains an Array of row objects in the property specified
* in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
* which contains the total size of the dataset.
* @return {Object} data A data block which is used by an Ext.data.Store object as
* a cache of Ext.data.Records.
*/
readRecords : function(o){
/**
* After any data loads, the raw JSON data is available for further custom processing. If no data is
* loaded or there is a load exception this property will be undefined.
* @type Object
*/
this.jsonData = o;
if(o.metaData){
this.onMetaChange(o.metaData);
}
//start modifications
var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;

var s = this.meta, Record = this.getRecordType(root),
f = Record.prototype.fields, fi = f.items, fl = f.length, v;
//end modifications

if(s.totalProperty){
v = parseInt(this.getTotal(o), 10);
if(!isNaN(v)){
totalRecords = v;
}
}
if(s.successProperty){
v = this.getSuccess(o);
if(v === false || v === 'false'){
success = false;
}
}

// TODO return Ext.data.Response instance instead. @see #readResponse
return {
success : success,
records : this.extractData(root, true), // <-- true to return [Ext.data.Record]
totalRecords : totalRecords
};
}
});

asbestos girl
9 Aug 2010, 5:32 AM
Another way to achieve dynamic column grids is using metaData.

in your js:

var ds = new Ext.data.Store({
url: '/cgi-bin/get_dynamic_grid_data.pl', // your script that fetches the data
reader: new Ext.data.JsonReader({
root: 'rows',
successProperty: 'success'
})
});

var grid = new Ext.grid.GridPanel({
ds: ds,
cm: new Ext.grid.ColumnModel([])
});
grid.render(Ext.getBody())

ds.on('metachange', function (store,meta) {
var columns = [];

for (var i = 0; i < meta.fields.length; i++ ) {
var hidden = meta.fields[i].hidden;
columns.push( { header: meta.fields[i].header, dataIndex: meta.fields[i].name, type: meta.fields[i].type, id: meta.fields[i].id, sortable: meta.fields[i].sortable } );
}

grid.reconfigure(store, new Ext.grid.ColumnModel(columns));
})

Then in your data fetching script return json in this format (called /cgi-bin/get_dynamic_grid_data.pl in this example):

{
metaData: {
fields: [
{header: 'Name', name: 'name', sortable: true},
{header: 'URL', name: 'url'},
{header: 'Size', name: 'size'},
{header: 'Last Modified', name: 'lastmod'}
],
root: 'rows'
},
rows: [
{name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
{name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
],
success: true
}The advantage of doing it this way is less code (no extensions required) and you can change the column model on one rendered grid as many times as you want. It also resolved the problem of being able to provide column widths, data types, sortable etc.

angelwen
19 Aug 2010, 11:06 PM
Hi Steve,
I am reusing your codes to generate dynamic grid. But I found the column width set in DynamicColumnModel did not work for me.
I changed the value but did not see any width changes in the page.
Do you know what's the problem?

Thanks
Angel

angelwen
19 Aug 2010, 11:15 PM
Hi Steve,
I have resolved it. Don't need take care of it.

Angel

PaulyWolly
12 Jul 2011, 10:41 PM
angelwen - Did you get the code to create the dynamic grid to work? Can you supply your final working model? I wish to know how to get the dynamic grid working. I am using EXTJS 3.4

I also was looking into the metadata route, but I do not know how to get the PHP to output the JSON formatted table fields to read from the database to supply the metaData. My model uses PHP on server, connected to Oracle using OCI8, and EXTJs 3.4.0

kengsreng
2 Aug 2011, 8:37 PM
Hi SteveEisner
my getRecordType return null !!!!!
can you help me ??

sgfrias
16 May 2012, 11:21 AM
SteveEisner thanks! i solved a big problem :D