Ronaldo
3 Feb 2009, 2:15 PM
Hi all,
I see more and more server side grid configuration solutions, so I thought I might post mine as well here. It's just a grid plugin and it's easily configurable.
The reason I wrote it is that it does not require much code in the grid itself, and that makes it very reusable.
Here's how I define my grid:
MyGrid = Ext.extend(Ext.grid.GridPanel, {
url: '/someUrl',
title: 'MyGrid',
remoteSort: true,
primaryEntity: 'Customer',
initComponent: function(){
var g=Ext.ux.grid;
this.plugins = [
new Ext.ux.grid.MetaGridPlugin(),
...
];
this.store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: this.url
}),
reader: new Ext.data.JsonReader({
root: 'rows',
totalProperty: 'rowCount',
id: 'id',
fields: this.mapping
}),
baseParams: ...,
remoteSort: true
});
this.cm = new Ext.grid.ColumnModel([{ id: '', dataIndex: '', width: 100}]);
this.mapping = [{name: '', mapping: '', type: 'string'}];
MyGrid.superclass.initComponent.call(this);
},
....
});
Note that I do need a column model and a mapping, but that's just because the grid needs a column. But that setting will be overridden by the server's response.
The plugin adds a parameter 'meta' to the request, and the first time this parameter looks like
{"primary":"Customer","fields":[""],"requestMeta":true}Yes, it's JSON. I thought that a single field might reduce the changes of interfering with other parameters. It basically tells the server that the primary file/entity of the grid is the Customer file, it doesn't know about any fields, and that it needs the metadata to configure itself.
The response should be
{"success":true,
"rowCount":140,
"rows":[
{"id":"1052","name":"A customer name","address":"some address"},
... more data ...
],
"metaData": {
"fields":[{"name":"id"},{"name":"name"},{"name":"address"}],
"root":"rows",
"totalProperty":"rowCount",
"id":"id",
"successProperty":"success"
}
}
In further requests, the grid has configured itself, and requests the fields to be displayed; the meta parameter now contains:
{"primary":"Customer","fields":["id","name","address"]}
And you don't need include the metaData in your response.
Of course, this makes the server side a little more complex, as it needs to know about a 'customer' and the fields available. Moreover, it should be able to create a query based on the fields in the request.
Btw, using php, doctrine and zend, I have all the metadata (fields, fieldtypes etc) in my doctrine model.
And here's the plugin.
/**
* @Author Twensoc - Ronald van Raaphorst
* @Version 0.9
* @License GPL v3
*/
Ext.ux.grid.MetaGridPlugin = function(config) {
Ext.apply(this, config, {
requestMeta: true
});
}
Ext.ux.grid.MetaGridPlugin.prototype = {
init : function(grid) {
this.grid = grid;
grid.store.on('metachange', this.onMetaChange, this);
grid.store.on('beforeload', this.onBeforeLoad, this);
grid.on('beforeviewchange', this.onBeforeViewChange, this);
grid.on('viewchange', this.onViewChange, this);
},
destroy: function() {
delete(this.grid);
},
onBeforeLoad: function(store, options) {
var flds = [];
store.fields.each(function(item) {
flds.push(item.name);
}, this);
p = {
primary: this.grid.primaryEntity,
fields: flds
}
if(this.requestMeta) {
p.requestMeta = this.requestMeta;
}
store.baseParams.meta = Ext.util.JSON.encode(p);
},
onMetaChange : function(store, meta) {
var colModel = this.createColumnModel(meta.fields);
if(meta.sortInfo) {
//this.grid.store.sortInfo = meta.sortInfo;
}
this.requestMeta = false;
this.grid.reconfigure(store, colModel);
},
onBeforeViewChange: function(grid, view, custom) {
this.requestMeta = true;
},
onViewChange: function(grid, view, custom, fields) {
if(custom==true && fields) {
this.grid.store.removeAll();
var colModel = this.createColumnModel(fields);
this.grid.reconfigure(this.grid.store, colModel);
}
},
createColumnModel: function(flds) {
var columns = [];
var autoExpand = false;
var fld = null;
for(var i=0,n=flds.length; i<n; i++) {
fld = flds[i];
if(fld.hidden == true)
continue;
fld = this.createColumn(fld);
fld.id = fld.id || 'col'+i;
columns.push(fld);
}
return new Ext.grid.ColumnModel(columns);
},
createColumn: function(fld) {
fld.header = fld.header || fld.name;
fld.dataIndex = fld.mapping || fld.name;
fld.width = fld.width ? (typeof field.width == 'string' ? parseInt(field.width) : fld.width) : 100;
delete fld.name;
return fld;
}
}Note that it's version 0.9, as some column properties are not supported yet. I'm working on it ;)
I already have taken this idea a step further, and having given each grid their own id, I can compose different views for the same grid, as you can see in the attachments.
Picture 2 shows a list of views defined for a log grid. Picture 1 shows the views, all the fields available (including parent tables), and the fields set for the plain view.
You can drag and drop fields from the tree to the field list and save them as a view. Later on, I intend to add filters to a view, so everyone can easily adapt every grid to their own preferences and switch between different views of the same data.
Now I intend to publish this tool too, but if it's used commercially, I'll ask a small fee. Drop me a note if you're interested.
I see more and more server side grid configuration solutions, so I thought I might post mine as well here. It's just a grid plugin and it's easily configurable.
The reason I wrote it is that it does not require much code in the grid itself, and that makes it very reusable.
Here's how I define my grid:
MyGrid = Ext.extend(Ext.grid.GridPanel, {
url: '/someUrl',
title: 'MyGrid',
remoteSort: true,
primaryEntity: 'Customer',
initComponent: function(){
var g=Ext.ux.grid;
this.plugins = [
new Ext.ux.grid.MetaGridPlugin(),
...
];
this.store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: this.url
}),
reader: new Ext.data.JsonReader({
root: 'rows',
totalProperty: 'rowCount',
id: 'id',
fields: this.mapping
}),
baseParams: ...,
remoteSort: true
});
this.cm = new Ext.grid.ColumnModel([{ id: '', dataIndex: '', width: 100}]);
this.mapping = [{name: '', mapping: '', type: 'string'}];
MyGrid.superclass.initComponent.call(this);
},
....
});
Note that I do need a column model and a mapping, but that's just because the grid needs a column. But that setting will be overridden by the server's response.
The plugin adds a parameter 'meta' to the request, and the first time this parameter looks like
{"primary":"Customer","fields":[""],"requestMeta":true}Yes, it's JSON. I thought that a single field might reduce the changes of interfering with other parameters. It basically tells the server that the primary file/entity of the grid is the Customer file, it doesn't know about any fields, and that it needs the metadata to configure itself.
The response should be
{"success":true,
"rowCount":140,
"rows":[
{"id":"1052","name":"A customer name","address":"some address"},
... more data ...
],
"metaData": {
"fields":[{"name":"id"},{"name":"name"},{"name":"address"}],
"root":"rows",
"totalProperty":"rowCount",
"id":"id",
"successProperty":"success"
}
}
In further requests, the grid has configured itself, and requests the fields to be displayed; the meta parameter now contains:
{"primary":"Customer","fields":["id","name","address"]}
And you don't need include the metaData in your response.
Of course, this makes the server side a little more complex, as it needs to know about a 'customer' and the fields available. Moreover, it should be able to create a query based on the fields in the request.
Btw, using php, doctrine and zend, I have all the metadata (fields, fieldtypes etc) in my doctrine model.
And here's the plugin.
/**
* @Author Twensoc - Ronald van Raaphorst
* @Version 0.9
* @License GPL v3
*/
Ext.ux.grid.MetaGridPlugin = function(config) {
Ext.apply(this, config, {
requestMeta: true
});
}
Ext.ux.grid.MetaGridPlugin.prototype = {
init : function(grid) {
this.grid = grid;
grid.store.on('metachange', this.onMetaChange, this);
grid.store.on('beforeload', this.onBeforeLoad, this);
grid.on('beforeviewchange', this.onBeforeViewChange, this);
grid.on('viewchange', this.onViewChange, this);
},
destroy: function() {
delete(this.grid);
},
onBeforeLoad: function(store, options) {
var flds = [];
store.fields.each(function(item) {
flds.push(item.name);
}, this);
p = {
primary: this.grid.primaryEntity,
fields: flds
}
if(this.requestMeta) {
p.requestMeta = this.requestMeta;
}
store.baseParams.meta = Ext.util.JSON.encode(p);
},
onMetaChange : function(store, meta) {
var colModel = this.createColumnModel(meta.fields);
if(meta.sortInfo) {
//this.grid.store.sortInfo = meta.sortInfo;
}
this.requestMeta = false;
this.grid.reconfigure(store, colModel);
},
onBeforeViewChange: function(grid, view, custom) {
this.requestMeta = true;
},
onViewChange: function(grid, view, custom, fields) {
if(custom==true && fields) {
this.grid.store.removeAll();
var colModel = this.createColumnModel(fields);
this.grid.reconfigure(this.grid.store, colModel);
}
},
createColumnModel: function(flds) {
var columns = [];
var autoExpand = false;
var fld = null;
for(var i=0,n=flds.length; i<n; i++) {
fld = flds[i];
if(fld.hidden == true)
continue;
fld = this.createColumn(fld);
fld.id = fld.id || 'col'+i;
columns.push(fld);
}
return new Ext.grid.ColumnModel(columns);
},
createColumn: function(fld) {
fld.header = fld.header || fld.name;
fld.dataIndex = fld.mapping || fld.name;
fld.width = fld.width ? (typeof field.width == 'string' ? parseInt(field.width) : fld.width) : 100;
delete fld.name;
return fld;
}
}Note that it's version 0.9, as some column properties are not supported yet. I'm working on it ;)
I already have taken this idea a step further, and having given each grid their own id, I can compose different views for the same grid, as you can see in the attachments.
Picture 2 shows a list of views defined for a log grid. Picture 1 shows the views, all the fields available (including parent tables), and the fields set for the plain view.
You can drag and drop fields from the tree to the field list and save them as a view. Later on, I intend to add filters to a view, so everyone can easily adapt every grid to their own preferences and switch between different views of the same data.
Now I intend to publish this tool too, but if it's used commercially, I'll ask a small fee. Drop me a note if you're interested.