it's already been several times I run into some kind of Dynamic Grid/Store/Reader implementation browsing Ext forum. I've also developed my own solution for this problem.
But why all these attempts?
Recently, before I found Ext, I was developing a huge web project with about 200 tables in mySQL and sold to various types of customers. You can imagine that if you have 200 tables how many queries do you need...
The number of queries/forms/grids wouln't be problem by itself but it's becoming a problem when you need to modify something either as a bug fix or as an adjustment to a specific customer needs.
There were times when I was really desperate when I recieved a 10 phone calls a day and I had to change and debug first query than a form than a grid and when I was ready and happy with that another customer called and asked for something similar.(:
Therefore, I adopted the policy: "Always have one source of data!" and I adjusted that project accordingly by developing a query builder and by saving all queries in one place. That actually saved my life and project too.
Now, I'm developing in Ext and imagine:
I need a table on the server to hold data
I need a query to fetch data from the server
I need a JsonReader with hard coded fields
I need a Grid with hard coded ColumnModel
OK, I put it together nicely and it works in a relatively short time. But, what if I need to display data from another table, another query? Well, cycle through 2-4, and another, another, another... And if you have 200 tables, believe me, you have 1000 queries.
Well, this job is huge but can be done. However, what happens when customer calls that: "I want to delete field XY from form YZ and want to add field AB to it."? Well, search through hundreds of files to find (at least) query/reader/grid configs, change, debug and of course, introduce new bugs for other user.
I believe that developers feel that this is not the way and that is the reason they are trying to develop an universal, auto, dynamic components. I believe that it's a dream of every bigger project maintainer that all changes are implemented fast, customer specific and bug free.
And, it's impossible to achieve if you have more than one source of (config) data.
Therefore I appeal to you Jack to provide for such components in Ext without any need of having single one letter of hard coded record/field/column model configuration.
Always have one source of (config) data!
Requset server to deliver "fields" object. This is record metadata.
Configure a component with it
Request server to deliver data into the configured componentI want you to understand that this is not my hot problem; I can always find a solutions for myself. All my effort in this thread is to establish a solid basics for future Ext development. Of course I'd be glad if I had original-ext-solution right now ;)
I'm attaching code of my "AutoStore" as an example. I have also solutions for other components. These work for me fine but they are not universal and "ext-way" enough.
// define Perseus object
var Perseus = Perseus {};
/**
* Ext.Store that auto-configures itself from server
*
* @class Perseus.AutoStore
* @extends Ext.Store
* @constructor
* Creates new AutoStore
* @param {String/Object} config A string to set only the title or a config object
* @cfg {String} objName Object name to send to server
* @cfg {String} url to get data from. Defaults to '/ajax.php'
* @cfg {Boolean/Object} autoLoad true or params for load
*/
Perseus.AutoStore = function(config) {
// create and apply baseParams
var cfg = {}; cfg.baseParams = {};
Ext.apply(cfg.baseParams, config.baseParams, {
requestID: "getData"
, objName: config.objName null
, format: "fields"
});
// I just do not want to have this cluttered with a ****
if(config.baseParams) {
delete(config.baseParams);
}
// apply config
Ext.apply(this, config, {url:'/ajax.php'});
// create proxy
this.proxy = new Ext.data.HttpProxy({url:this.url, scope:this, method:'post'});
// fool parent constructor
this.recordType = function(){};
this.recordType.prototype.fields = null;
// call parent constructor
Perseus.AutoStore.superclass.constructor.call(this , cfg);
// add events
this.addEvents({
/**
* fired when datastore is initialized and ready to load data
* signature ({Perseus.AutoStore} store)
*/
ready: true
});
}; // end of constructor
// extend
Ext.extend(Perseus.AutoStore, Ext.data.Store, {
// {{{
init: function() {
new Ext.data.Connection().request({
url: this.url
, method: 'post'
, scope: this
, params: this.baseParams
, callback: function(options, bSuccess, response) {
// {{{
// create recordType array
var o = Ext.decode(response.responseText);
if("object" != typeof o) {
throw "OrgboardPanel: json object not found";
}
var fields = o.fields;
if("object" != typeof fields) {
throw "OrgboardPanel: fields object not found";
}
var rta = ; // recordType array
var id, type = "auto";
for(var fname in fields) {
var field = fields[fname];
if(field.ispkey) {
id = fname;
}
// convert field type
if(field.type) {
if(field.type.match(/char/)) {
type = "string";
}
else if(field.type.match(/text/)) {
type = "string";
}
else if(field.type.match(/int/)) {
if(2 < field.size) {
type = "int";
}
else {
type = "boolean";
}
}
else if(field.type.match(/date/)) {
type = "date";
}
else if(field.isnum) {
type = "float";
}
else {
type = "auto";
}
}
else {
type = "auto";
}
rta.push({
name: fname
, type: type
, dateFormat: "date" == type ? "Y-m-d H:i:s" : null
, nameLocal: field.name_local
, alias: field.alias
, aliasLocal: field.alias_local
, sqlType: field.type
, sqlSize: field.size
, sqlIsReq: field.isreq ? true : false
, sqlIsNum: field.isnum ? true : false
, sqlIsExpr: field.isexpr ? true : false
});
}
// }}}
// create reader
this.reader = new Ext.data.JsonReader({
root: "records"
, totalProperty: "totalCount"
, id: id
}, rta);
// do what parent constructor should do but we've fooled him
this.recordType = this.reader.recordType;
this.fields = this.recordType.prototype.fields;
// prepare to load records
this.baseParams.format = 'json';
// fire ready event
this.fireEvent("ready", this);
// load data if requested
if("object" == typeof this.autoLoad) {
this.load({params:this.autoLoad});
}
else if(this.autoLoad) {
this.load();
}
}
});
} // end of init
// }}}
// todo this is not used yet. I had only plan to extract defaults out
, defaults: {
url: '/ajax.php'
, baseParams: {
requestID: "getData"
, format: "fields"
}
}
}); // end of extend
// end of file
After experimenting a bit I came up with this:
http://ext.designism.nl/examples/autosetupgrid/autosetupgrid.html
What do you think?
Maybe something like this should be supported in Ext2.0
I just tried the above link but the grid does not work in IE7
additionally i have this code:
Ext.data.Store.prototype.originalLoad = Ext.data.Store.prototype.load;
Ext.override(Ext.data.Store, {
load : function(options) {
if(!this.reader.meta) {
this.baseParams.meta = true;
}
this.originalLoad(options);
if (this.baseParams.meta) delete this.baseParams.meta;
}
});
which basically means that every reader that has no meta set (through the constructor or from the server) sends the meta parameter in the first request.
The nice thing: you don't have to call store.load({params:{meta: true}}) manually - which is very useful if you use the store for a ComboBox with mode=remote
I have a class, "ListManager" which tries to encapsulate a very high level view. You tell it what entity (table) to view, and optionally, some filter parameters, and sort parameters, and it passes these in to a server-side object which knows a lot about the database and the persistent objects from both Java introspection, our own Java annotations on our entity classes, and from Hibernate's metadata. This server-side object has the smarts to build an HQL (Hibernate Query Language, a superset of SQL) query to provide the data for the Grid.
You can also pass the ListManager an array of URLs to display in the "south" Region of its layout which depend upon the selected row in the Grid. On selection change the Panels in the "south" region are refreshed from their URLs with the entity name, database row id, Panel container id, and ListManager id passed so that the generating script knows what it is dealing with and where it is being put, and how to subscribe to ListManager events ("rowclick" etc).
The ListManager communicates with the server-side object stored in the HttpSession which provides the data. The ListManager creates it at first, and the create function returns a column definition object from which it calculates everything Record layout and ColumnModel.
Once it has a reference to this server-side object, it can then request new pages, sort the list, or requery with new filters.
It can be created either in a passed container, or if not passed a container, it creates its own BasicDialog.
It is the main class which will drive our application. Almost everything will be driven by a list with a detail view below it.
All key lookups (selecting customer, currency, etc) use a pop up ListManager to display available choices, with "rowdblclick" selecting the record, and populating the input field.
case 'ComboBox' :
editor.config.store = eval(editor.config.store);
field.editor = new Ext.grid.GridEditor(new Ext.form.ComboBox(editor.config));
break;
but i dont know if is the rigth way to do that :-?
It has this in it's read method:
if(o.metaData){
delete this.ef;
this.meta = o.metaData;
this.recordType = Ext.data.Record.create(o.metaData.fields);
this.onMetaChange(this.meta, this.recordType, o);
}
So you can pass back from the server a field definition array in the property metaData.fields, and it will reconfigure itself. The default onMetaChange method is an empty imlpementation, and a function is injected by a Store. IMHO, it should fire an event which the Store listens for.
This makes at least the data part of a Grid dynamically configurable. The view part - the ColumnModel will still need to be created, but you can probably infer that from the metaData.fields object. You would need to be able to listen for a "metachange" event though to hook your ColumnModel creation code in.
Jack, can the configurable JsonReader fire an event on metadata change rather than calling an injected function?
After experimenting a bit I came up with this:
http://ext.designism.nl/examples/autosetupgrid/autosetupgrid.html
What do you think?
Maybe something like this should be supported in Ext2.0
autogrid suport paging, just a few adds to php code and js ;)
autogrid+paging (http://www.cadenatextilconfeccion.org/genius551v/autogrid-rev-10(paging)/)
download code (http://www.cadenatextilconfeccion.org/genius551v/autogrid-rev-10(paging).zip)
tnks JorisA for the expiration
My wish list:
(if anybody is interesting for collaboration)
1. load with diferent query (for ex: query2.php, query3.php) (query2,query3 diferent metadata)
2. fix bug in hide column (try hide column and reload)
there is a new and improved version of this Grid:
http://ux.designism.nl/ux/autogrid/
Its also posted to the User Extensions Page in the Wiki (http://extjs.com/learn/Ext_Extensions) :-)
And it works with paging ans expected :-)
Bye
Tim
PD.your auto grid can be editor-grid?
tnks
Like I said, new to EXT, but like it! Is there an EXT security model I should be following?
Thanks!
- N
No, it's MacBaren Vanilla Cream in Peterson pipe. ;)
Don't even drink alcohol. Just smoking...
It has this in it's read method:
if(o.metaData){
delete this.ef;
this.meta = o.metaData;
this.recordType = Ext.data.Record.create(o.metaData.fields);
this.onMetaChange(this.meta, this.recordType, o);
}
So you can pass back from the server a field definition array in the property metaData.fields, and it will reconfigure itself. The default onMetaChange method is an empty imlpementation, and a function is injected by a Store. IMHO, it should fire an event which the Store listens for.
This makes at least the data part of a Grid dynamically configurable. The view part - the ColumnModel will still need to be created, but you can probably infer that from the metaData.fields object. You would need to be able to listen for a "metachange" event though to hook your ColumnModel creation code in.
Jack, can the configurable JsonReader fire an event on metadata change rather than calling an injected function?
The idea is to provide meta data once (on first load?) or multiple times for reconfiguring the reader. Paging or reloads shouldn't provide meta data.
I have added a metachange event on Store in SVN.
http://ux.designism.nl/ux/autogrid/
I'm trying to create a sort of simple user extension demo framework so if download/source links don't work please let me know.
How do I have to change your Script to use Paging?
try this:
just a mistake :-)
you must be very attentive reader if you were able to find that funny line. ;)
// fool parent constructor
this.recordType = function(){};
this.recordType.prototype.fields = null;
Ext.Store constructor just expects that it has recordType. But I don't have the recordType at the time of construction. I have it after I get metadata from server.
My solution is to "fool parent (Ext.data.Store) constructor" with empty values and than repair it at the time of init.
Maybe not best solution, but it works. This is the example of that "weak points" I mentioned in one of my previous posts in this thread.
http://extjs.com/forum/showthread.php?t=3719
It gets the data, and from that it infers the schema. I think this method is better than the round-trip get schema, get data that has been proposed earlier. I like the idea of the new metaData property, too, since there are sometimes going to be column properties that you simply can't automatically infer.
Steve
var ds = new Ext.data.Store({
proxy: new Ext.data.HttpProxy(....),
reader: new Ext.data.JsonReader()});
ds.on('metachange', function(ds, meta) {
var colModel = ....generate out of meta;
var grid = new Ext.grid.Grid('container', {dataSource: ds, colModel: colModel});
grid.render();
});
ds.load({params: {meta: true}}); //send meta only on first request
This JSON is returned by the server:
{"rows":[{"id":"1","visible":"1","name":"foo"},{"id":"1","visible":"1","name":"bar"}],
"metaData":{"fields":[{"name":"id","type":"int"},{"name":"visible","type":"boolean"},{"name":"name","type":"string"}],"root":"rows","id":"id"},
"success":true}
greetings,
vivid-planet
'editor' => Array(
'type'=>'ComboBox',
'config' => Array(
'allowBlank' => false,
'typeAhead' => true,
'triggerAction' => 'all',
'lazyRender' => true,
'displayField' => 'name',
'valueField' => 'value',
'store' => 'new Ext.data.SimpleStore({fields:["name","value"],data:[["Nacional","N"],["Internacional","I"]]})'
)
But the new Ext.data.SimpleStore is going as a String, if I take off the single cotes, PHP try to evaluate the code and die trying.
Somebody have some idea on how to get the new Ext.data.SimpleStore evaluated in JavaScript in the moment of store asign on ComboBox? Or how to pass it without being evaluated by PHP?
Come on... I know that are coders more cleaver than me out there! :)
It's not question that I'm not willing to learn new things but I already have established the server environment I use (Linux HA cluster with MySQL and PHP) that has proven long term reliability and availability.
There are also administrators trained on this so introducing new (untested) frameworks might be too costly.
Finally, is the ListManager going to be included in Ext?
my concept is same as yours; details differ of course. I still think that Ext could provide better for auto configuration of pertinent components.
In priciple it is same as displaying data. You do not know in Ext what are column names so you have dataIndex property in grid and you let user to provide the mapping.
The same is not true for other config data, e.g. I'm expected to write header displayed in column in Ext code. If we take this header as an example, server knows better what's header text. Either it's in database as kind of "Display Name" or it pipes through localization but in any case server can, and maybe should, provide "Display Name".
Take the above only as an example. Server can provide other metadata like: is field numerical? => NumberField, is field date? => DateField what's size of field? => Text/Textarea, is field required? => allowBlank, etc.
So what would be my ideal scene:
I have ONE datum that server can use to identify my request, let's say requestID.
I create unconfigured components store/reader/proxy/grid/whatever
I provide server metadata <-> Ext metadata mapping for these components. This is quite feasible as neither server's metadata nor Ext metadata structures are likely to change frequently.
I put requestID plus a flag "sendAlsoMetadata" to params and I make request
Components (store/reader/proxy/grid/whatever) know they are not configured yet so the take server's metadata, map them to Ext metadata and configure themselves.
Then they process/display received data.
The above is one server request.
On next request I clear "sendAlsoMetadata" flag and components work like now.
If I want to display another data (sql table/query) in the same grid I just change the requestID and proceed to step 4.
This, IMHO, would execute my one-source-of-data policy to the letter as on any change (added/deleted/modified) field of sql query I wouldn't need to touch the Ext app. I would only change the server's response to the given requestID. Change = piece of cake.
Also, It wouldn't make Ext too tightly bound to any kind of server as Ext wouldn't take care about server's metadata; it would have user defined mapping instead.
Also, the current funcionality wouldn't need to be changed; one flag "autoConfigure" (default false) would switch off all this logic allowing user to hard-code store/reader/grid config as of now.
I understand that if one is developing a public web page where one uses "a bit of Ext" one doesn't care about any such features.
On the other hand, Ext is that good that it can be used for full blast applications that deal with thousands of records/views/forms/grids far beyond the scope of a simple public web page.
I believe that I'm not the only one who is going to use Ext for such applications. Applications that will be used by hundreds of big customers around the globe.
This functionality is very important from this viewpoint.
Good luck.
We can add a {single:true} handler to the Store's metachange which can create the Grid's ColumnModel from the metadata on initial load of the Store. The Grid would be rendered in that handler, and then be ready to accept data from the Store when the Store fires it's "datachanged" event.
So it should become possible to subclass Grid to have completely dynamic config which just pops up the correct columns based on initial metadata.
After experimenting a bit I came up with this:
http://ext.designism.nl/examples/autosetupgrid/autosetupgrid.html
What do you think?
Maybe something like this should be supported in Ext2.0
Hi! I agree with JorisA! And just had a chance to play with his proof code. TY! That was exactly what I was thinking. And I am hoping this sort of thing gets expanded on and internalized into EXT. I'm playing with vivid-planet's suggestion as well. The more that I read, seems like we are all creating very similar code sets for this functionality, I'm guessing if we look in the general population we will find quite a few more. So to me that makes it a core EXT pattern.
This might also fix jsakalos problems with having 200 some data source files...
Thanks to Animal for pointing out the metachange event. My apologies, I didn't read back far enough and it makes a lot of things possible. And of course Jack's cool addition to make this possible. Along with JorisA's proof and vivid-planet's add.
My addition to this in my own code was to add a security coupon along with a bunch of general purpose data type renders and handlers for things like add, delete, etc...
Sorry I didn't reply earlier, I was stuck on a 24hr a day clock pounding out some code for an install at Cisco and just found some free time.
Best, -N
newt, can you show (share) us your code?
tnks
I have it mostly functional but for the life of me I cannot find a way to delete a columnModel from the grid after the grid has been rendered. I have followed the logic behind AutoGrid and have it pulling data from a JSON source, paging and showing data. If I call reconfigure inside of a metaChanged event the columnModel is not changed and the data is not being displayed. I am specifically doing this.
colModel = this.buildCustomColumnModel(store);
this.reconfigure(store, colModel);
The buildCustomColumModel method was referenced in another post but here it is.
buildCustomColumnModel : function(ds) {
var config = ;
config[0] = {header:'bogus field', hidden:true, width:0};
for(var a in ds.fields.keys) {
After experimenting a bit I came up with this:
http://ext.designism.nl/examples/autosetupgrid/autosetupgrid.html
What do you think?
Maybe something like this should be supported in Ext2.0
Thank you very much for this code! My auto grid is working now! :D
override.js
Ext.grid.AutoGrid = function(container, config){
// cant render withot cm... workaround?
if(!config.cm) {
config.cm = new Ext.grid.ColumnModel([{header: ""}]);
}
Ext.grid.AutoGrid.superclass.constructor.call(this , container, config);
// register the metachange event
if(this.dataSource){
this.dataSource.on("metachange", this.onMetaChange, this);
}
};
Ext.extend(Ext.grid.AutoGrid, Ext.grid.EditorGrid, {
cellRenderers : {},
addRenderer : function(name, fn) {
this.cellRenderers[name] = fn;
},
onMetaChange : function(store, meta) {
//console.log("onMetaChange", store, meta);
var field;
var config = ;
for(var i=0; i
// loop for every dataIndex, only add fields with a header property
field = meta.fields[i];
if(field.header !== undefined){
field.dataIndex = field.name;
// search for cell render functions by [field.renderer] or _[field.name]
if(this.cellRenderers[field.renderer]) {
field.renderer = this.cellRenderers[field.renderer];
} else if(this.cellRenderers["_"+field.name]) {
field.renderer = this.cellRenderers["_"+field.name];
}
// add editors
if(field.editor !== undefined){
var editor = field.editor;
switch (editor.type)
{
case 'TextField' : field.editor = new Ext.grid.GridEditor(new Ext.form.TextField(editor.config));
break;
case 'NumberField' : field.editor = new Ext.grid.GridEditor(new Ext.form.NumberField(editor.config));
break;
case 'DateField' : field.editor = new Ext.grid.GridEditor(new Ext.form.DateField(editor.config));
break;
case 'Checkbox' : field.editor = new Ext.grid.GridEditor(new Ext.form.Checkbox(editor.config));
break;
default : alert('type: unknow');
}
}
// add to the config (field.name is replaced by dataIndex)
delete field.name;
config[config.length] = field;
}
}
// Create the new cm, and update the gridview.
var cm = new Ext.grid.ColumnModel(config);
this.reconfigure(this.dataSource, cm);
}
});
and query.php:
require_once "../config/config.inc.php";
require_once "../../../lib/Json/json.php";
$json_service = new Services_JSON();
$prod_coleccion_manager = Application::getDomainController('Prod_coleccionMa nager');
$registros_tabla = $prod_coleccion_manager->getAllProd_coleccion();
$_reader_root = "data";
$_reader_id = "id";
$_reader_totalProperty = count($registros_tabla); //for paging
$_reader_fields = Array(
Array(
'name' => "id",
'type' => "int",
'header' => "Id",
//'renderer' => 'usMoney',
//'hidden' => true,
//'locked' => true,
//'tooltip' => 'Hola Mundo!!!',
'editor' => Array(
'type'=>'NumberField',
'config' => Array(
'allowBlank' => false,
'allowNegative' => false
),
),
'width' => 20
),
Array(
'name' => "nombre",
'header' => "Nombre",
'editor' => Array(
'type'=>'TextField',
'config' => Array(
'allowBlank' => false
),
),
'width' => 250
),
Array(
'name' => "fecha_inicio",
//'type' => 'date',
//'dateFormat' => 'timestamp',
'header' => "Fecha Inicio",
//'renderer' => 'formatDate',
'editor' => Array(
'type'=>'DateField',
'config' => Array(
'allowBlank' => false,
'format' => 'm/d/y'//,
//'minValue' => '01/01/06',
//'disabledDays' => '[0, 6]',
//'disabledDaysText' => 'Plants are not available on the weekends'
),
),
'width' => 100
),
Array(
'name' => "fecha_fin",
'header' => "Fecha Final",
'renderer' => 'formatDate',
'editor' => Array(
'type'=>'DateField',
'config' => Array(
'allowBlank' => false,
'format' => 'm/d/y'//,
//'minValue' => '01/01/06',
//'disabledDays' => '[0, 6]',
//'disabledDaysText' => 'Plants are not available on the weekends'
),
),
'width' => 100
),
Array(
'name' => "observaciones",
'header' => "Observaciones",
'editor' => Array(
'type'=>'TextField',
'config' => Array(
'allowBlank' => true
),
),
'width' => 200
),
Array(
'name' => "id_estado",
'type' => "int",
'header' => "Estado",
'editor' => Array(
'type'=>'Checkbox',
'config' => '',
),
'width' => 50
)
);
$rows = Array();
for($i=0;$i
}
$json = Array();
if(isset($_REQUEST['meta'])) {
// if requested send meta data
$json['metaData'] = Array(
'root' => $_reader_root,
'id' => $_reader_id,
'totalProperty' => $_reader_totalProperty, //for paging
'fields' => $_reader_fields
);
}
$json[$_reader_root] = Array();
foreach ($rows as $i => $row) {
$jrow = Array();
// For each field, add the according value to the data row
foreach($_reader_fields as $field) {
$jrow[$field['name']] = $row[$field['name']];
}
// push this row to the rows array
array_push($json[$_reader_root], $jrow);
}
echo "(".$json_service->encode($json).")";
?>
testing....i think should be fine
2. Hmm I see your point. not sure how to fix yet.
Are you working on a fix?
(3.) How about Sorting?
If i try to make a Column sortable I get this Error-Message
this.config[col] has no properties
if(typeof this.config[col].sortable == "undefined") {
It would be easy to write if you defined a Json structure, or XML structure that the server must provide, then it's a doddle to loop through that and create the Store and Record and Reader objects.
I think it's just tying everyone down to this absolutely defined structure might be a problem.
Perhaps you could send back from the server
{
columns: [
{mapping:"name", header:"Name", width:100, type:"string",
sortable:true ... other ColumnModel properties...},
{}...
]
}
The "mapping" is the column name (probably best use an alias).
You could process that into the appropriate objects quite easily.
That would work for you.
Wouldn't work for me though.
But, the story is much more complicated because this gives you info on all columns from one table. And what if you have a query with some expression fields retrieving data from more (joined) tables?
Therefore, I've written PHP classes that take care about metadata generation also in these more complicated cases.
Thanks.
If you're going to be changing the metadata dynamically, and totally changing your Grid, it's probably best to call destroy on any existing Grid first.
http://ext.designism.nl/examples/autosetupgrid/autosetupgrid.html
I used your code and it works great except for one thing: I can't seem to get paging to work... I get a message like "Displaying page NaN of NaN to 20" in the paging toolbar and of course, the page navigation buttons don't work.
Have you tried your autogrid with a paging bar? Any suggestions?
I'm not sure if I understand all this yet. =) I'm still just in the learning stages here and not a JSON or javascript king.
But if I had my choice, I'd want the ability to have my data object be encapsulated, so that none of it's specific code or description was in EXT. In this case my data object would be an encapsulted php file that provides to EXT all the necessary info when called, but the language really makes no difference. Then EXT would take that info and do the right thing with it.
In essence then you've abstracted the data and it's specifics out of EXT and into the data source file where it belongs. This would make maintenance way easier, you always only have one server file to edit, the data source file. If you need to do any substitutions, it can be done in the data source file and so on... And it removes data source description editing from your complexity stack, pushing it into the language of the programmer's choice. Making it also easier for them. To take this one step further, then the server file, in this case a php file could then use existing language specific db code to interrogate the db if they like to provide automated grids. Removing this also from your complexity stack.
This also would then allow for a simpler standardized implementation in EXT when handling data sources and extremely easy switching of data sources in a grid. If blah I display this data source or data format, if blah2 I display this data source or data format, making the grid data source and format nicely dynamic, removed from the presentation(the grid) and more loosely coupled.
And it would allow the programmer to set in the data source file whether it was a static data source format needed only the first time for display and reused for the gird or a dynamic one where it should recall to get it. In either case there should be no real performance hit, as it needs to call the data source file each time anyways.
Is this what you are trying to do? I saw some of the code snippets and read the comments, but wasn't so sure.
Best, N
I'm not calling for tools, that's my job, of course. I'm calling for components without hard coded configuration.
And don't take it as criticism of Ext in any way - I've never seen anything better for web app than Ext.
I'm just looking in the future and it's my prime interest to have even better Ext. Such Ext that will be real solid base for projects of any size regardles if it's a one grid somewhere on a page or thousands of files/grid/forms/records/queries, whaterver.
After to add the ComboBox stuff into the Autogrid.js, I tryed to use transform (follow the plant example). The ComboBox worked right, but my grid stoped from paging and reloading. In truth it continue to made the requests to server and receive the responses but the grid don't change anymore.
Somebody know how to put a Grid inside a pagenable Autogrid?
I tryed to use store too, but don't know how to construct this store manually in server side!!!
Thanks Jack; I haven't tested yet but I trust to your ability to create great things.
http://ux.designism.nl/ux/autogrid/
I'm trying to create a sort of simple user extension demo framework so if download/source links don't work please let me know.
My AutoGrids and Pagenable AutoGrids are working ok, the problem is with my editable Grids.
First:
When I double click the cell to edit, it works, but is given me a error in FireBug:
[Exception... "'Permission denied to read the property HTMLDivElement.nodeType' when calling method: [nsIDOMEventListener::handleEvent]" nsresult: "0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)" location: "
Second:
How do I do to create a editable with ComboBox?
I added to Autogrid.js this:
case 'ComboBox' :
field.editor = new Ext.grid.GridEditor(new Ext.form.ComboBox(editor.config));
break; Worked, but don't know how to populate it with server side options in PHP.
Some idea?
Third:
My Grids are sortable, but when click it, don't show the little arrow in header showing if it is ascending or descending, the only way to select it is right clicking and selecting the way I want to sort the grid.
Is something missing to show the arrow when click header to sort?
Thanks for any help!
hi, i try extend your solution to editor, where in your code your create or config the column?
i try some like this:
$_reader_fields = Array(
Array(
'name' => "id",
'type' => "int",
'header' => "Id",
'editor' => Array(
'type'=>'NumberField',
'allowBlank' => false,
'allowNegative' => false
),
'width' => 20
),
Array(
'name' => "nombre",
'header' => "Nombre",
'editor' => Array(
'type'=>'TextField',
'allowBlank' => false
),
'width' => 250
),
what are you think?
tnks
As you can see, I have used my own server <-> Ext metadata processing/mapping. What I am calling for in this thread is to provide in Ext some built-in mechanism.
Another problem I'm struggling with is that some objects have to be present and configured at the time of instantiation of another objects. Even in the above code I had to "fool the parent constructor".
These troublesome points should be spotted in Ext and rewritten to provide for smooth and flawless auto configuration.
thank you for your interest, your contribution and your vote for adding the functionality to Ext.
I have solved this problem meanwhile by extending Store and Grid to auto-configure themselves on the receipt of metadata. I haven't published the code and I don't believe I will as my solution is not universal enough and is too tightly bound to my serverside database layer (mySQL/PHP/DB layer written by myself)@Linux.
Although I won't use the code you posted myself, I believe that it will be useful somebody else struggling with the same problem.
Thanks again,
Get all metadata
Construct & config
Get dataThanks for informing, I'll think over it a little bit more then I'll let you know.
I have solved this in my way, and I have an Ext based component that integrates tightly with a servlet which can access any table in my database (I'm using Hiberrnate, so it actually accesses "entities").
I generate a ColumnModel and Record definition on the fly based on metadata passed back from the servlet, and get a Grid based on all viewable properties (columns) of the selected entity (table).
The servlet is passed parameters which define what entity is being accessed, and what criteria to apply, and what properties to sort by etc, and back comes a response in Json format which is basically a ColumnModel definition - you could pass it directly into the ColumnModel constructor, but there's a bit extra in there to allow generation of a Record layout too.
This response is processed to create the ColumnModel and Record def.
The servlet caches the query in the session and it can be accessed for subsequent paging and sorting.
Even without Hibernate, you could use JDBC's database metadata to find a lot of this information, and automate a lot of this.
The approuch you take now seems ok, then again I haven't checked the editorgrid yes. You should add a handler in the onMetaChange loop. It's iterated for every field, so you can add the editor properties there.
Code is not ready yet, I can post it later if you'll be interested.
It has this in it's read method:
if(o.metaData){
delete this.ef;
this.meta = o.metaData;
this.recordType = Ext.data.Record.create(o.metaData.fields);
this.onMetaChange(this.meta, this.recordType, o);
}
So you can pass back from the server a field definition array in the property metaData.fields, and it will reconfigure itself. The default onMetaChange method is an empty imlpementation, and a function is injected by a Store. IMHO, it should fire an event which the Store listens for.
This makes at least the data part of a Grid dynamically configurable. The view part - the ColumnModel will still need to be created, but you can probably infer that from the metaData.fields object. You would need to be able to listen for a "metachange" event though to hook your ColumnModel creation code in.
Jack, can the configurable JsonReader fire an event on metadata change rather than calling an injected function?
No, I haven't tested it yet, but this is veeeeery good news. To tell you truth I'm not very big fan of updating svn every day while working on a production application and testing the new features/bugs endlessly; I rather wait for a stable version.
But I'm very glad to hear this as it looks like that my voice was heard, listened to and understood. (Thanks Jack!)
I'll maybe give it a try soon.
I ran into the same issue you did, I have multiple tables, and they are differenent for each client logging in, so I needed a way to dynamically get the metadata of the tables and build the column models for Ext. I too, wish that Ext could handle this automatically.
I'm working on ASP.Net and I was able to handle it by passing an array of the table names and another array of the column definitions at startup:
// array is ['data_source', table_name']...
var layerList = [['Data Layers','Call Data'],['Data Layers','NpaNxx by Region'],['Data Layers','US NpaNxx'],['Data Layers','Cities'],['Data Layers','DMA'],['Data Layers','Zip Codes'],['Data Layers','Counties'],['Data Layers','Census Tracts'],['Data Layers','Block Groups'],['Data Layers','States']];
var Columns = [
[
['SESSIONID','System.String'],['DIALEDNUMB','System.String'],['ANI','System.String'],['CPN','System.String'],['CALLEDNUMB','System.String'],['DID','System.String'],['TFN','System.String'],['TOTALTIME','System.Double'],['IVRTIME','System.Double'],['XFERTIME','System.Double'],['TTA','System.Double'],['COMPCODEID','System.Double'],['ENDSTATE','System.String'],['CALLSTART','System.DateTime'],['CALLSTOP','System.DateTime'],['LAT','System.Double'],['LON','System.Double'],['DIALEDNOAN','System.Double'],['ORIGTZ','System.Double'],['TERMTZ','System.Double'],['NETWORKID','System.Double'],['LOCATIONID','System.Double'],['ZIPCODE','System.String']
],
[
['LON','System.Double'],['LAT','System.Double'],['NPANXX','System.Int32'],['PL_NAME','System.String'],['ST_NAME','System.String'],['TIME_ZONE','System.String'],['DAYLT_OBS','System.String'],['ZIP','System.String'],['COUNTY','System.String'],['REGION','System.String'],['RATE_CENTE','System.String'],['DATE_','System.Int16'],['ST','System.String']
],
[
['LID','System.String'],['LON','System.Double'],['LAT','System.Double'],['NPA','System.Int16'],['NXX','System.Int16'],['NPANXX','System.Int32'],['NXXTYPE','System.String'],['PL_NAME','System.String'],['ST_NAME','System.String'],['TIME_ZONE','System.String'],['DAYLT_OBS','System.String'],['POINT_IND','System.String'],['EFF_DATE','System.String'],['ZIPCODE','System.String'],['COUNTY','System.String']
],
[
['NAME','System.String'],['CLASS','System.String'],['ST','System.String'],['STFIPS','System.String'],['PLACEFIP','System.String'],['CAPITAL','System.String'],['AREALAND','System.Double'],['AREAWATER','System.Double'],['POP_CLASS','System.Int32'],['POP2000','System.Int32'],['WHITE','System.Int32'],['BLACK','System.Int32'],['AMERI_ES','System.Int32'],['ASIAN','System.Int32'],['HAWN_PI','System.Int32'],['OTHER','System.Int32'],['MULT_RACE','System.Int32'],['HISPANIC','System.Int32'],['MALES','System.Int32'],['FEMALES','System.Int32'],['AGE_UNDER5','System.Int32'],['AGE_5_17','System.Int32'],['AGE_18_21','System.Int32'],['AGE_22_29','System.Int32'],['AGE_30_39','System.Int32'],['AGE_40_49','System.Int32'],['AGE_50_64','System.Int32'],['AGE_65_UP','System.Int32'],['MED_AGE','System.Double'],['MED_AGE_M','System.Double'],['MED_AGE_F','System.Double'],['HOUSEHOLDS','System.Int32'],['AVE_HH_SZ','System.Double'],['HSEHLD_1_M','System.Int32'],['HSEHLD_1_F','System.Int32'],['MARHH_CHD','System.Int32'],['MARHH_NO_C','System.Int32'],['MHH_CHILD','System.Int32'],['FHH_CHILD','System.Int32'],['FAMILIES','System.Int32'],['AVE_FAM_SZ','System.Double'],['HSE_UNITS','System.Int32'],['VACANT','System.Int32'],['OWNER_OCC','System.Int32'],['RENTER_OCC','System.Int32']
],
[
['DMA','System.String'],['NAME','System.String'],['MARKETID','System.Double'],['MARKETNAME','System.String'],['CustomerGDB_DBO_DMA_area','System.Double'],['CustomerGDB_DBO_DMA_len','System.Double'],['Shape_Length','System.Double']
],
[
['OBJECTID_1','System.Int32'],['ZIP','System.String'],['PO_NAME','System.String'],['STATE','System.String'],['SUMBLKPOP','System.Int32'],['POP2005','System.Int32'],['POP05_SQMI','System.Double'],['SQMI','System.Double'],['AREA_','System.Double'],['PERIMETER','System.Double'],['ZPR_','System.Double'],['ZPR_ID','System.Double'],['ST_FIPS','System.String'],['Shape_Length','System.Double']
],
[
['CNTY','System.String'],['NAME','System.String'],['STATEFIPS','System.String'],['STATE','System.String'],['COUNT_','System.Int32'],['Shape_Length','System.Double']
],
[
['STATE_FIPS','System.String'],['CNTY_FIPS','System.String'],['STCOFIPS','System.String'],['TRACT','System.String'],['FIPS','System.String'],['POP2000','System.Int32'],['POP2005','System.Double'],['POP00_SQMI','System.Double'],['POP05_SQMI','System.Double'],['WHITE','System.Int32'],['BLACK','System.Int32'],['AMERI_ES','System.Int32'],['ASIAN','System.Int32'],['HAWN_PI','System.Int32'],['OTHER','System.Int32'],['MULT_RACE','System.Int32'],['HISPANIC','System.Int32'],['MALES','System.Int32'],['FEMALES','System.Int32'],['AGE_UNDER5','System.Int32'],['AGE_5_17','System.Int32'],['AGE_18_21','System.Int32'],['AGE_22_29','System.Int32'],['AGE_30_39','System.Int32'],['AGE_40_49','System.Int32'],['AGE_50_64','System.Int32'],['AGE_65_UP','System.Int32'],['MED_AGE','System.Double'],['MED_AGE_M','System.Double'],['MED_AGE_F','System.Double'],['HOUSEHOLDS','System.Int32'],['AVE_HH_SZ','System.Double'],['HSEHLD_1_M','System.Int32'],['HSEHLD_1_F','System.Int32'],['MARHH_CHD','System.Int32'],['MARHH_NO_C','System.Int32'],['MHH_CHILD','System.Int32'],['FHH_CHILD','System.Int32'],['FAMILIES','System.Int32'],['AVE_FAM_SZ','System.Double'],['HSE_UNITS','System.Int32'],['VACANT','System.Int32'],['OWNER_OCC','System.Int32'],['RENTER_OCC','System.Int32'],['SQMI','System.Double'],['Shape_Length','System.Double']
],
[
['STATE_FIPS','System.String'],['CNTY_FIPS','System.String'],['STCOFIPS','System.String'],['TRACT','System.String'],['BLKGRP','System.String'],['FIPS','System.String'],['POP2000','System.Int32'],['POP2005','System.Double'],['POP00_SQMI','System.Double'],['POP05_SQMI','System.Double'],['WHITE','System.Int32'],['BLACK','System.Int32'],['AMERI_ES','System.Int32'],['ASIAN','System.Int32'],['HAWN_PI','System.Int32'],['OTHER','System.Int32'],['MULT_RACE','System.Int32'],['HISPANIC','System.Int32'],['MALES','System.Int32'],['FEMALES','System.Int32'],['AGE_UNDER5','System.Int32'],['AGE_5_17','System.Int32'],['AGE_18_21','System.Int32'],['AGE_22_29','System.Int32'],['AGE_30_39','System.Int32'],['AGE_40_49','System.Int32'],['AGE_50_64','System.Int32'],['AGE_65_UP','System.Int32'],['MED_AGE','System.Double'],['MED_AGE_M','System.Double'],['MED_AGE_F','System.Double'],['HOUSEHOLDS','System.Int32'],['AVE_HH_SZ','System.Double'],['HSEHLD_1_M','System.Int32'],['HSEHLD_1_F','System.Int32'],['MARHH_CHD','System.Int32'],['MARHH_NO_C','System.Int32'],['MHH_CHILD','System.Int32'],['FHH_CHILD','System.Int32'],['FAMILIES','System.Int32'],['AVE_FAM_SZ','System.Double'],['HSE_UNITS','System.Int32'],['VACANT','System.Int32'],['OWNER_OCC','System.Int32'],['RENTER_OCC','System.Int32'],['SQMI','System.Double'],['Shape_Length','System.Double']
],
[
['STATE_NAME','System.String'],['STATE_FIPS','System.String'],['SUB_REGION','System.String'],['STATE_ABBR','System.String'],['POP2000','System.Int32'],['POP2005','System.Int32'],['POP00_SQMI','System.Double'],['POP05_SQMI','System.Double'],['WHITE','System.Int32'],['BLACK','System.Int32'],['AMERI_ES','System.Int32'],['ASIAN','System.Int32'],['HAWN_PI','System.Int32'],['OTHER','System.Int32'],['MULT_RACE','System.Int32'],['HISPANIC','System.Int32'],['MALES','System.Int32'],['FEMALES','System.Int32'],['AGE_UNDER5','System.Int32'],['AGE_5_17','System.Int32'],['AGE_18_21','System.Int32'],['AGE_22_29','System.Int32'],['AGE_30_39','System.Int32'],['AGE_40_49','System.Int32'],['AGE_50_64','System.Int32'],['AGE_65_UP','System.Int32'],['MED_AGE','System.Double'],['MED_AGE_M','System.Double'],['MED_AGE_F','System.Double'],['HOUSEHOLDS','System.Int32'],['AVE_HH_SZ','System.Double'],['HSEHLD_1_M','System.Int32'],['HSEHLD_1_F','System.Int32'],['MARHH_CHD','System.Int32'],['MARHH_NO_C','System.Int32'],['MHH_CHILD','System.Int32'],['FHH_CHILD','System.Int32'],['FAMILIES','System.Int32'],['AVE_FAM_SZ','System.Double'],['HSE_UNITS','System.Int32'],['VACANT','System.Int32'],['OWNER_OCC','System.Int32'],['RENTER_OCC','System.Int32'],['NO_FARMS97','System.Double'],['AVG_SIZE97','System.Double'],['CROP_ACR97','System.Double'],['AVG_SALE97','System.Double'],['SQMI','System.Int32'],['Shape_Length','System.Double']
]
];
And then I defined the column mode:
//Build the column name array for use to define Ext datastore
var colNames = new Array();
for (x = 0; x < Columns[0].length; x++)
{
colNames[x] = Columns[0][x][0];
}
// Build the column definition by parsing through the columns array and setting up the defenitions
var colDef="";
var jsval = "colDef = [";
for (x = 0; x < Columns[0].length; x++)
{
var w = 80
if (Columns[0][x][0].length < 5) w = Columns[0][x][0].length * 20;
else if (Columns[0][x][0].length > 5 & Columns[0][x][0].length < 10) w = Columns[0][x][0].length * 15;
else if (Columns[0][x][0].length > 10 & Columns[0][x][0].length < 15) w = Columns[0][x][0].length * 10;
else w = Columns[0][x][0].length * 8;
jsval = jsval + "n{header: '" + Columns[0][x][0] + "', width: " + w + ", dataIndex: '" + Columns[0][x][0] + "'},";
}
//remove the last comma
jsval = jsval.substring(0, jsval.length -1);
jsval = jsval + "n]";
eval(jsval);
Then I use these to define the column model and datastore:
// create the Data Store
var ds = new Ext.data.Store({
// load using HTTP
proxy: new Ext.data.HttpProxy({url: 'AttributeGridHandler.ashx'}),
// the return will be XML, so lets set up a reader
reader: new Ext.data.XmlReader({
// records will have an "Item" tag
record: 'row',
id: 'OBJECTID',
totalRecords: 'TotalCount'
}, colNames)
});
var cm = new Ext.grid.ColumnModel(colDef);
I hope this helps
describe `tablename`;
You then get a two-dimensional array, with each row containing the field name, type and size, key type, etc etc... If you used PHP exclusively, I think it would be a series of seperate commands, if they exist (which I believe they do.... never wanted to spend time with them though, since I can use SQL and get everything I need.)
Jason
Obviously, that's asynchronous, so the calling code has to put any subsequent processing into an "onready" event that the ListManager offers.
eg
var lm = new Aspicio.ListManager({
drivingEntity:"Area",
filter:"country.code='GB'",
detailPanels:[{url:"/maint/Area.jsp", loadOnRowSelect:true"},
{url:"/maint/Address.jsp", loadonRowSelect:true}],
container:"main-content-panel"
});
lm.on("ready", function(){
// continue processing
});
lm.on("rowdblclick" function(lm, record, rowIndex) {
// process click
});
There's no reason why you'd [B]need/B] Hibernate to write your own ListManager. There's a lot you can find out about a database. I don't know about PHP, but in Java's JDBC, you can find out about the whole schema, tables, columns, indices, foreign keys, everything. So you could write a smart component for the server side which a ListManager could partner with. (You might end up writing something like Hibernate which provides full ORM mapping)
The ListManager is a proprietary class which I developed for my employers, so I can't share it, but the concept is simple enough.
Just subscribe to a Store's "metachange" event: http://extjs.com/deploy/ext-1.1-beta1/docs/output/Ext.data.Store.html#event-metachange
I'm currently on developing a plugin for Grails (http://grails.org), a Java/Groovy based and great Webframework. The plugin is an instant administration UI for the domain model which also uses some sort of autoconfiguration for grid and forms from metadata, but implemented in a custom way. I have set up a live demo ... It's not really up-to-date, but it shows some basic operations ...
The blog entry with a link to the demo (http://siegfried.puchbauer.com/blog/2007/06/grails-ext-userinterface.html)
cheers, sigi
1. If you mean loading from different urls you probably can just change the reader url instead of changing grid.dataSource.baseParams.style as in the example. Although I'm not sure why you would want to do this.
2. Hmm I see your point. not sure how to fix yet.
may be you can look the code AutoGrid from this post and recommend to us something about ethraza problems, because i have this too.
tnks
// I just do not want to have this cluttered with a ****
:>
#If you have any other info about this subject , Please add it free.# |
edit