PDA

View Full Version : RowEditor hbox element.style height



jasonb885
24 May 2009, 8:30 PM
I've spent a few days on this one, finally narrowing it down to what specific style is causing me trouble, but I don't know why.

The RowEditor example works fine in the examples. My specific usage of it seems to be giving me problems, though. I've run through the usual suspects. I ran md5sum against ext-all.css, ext-all-debug.js, and ext-base.js, just in case. I copied all the CSS from the RowEditor example html file inside the style tag as well. All assets are loading without any 404 errors.

Strangely, I end up with the following attached images from Firefox 3. I have the same behavior in Opera 9.64. You'll notice the height of the



<div id="ext-gen77" class="x-row-editor-body x-box-layout-ct" style="width: 705px; height: 17px;">


is fixed at 17px. In the functional RowEditor example, there is no height set. I have no idea how it is I am getting a height of 17px or how to resolve it. The active editor on the left has the height disabled in Firebug, so it looks mostly normal. I left the one on the right side broken to demonstrate the distinction.

Here's the page source.



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Inventory Manager</title>
<link href="/stylesheets/ext-all.css?1243222494" media="all" rel="stylesheet" type="text/css" />

<style type="text/css">
body {
background-color: #F5F7EE;
}

.systems-grid {
background-image:url(/images/laptop.png) !important;
}
.add-system {
background-image:url(/images/laptop_add.png) !important;
}
.remove-system {
background-image:url(/images/laptop_delete.png) !important;
}
.parts-grid {
background-image:url(/images/server_key.png) !important;
}
.add-part {
background-image:url(/images/mouse_add.png) !important;
}
.remove-part {
background-image:url(/images/mouse_delete.png) !important;
}
.menu-part {
background-image:url(/images/folder_table.png) !important;
}

.x-grid3 .x-window-ml{
padding-left: 0;
}
.x-grid3 .x-window-mr {
padding-right: 0;
}
.x-grid3 .x-window-tl {
padding-left: 0;
}
.x-grid3 .x-window-tr {
padding-right: 0;
}
.x-grid3 .x-window-tc .x-window-header {
height: 3px;
padding:0;
overflow:hidden;
}
.x-grid3 .x-window-mc {
border-width: 0;
background: #cdd9e8;
}
.x-grid3 .x-window-bl {
padding-left: 0;
}
.x-grid3 .x-window-br {
padding-right: 0;
}
.x-grid3 .x-panel-btns {
padding:0;
}
.x-grid3 .x-panel-btns td.x-toolbar-cell {
padding:3px 3px 0;
}
.x-box-inner {
zoom:1;
}
.ext-ie .x-row-editor .x-form-text {
margin:0 !important;
}
.x-row-editor-header {
height:2px;
overflow:hidden;
background: transparent url(/images/row-editor-bg.gif) repeat-x 0 0;
}
.x-row-editor-footer {
height:2px;
overflow:hidden;
background: transparent url(/images/row-editor-bg.gif) repeat-x 0 -2px;
}
.ext-ie .x-row-editor-footer {
margin-top:-1px;
}

.x-row-editor-body {
overflow:hidden;
zoom:1;
background: #ebf2fb;
padding-top:2px;
}
.x-row-editor .x-btns {
position:absolute;
top:28px;
left:20px;
padding-left:5px;
background: transparent url(/images/row-editor-btns.gif) no-repeat 0 0;
}
.x-row-editor .x-btns .x-plain-bwrap {
padding-right:5px;
background: transparent url(/images/row-editor-btns.gif) no-repeat right -31px;
}
.x-row-editor .x-btns .x-plain-body {
background: transparent url(/images/row-editor-btns.gif) repeat-x 0 -62px;
height:31px;
}
.x-row-editor .x-btns .x-table-layout-cell {
padding:3px;
}
.errorTip .x-tip-body ul{
list-style-type:disc;
margin-left:15px;
}
</style>

<script src="/javascripts/ext-base.js?1242678100" type="text/javascript"></script>

<script src="/javascripts/ext-all-debug.js?1243223180" type="text/javascript"></script>
<script src="/javascripts/Component.js?1243134898" type="text/javascript"></script>
<script src="/javascripts/MetaGrid.js?1243187284" type="text/javascript"></script>
<script src="/javascripts/RowEditor.js?1243220116" type="text/javascript"></script>
<script src="/javascripts/RowExpander.js?1242854040" type="text/javascript"></script>
<script src="/javascripts/override.js?1243203189" type="text/javascript"></script>

<script src="/javascripts/application.js?1243223900" type="text/javascript"></script>
</head>
<body>
</body>
</html>


And usage:



Ext.onReady(function() {

Ext.QuickTips.init();
new Ext.Viewport({
layout:'border',
items:[
{xtype:'grid-parts', region:'center', margins:'5 5 5 5'},
{
xtype:'grid-systems',
region:'east',
width:250,
margins:'5 5 5 0',
cmargins:'5 5 5 0',
collapsible:true
}
]
});

(function(){Ext.StoreMgr.lookup('store-systems').load();}).defer(200);
});


Several classes involved. The relevant portion is from abstractGrid:



var abstractGrid = function() {
abstractGrid.superclass.constructor.apply(this, arguments);
this.editor = this.plugins[1];
};
Ext.extend(abstractGrid, Ext.grid.GridPanel, {

loadMask:true,
autoExpandColumn:'comment',

viewConfig:{
forceFit:true
},

initComponent:function() {

this.store = {
autoDestroy:true,
reader:new Ext.data.JsonReader(),
proxy:new Ext.data.HttpProxy({
url:this.path,
method:'GET',
headers:{'Accept':'application/json'}
})
};

this.tools = [
{
id:'refresh',
qtip:'Refresh',
handler:function(e, el, p) {
p.getStore().reload();
}
}
];

this.plugins = [{ptype:'ux-grid-metagrid', maskEmpty:false}, editor];

var buttons = [
...
];

if(Ext.isArray(this.tbar)) {
this.tbar = this.tbar.concat(['-'].concat(buttons));
}
else {
this.tbar = buttons;
}

abstractGrid.superclass.initComponent.apply(this, arguments);

this.getStore().on('load', function(store) {

// Enable our buttons
this.addButton.enable();
this.deleteButton.enable();

// Ensure editor knows about changed columns
if(this.editor.initialized) {
this.editor.items.each(function(i) {
i.destroy();
});
this.editor.items.clear();
}
this.editor.initialized = false;

// Prevent spurious reconfigures
delete store.baseParams['reconfigure'];

}, this);
}
});


An extended class looks like the following. No surprises, except maybe the lack of a columnModel. That's handled by MetaGrid, which loads it via the metachange store event. Initially it has an empty columnModel, then onRender the store is loaded and the columns actually configured.



var partsGrid = Ext.extend(abstractGrid, {
title:'Inventory',
iconCls:'parts-grid',

path:'/parts',
singular:'Part',
plural:'Parts',
json_root:'part',

initComponent:function() {

this.tbar = [{xtype:'menu-parts', grid:this}];

partsGrid.superclass.initComponent.apply(this, arguments);
}
});
Ext.reg('grid-parts', partsGrid);


The meta:



{"metaData":
{"idProperty": "id", "totalProperty": "total", "root": "results", "successProperty": "success",
"fields": [
{"ptype": "rownumberer", "header": "", "autoDestroy": true, "name": ""},
{"hidden":true, "editable": false, "header": "id", "editor": {"xtype": "textfield"}, "name": "id"},
{"header": "name", "editor": {"xtype": "textfield"}, "name": "name"},
{"header": "comment", "editor": {"xtype": "textfield"}, "name": "comment"}
]
},
"results": [
{"name": "abcaaaa", "comment": "really long and useless comment", "id": 1},
{"name": "123", "comment": "", "id": 13},
{"name": "ab", "comment": "", "id": 14}
],
"total": 3, "success": true}


The overrides include code from a few places, but none of it seems related to my problem.

http://extjs.com/forum/showthread.php?t=65609 (IE quirks mode fix for RowEditor)

Stuff for plugins to better work with Ext.ux.MetaGrid:

https://extjs.com/forum/showthread.php?t=65977

And MetaGrid itself:

http://extjs-ux.org/repo/trunk/Ext/ux/grid/MetaGrid.js

At this point, I have no idea what else to try. Maybe if I better understood how the hbox layout decided to apply or not apply a height I'd have some idea to what to look for. I'm clueless when it comes to element positioning.

Thanks.

dawesi
25 May 2009, 7:24 AM
might get a faster response if you post a public demo of the issue.

also do a search of the code of each of these components for the word 'height' and see where it's calculated. Usually I've dropped in a combo or checkbox (or a style on these) and it's stuffed up the formatting, not necessarily the grid or alike.

Also any style you put in your css will be overwritten as the 17px height is on the element itself. you'd have to use !important to get there...

also use the layout tab in your style inspector in firebug to see what element(s) have extra margins, etc

jasonb885
25 May 2009, 7:34 AM
I have no doubts. Unfortunately, it requires a server side component. I have not yet been able to replicate it outside of Ext.ux.MetaGrid.

The RowEditor example actually includes json of its data for the charting component, amusingly, so maybe I can figure out how to get it to load ajax across file://.

jasonb885
26 May 2009, 4:52 PM
Finally came up with a server-less example that is broken as described earlier. It must have something to do with MetaGrid, but I have no idea what. Should run in a copy of the grid examples directory. I adjusted some of the CSS and JavaScript paths for my testing setup.

Once it loads, double click any row to bring up a RowEditor with a fixed height that should not appear on:



<div id="ext-gen36" class="x-row-editor-body x-box-layout-ct" style="width: 402px; height: 17px;">


(In this example, the Update RowEditor button appears clipped and is not visible, only the Cancel buttons appears. That's a different issue that seems to relate to autoExpandColumn, even without MetaGrid being used.)

Any ideas?

Here's the updated row-editor.js:



var jsond = {
metaData:{
fields:[
{name:'name', header:'name', editor:{xtype:'textfield'}},
{name:'email', header:'email', editor:{xtype:'textfield'}},
{name:'start', header:'start', editor:{xtype:'textfield'}},
{name:'active', header:'active', editor:{xtype:'textfield'}}
],
successProperty:'success',
totalProperty:'total',
root:'records'
},
total:12,
success:true,
records:[{
'name': 'Billy Bob',
'email': '[email protected]',
'start': '01/16/2007',
'active': true
},{
'name': 'Frisco Jones',
'email': '[email protected]',
'start': '02/01/2007',
'active': true
},{
'name': 'Mikey M',
'email': '[email protected]',
'start': '04/02/2007',
'active': true
},{
'name': 'Davey D',
'email': '[email protected]',
'start': '04/02/2007',
'active': true
},{
'name': 'Freddie F',
'email': '[email protected]',
'start': '04/15/2007',
'active': true
},{
'name': 'Susie S',
'email': '[email protected]',
'start': '06/21/2007',
'active': true
},{
'name': 'Julie J',
'email': '[email protected]',
'start': '06/28/2007',
'active': true
},{
'name': 'Martin M',
'email': '[email protected]',
'start': '07/01/2007',
'active': true
},{
'name': 'Bill B',
'email': '[email protected]',
'start': '10/07/2007',
'active': true
},{
'name': 'Jill J',
'email': '[email protected]',
'start': '10/07/2007',
'active': true
},{
'name': 'Chris C',
'email': '[email protected]',
'start': '12/01/2007',
'active': true
},{
'name': 'Kelly K',
'email': '[email protected]',
'start': '01/01/2008',
'active': true
}]
};

Ext.onReady(function(){

Ext.BLANK_IMAGE_URL = 'images/default/s.gif';
Ext.QuickTips.init();

var astore = new Ext.data.JsonStore({
autoDestroy:true,
data: jsond,
root: 'records',
fields: ['name', 'email', 'active', 'start']
});

var editor = new Ext.ux.RowEditor({
saveText: 'Update'
});

var grid = new Ext.grid.GridPanel({
id:'grid',

store:astore,

width: 600,
region:'center',
margins: '0 5 5 5',

// Doesn't survive if column by name doesn't exist
//autoExpandColumn: 'name',

// Doesn't survive reconfigure
autoExpandColumn: 0,

plugins: [new Ext.ux.grid.MetaGrid(), editor],

// Defer, needs changes for MetaGrid
/*
view: new Ext.grid.GroupingView({
markDirty: false
}),
*/

tbar: [{
iconCls: 'icon-user-add',
text: 'Add Employee',
handler: function(){
var e = new Employee({
name: 'New Guy',
email: '[email protected]',
start: (new Date()).clearTime(),
salary: 50000,
active: true
});
editor.stopEditing();
store.insert(0, e);
grid.getView().refresh();
grid.getSelectionModel().selectRow(0);
editor.startEditing(0);
}
},{
ref: '../removeBtn',
iconCls: 'icon-user-delete',
text: 'Remove Employee',
disabled: true,
handler: function(){
editor.stopEditing();
var s = grid.getSelectionModel().getSelections();
for(var i = 0, r; r = s[i]; i++){
store.remove(r);
}
}
}]
});

var layout = new Ext.Panel({
title: 'Employee Salary by Month',
layout: 'border',
layoutConfig: {
columns: 1
},
width:600,
height: 600,
//items: [chart, grid]
items: [grid]
});
layout.render(Ext.getBody());

grid.getSelectionModel().on('selectionchange', function(sm){
grid.removeBtn.setDisabled(sm.getCount() < 1);
});

Ext.apply(grid, {
reconfigure:grid.reconfigure.createSequence(function() {
if(editor.initialized) {
editor.items.each(function(i) {
i.destroy();
});
editor.items.clear();
}
editor.initialized = false;
})
});

// Cheat. Badly.
Ext.getCmp('grid').getStore().fireEvent('metachange', astore, jsond.metaData);


Updated row-editor.html:



<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Editor Grid Example</title>
<link rel="stylesheet" type="text/css" href="styles/ext-all.css" />
<!-- GC --><!-- LIBS --><script type="text/javascript" src="ext-base.js"></script>
<!-- ENDLIBS --><script type="text/javascript" src="ext-all-debug.js"></script>
<script type="text/javascript" src="Component.js"></script>
<script type="text/javascript" src="MetaGrid.js"></script>
<script type="text/javascript" src="gen-names.js"></script>
<script type="text/javascript" src="RowEditor.js"></script>
<script type="text/javascript" src="override.js"></script>
<script type="text/javascript" src="row-editor.js"></script>
<link rel="stylesheet" type="text/css" href="styles/grid-examples.css" />
<!-- Common Styles for the examples -->
<link rel="stylesheet" type="text/css" href="styles/examples.css" />
<!-- Common Styles for the examples -->
<style type="text/css">
.x-grid3 .x-window-ml{
padding-left: 0;
}
.x-grid3 .x-window-mr {
padding-right: 0;
}
.x-grid3 .x-window-tl {
padding-left: 0;
}
.x-grid3 .x-window-tr {
padding-right: 0;
}
.x-grid3 .x-window-tc .x-window-header {
height: 3px;
padding:0;
overflow:hidden;
}
.x-grid3 .x-window-mc {
border-width: 0;
background: #cdd9e8;
}
.x-grid3 .x-window-bl {
padding-left: 0;
}
.x-grid3 .x-window-br {
padding-right: 0;
}
.x-grid3 .x-panel-btns {
padding:0;
}
.x-grid3 .x-panel-btns td.x-toolbar-cell {
padding:3px 3px 0;
}
.x-box-inner {
zoom:1;
}
.ext-ie .x-row-editor .x-form-text {
margin:0 !important;
}
.x-row-editor-header {
height:2px;
overflow:hidden;
background: transparent url(images/row-editor-bg.gif) repeat-x 0 0;
}
.x-row-editor-footer {
height:2px;
overflow:hidden;
background: transparent url(images/row-editor-bg.gif) repeat-x 0 -2px;
}
.ext-ie .x-row-editor-footer {
margin-top:-1px;
}

.x-row-editor-body {
overflow:hidden;
zoom:1;
background: #ebf2fb;
padding-top:2px;
}
.x-row-editor .x-btns {
position:absolute;
top:28px;
left:20px;
padding-left:5px;
background: transparent url(images/row-editor-btns.gif) no-repeat 0 0;
}
.x-row-editor .x-btns .x-plain-bwrap {
padding-right:5px;
background: transparent url(images/row-editor-btns.gif) no-repeat right -31px;
}
.x-row-editor .x-btns .x-plain-body {
background: transparent url(images/row-editor-btns.gif) repeat-x 0 -62px;
height:31px;
}
.x-row-editor .x-btns .x-table-layout-cell {
padding:3px;
}
.icon-user-add {
background-image: url(../shared/icons/fam/user_add.gif) !important;
}
.icon-user-delete {
background-image: url(../shared/icons/fam/user_delete.gif) !important;
}

.errorTip .x-tip-body ul{
list-style-type:disc;
margin-left:15px;
}
</style>
</head>
<body>
<h1>Row Editor Grid Example</h1>
<p>
This example shows how to create a grid with inline row based editing using a custom row-editor.
The example code <a href="row-editor.js">row-editor.js</a> is not minified, so it's readable.
</p>
<p>
To use this example, be sure to include the <a href="RowEditor.js">RowEditor.js</a> file.
</p>

</p>
</body>
</html>


The Component.js override:



/**
Related ExtJS forum thread:
https://extjs.com/forum/showthread.php?t=65977
*/

Ext.override(Ext.Component, {

constructor: function (config) {

config = config || {};
if(config.initialConfig){
if(config.isAction){ // actions
this.baseAction = config;
}
config = config.initialConfig; // component cloning / action set up
}else if(config.tagName || config.dom || typeof config == "string"){ // element object
config = {applyTo: config, id: config.id || config};
}


this.initialConfig = config;

Ext.apply(this, config);
this.addEvents(

'disable',

'enable',

'beforeshow',

'show',

'beforehide',

'hide',

'beforerender',

'render',

'afterrender',

'beforedestroy',

'destroy',

'beforestaterestore',

'staterestore',

'beforestatesave',

'statesave'
);
this.getId();
Ext.ComponentMgr.register(this);
Ext.Component.superclass.constructor.call(this);

if(this.baseAction){
this.baseAction.addComponent(this);
}

this.initComponent();

var ps = this.plugins;
ps = this.plugins = ps ? [].concat(ps) : []
Ext.each(ps, function(p, i) {
ps[i] = this.initPlugin(p);
}, this);

/*
if(this.plugins){
if(Ext.isArray(this.plugins)){
for(var i = 0, len = this.plugins.length; i < len; i++){
this.plugins[i] = this.initPlugin(this.plugins[i]);
}
}else{
this.plugins = this.initPlugin(this.plugins);
}
}
*/

if(this.stateful !== false){
this.initState(config);
}

if(this.applyTo){
this.applyToMarkup(this.applyTo);
delete this.applyTo;
}else if(this.renderTo){
this.render(this.renderTo);
delete this.renderTo;
}
},

// existing code for initPlugin
initPlugin : function(p){
//this block should be moved to ComponentMgr similar to ComponentMgr.create
if(p.ptype && typeof p.init != 'function'){
p = Ext.ComponentMgr.createPlugin(p);
}else if(typeof p == 'string'){
p = Ext.ComponentMgr.createPlugin({
ptype: p
});
}
p.init(this);
return p;
},

// modified code for initPlugin
initPlugin : function(p, defaultType){

if(p.ptype && typeof p.init != 'function'){
p = Ext.ComponentMgr.createPlugin(p);
}else if(typeof p == 'string'){
p = Ext.ComponentMgr.createPlugin({
ptype: p
});
}

if (!p._initialized){
p.init(this);
}

/**
This completely hoses the RowEditor ux which uses initialized internally!
*/

p._initialized = true; // not sure if this is required yet (making sure a plugin is not initialized more than one time on a given component).

return p;
},

// need to return the initialized plugin, so only accept a single plugin
addPlugin : function(p, defaultType){
p = this.initPlugin(p, defaultType);
if (!this.plugins) {
this.plugins = [];
}
this.plugins.push(p);
return p;
}
});


override.js:



// Don't load the store, it's already loaded
Ext.override(Ext.ux.grid.MetaGrid, {
onRender:Ext.emptyFn
});


MetaGrid:



Ext.ns('Ext.ux.grid');

/**
* @class Ext.ux.grid.MetaGrid
* @extends Object
* <p><b>Demo</b> link (<i>Firefox only for this particular demo!</i>): <a href="http://extjs-ux.org/repo/authors/mjlecomte/trunk/Ext/ux/metagrid.html">here</a></p>
* <p>Plugin (ptype = 'ux-grid-metagrid') to add capability to automatically process a data packet
* with metaData sent by the server to configure a GridPanel or EditorGridPanel. To use, configure the
* grid using this plugin, and let the server do the rest.</p>
* <p>Sample usage:</p>
* <pre><code>
var grid = new Ext.grid.EditorGridPanel({
plugins: [
{ptype: 'ux-grid-metagrid'}
],
//colModel: provided by the plugin, configured through the metaData.fields
// property in the data packet sent back by server
store: {
//xtype: 'groupingstore',// Ext.data.GroupingStore
autoDestroy: true,
baseParams: {
foo: 'bar'
},
// create reader (reader will be further configured through metaData sent by server)
reader: new Ext.data.JsonReader(),
url: 'meta-data.txt' // see options parameter for Ext.Ajax.request
},
//autoExpandColumn: 'company', // configurable through metaData
loadMask: true,
clicksToEdit:1,
stripeRows: true,
viewConfig: {
emptyText: 'No Data'
}
});
* </code></pre>
* <p>Anatomy of a data packet:</p>
* <pre><code>
{
// the typical total and results
"total":30,
"results":[
// record data objects go here
],
"success":30,
// metaData to configure the grid
"metaData":{
// metaData goes here
}
}
* </code></pre>
* <p>Typically the metaData packet will provide at least the following configuration specifications:
* <pre><code>
{
...
"metaData":{
"idProperty":"id",
"root":"results", // notice this corresponds to the "results" property sent back above
"successProperty":"success",
"totalProperty":"total", // notice this corresponds to the "total" property sent back above
...
}
}
* </code></pre>
* <p>The reader and column model are configured through the metaData fields property:</p>
* <pre><code>
{
...
"metaData":{
...
"fields":[
// fields config objects go here
],
...
}
}
* </code></pre>
* <p>Additional configuration and custom properties can be specified in the metaData object:</p>
* <pre><code>
{
...
"metaData":{
...
"sortInfo":{
"field":"id",
"direction":"ASC"
},
// column model defaults
"cmDefaults":{
"sortable":true,
"menuDisabled":false,
"width":100
},
// column model listeners
"cmListeners":{
"widthchange":function(cm,colIndex,width){
saveConfig(colIndex,width);
}
},
// support for some grid configuration
"gridConfig":{
"autoExpandColumn":"company",
"viewConfig":{
"forceFit":true
}
},
// potential to alter the type of store used by the grid
"storeConfig":{
"xtype":"groupingstore"
// other store configs
},
...
}
}
* </code></pre>
* <p>The metaData configuration shown here would consume a data object of this format:</p>
* <pre><code>
"results":[
{
"companyID":"166",
"company":"3m Company",
"price":"44",
"change":"0.02",
"pctChange":"0.03",
"lastChange":"2007-08-01 00:00:00",
"industryID":"5",
"stars":"1",
"check":false,
"industry":"1",
"desc":"foo",
"tax":2.64
}
]
* </code></pre>
* <p>Typical field configuration objects look something like this:</p>
* <pre><code>
{
...
"fields":[
{
"name":"id", // a standard field
"mapping":"companyID",
"header":"ID",
"align":"right",
"sortable":false,
"menuDisabled":true
},{
"name":"company",
"id":"company", // id for the autoExpandColumn
"editor":{ // an editable field
"xtype":"textfield",
"allowBlank":false
},
"fixed":false,
"header":"Company",
"hidden":false,
"hideable":true,
"locked":false,
"mapping":"company",
"tooltip":"Click to sort"
},{
"name":"tax",
"ignoreColumn":true // a field in the data object, but not in the column model
},
* </code></pre>
* <p>Fields that utilize a renderer or column xtype for rendering:</p>
* <pre><code>
{
...
"fields":[
{
"name":"price",
"header":"Price",
"align":"right",
"renderer":"Ext.util.Format.usMoney"
},
{
"name":"lastChange",
"dateFormat":"Y-m-d H:i:s",
"editor":{
"xtype":"datefield",
"allowBlank":false,
"selectOnFocus":true
},
"fixed":false,
"header":"Last Change",
"hidden":false,
"hideable":true,
"locked":false,
"mapping":"lastChange",
"tooltip":"Click to sort",
"type":"date",
// instead of renderer use an xtype:
"xtype": "datecolumn",
"format": "M/d/Y" // config for datecolumn xtype
},
* </code></pre>
* <p>A field with ComboBox editor and renderer:</p>
* <pre><code>
{
...
"fields":[
{
"name":"industry",
"header":"Industry",
// add combobox rendering capability:
"xtype": 'combocolumn',
// combobox rendering needs an editor:
"editor":{
"xtype":"combo",
"name":"foo", // needed?
"editable":false,
"forceSelection":true,
"selectOnFocus":true,
"lazyRender":true,
"listClass":"x-combo-list-small",
"mode":"local",
"triggerAction":"all",
"typeAhead":false,
"valueNotFoundText":"Select Industry...",
// the store:
"store": {
"xtype": "arraystore",
"id": 0,
"fields": [
"myId",
"displayText"
],
"data": [
[ 1, "Food"],
[ 2, "Retail"],
[ 3, "Aerospace"],
[ 4, "Communications"],
[ 5, "Construction"],
[ 5, "Finance"]
]
},
"valueField": "myId", // as per store.fields configuration
"displayField": "displayText" // as per store.fields configuration
}
},
* </code></pre>
* <p>Examples of fields that invoke plugins:</p>
* <pre><code>
{
...
"fields":[
{
"ptype":"rownumberer", // must be a registered ptype
"header":"",
"autoDestroy":true // used to trigger destruction of plugin
},{
"ptype":"ux-rowexpander",
"header":"",
"autoDestroy":true,
// ability to execute arbitrary javascript:
"script":"(function (){return {tpl : new Ext.Template('&lt;p>&lt;b>Company:&lt;/b> {company}&lt;/p>&lt;br>','&lt;p>&lt;b>Summary:&lt;/b> {desc}&lt;/p>')}})()"
},{
"ptype":"ux-checkcolumn", // must be a registered ptype
"autoDestroy":true,
"name":"check",
"align":"center",
"fixed":false,
"header":"Check",
"hidden":false,
"hideable":true,
"locked":false,
"mapping":"check",
"sortable":true,
"tooltip":"Click to sort",
"width":50
},{
// experimental: changing the selection model
"selModel":{
"type":"CheckboxSelectionModel",
"cfg":{}
},
"autoDestroy":true
},
* </code></pre>
*/
Ext.ux.grid.MetaGrid = Ext.extend(Object, {

/**
* @cfg {Boolean} maskEmpty
* Defaults to <tt>true</tt> to mask the grid if there's no data to make it
* even more obvious that the grid is empty. This will apply a mask to the
* grid's body with a message in the middle if there are zero rows - quite
* hard for the user to miss.
*/
maskEmpty: true,

paging: {
perPage: 25
},
stripeRows: true,
/**
* @cfg {Boolean} trackMouseOver
* Defaults to <tt>true</tt>. See Ext.grid.GridPanel.
*/
trackMouseOver: true,

constructor: function (config) {
Ext.apply(this, config);
},

// @private
init: function(grid) {
this.grid = grid;
this.grid.on({
render: this.onRender,
destroy: this.onDestroy,
scope: this
});

grid.store.on({
// register to the store's metachange event
metachange: {
fn: this.onMetaChange,
scope: this
},
loadexception: {
fn: function(proxy, options, response, e){
//if (Ext.isFirebug) {
console.warn('store loadexception: ', arguments);
//}
//else {
// Ext.Msg.alert('store loadexception: ', arguments);
//}
}
},
scope: this
});

// mask the grid if there is no data if so configured
if (this.maskEmpty) {
grid.store.on(
'load', function() {
if (this.store.getTotalCount() == 0) {
var el = this.getGridEl();
if (typeof el == 'object'){
el.mask('No Data', 'x-mask');
}
}
},
grid // scope it to grid
);
}

if (grid.filters) {
grid.filters = new Ext.grid.GridFilters({
filters:[]
});
this.plugins.push(grid.filters);
this.paging.plugins = [];
this.pagingPlugins.push(grid.filters);
}
/*
//Create Paging Toolbar
this.pagingToolbar = new Ext.PagingToolbar({
store: grid.store,
pageSize: this.pageSize || 25, //default is 20
plugins: this.pagingPlugins,
displayInfo: true,//default is false (to not show displayMsg)
displayMsg: 'Displaying {0} - {1} of {2}',
emptyMsg: "No data to display",//display message when no records found
items: [{
text: 'Change data',
scope: this
}]
});

//Add a bottom bar
this.bbar = this.pagingToolbar;
*/
/*
* JSONReader provides metachange functionality which allows you to create
* dynamic records natively
* It does not allow you to create the grid's column model dynamically.
*/
if (grid.columns && (grid.columns instanceof Array)) {
grid.colModel = new Ext.grid.ColumnModel(grid.columns);
delete grid.columns;
}

// Create a empty colModel if none given
if (!grid.colModel) {
grid.colModel = new Ext.grid.ColumnModel([]);
}

},

// @private
onRender : function() {

var params = { //this is only parameters for the FIRST page load,
reconfigure: true,
start: 0, //pass start/limit parameters for paging
limit: this.paging.perPage
};

this.grid.store.load({params: params});
},

// @private
onDestroy: function() {

},

/**
* Configure the reader using the server supplied meta data.
* This grid is observing the store's metachange event (which will be triggered
* when the metaData property is detected in the returned json data object).
* This method is specified as the handler for the that metachange event.
* This method interrogates the metaData property of the json packet (passed
* to this method as the 2nd argument ).
* @param {Object} store
* @param {Object} meta The reader's meta property that exposes the JSON metadata
*/
onMetaChange: function(store, meta){

var cols = [], script;

Ext.each(meta.fields, function(col){

// do not add to column model if ignoreColumn == true
if (col.ignoreColumn) {
return;
}

// if not specified assign dataIndex to name
if (typeof col.dataIndex == "undefined" && col.name) {
col.dataIndex = col.name;
}

//if using gridFilters extension
if (this.grid.filters) {
if (col.filter !== undefined) {
if ((col.filter.type !== undefined)) {
col.filter.dataIndex = col.dataIndex;
this.filters.addFilter(col.filter);
}
}
delete col.filter;
}

// if renderer specified
if (typeof col.renderer == "string") {

// if specified Ext.util or a function will eval to get that function
if (col.renderer.indexOf("Ext") < 0 && col.renderer.indexOf("function") < 0) {
col.renderer = this.grid[col.renderer].createDelegate(this.grid);
}
else {
col.renderer = eval(col.renderer);
}
}

// if listeners specified in meta data
l = col.listeners;
if (typeof l == "object") {
for (var e in l) {
if (typeof e == "string") {
for (var c in l[e]) {
if (typeof c == "string") {
l[e][c] = eval(l[e][c]);
}
}
}
}
}

// if convert specified assume it's a function and eval it
if (col.convert) {
col.convert = eval(col.convert);
}

// column editor
if (col.editor) {
col.editor = Ext.create(col.editor, 'textfield');
}

// check if script property specified on the column
// use this property to pass back custom code wrapped inside a self evaluating function:
// (function () { <<do whatever>> })()
if (col.script){
script = eval(col.script);
Ext.apply(col, script);
}

// if plugin specified add it
if (col.ptype !== undefined) {
// requires overrides to Ext.Component and Ext.ComponentMgr
col = this.grid.addPlugin(col);
}

// add ability to change to CheckboxSelectionModel
if (col.selModel){
col = this.setSelectionModel(col.selModel);
}

// add column to colModel config array
cols.push(col);

}, this); // end of columns loop

var cm = new Ext.grid.ColumnModel({
columns: cols,
defaults: meta.cmDefaults || {},
listeners: meta.cmListeners || {}
});

// apply any passed grid configs (eg, autoExpandColumn, etc)
if (meta.gridConfig) {
Ext.apply(this.grid, meta.gridConfig);
// explicitly apply any viewConfigs to view
if (meta.gridConfig.viewConfig){
Ext.apply(this.grid.getView(), meta.gridConfig.viewConfig);
// will have limited success, scroller won't be changed, etc.
// if setting forceFit=true might need:
//this.grid.getView().scroller.setStyle('overflow-x', 'hidden');
}
}

// use meta.storeConfig to provide capability to change the store,
// perhaps change to a GroupingStore, etc.
var newStore;
if (meta.storeConfig) {
var storeConfig = Ext.apply({}, meta.storeConfig);
newStore = Ext.StoreMgr.lookup(storeConfig);

} else {
newStore = store;
}

// Reconfigure the grid to use a different Store and Column Model. The View
// will be bound to the new objects and refreshed.
this.grid.reconfigure(newStore, cm);

// update the store for the pagingtoolbar also
if(this.grid.pagingToolbar){
this.grid.pagingToolbar.bindStore(newStore);
}

if (this.grid.stateful) {
this.grid.initState();
}
},

// experimental, dynamically change selection model
setSelectionModel : function(sm){
delete this.grid.selModel;
this.grid.selModel = new Ext.grid[sm.type](sm.cfg);
sm = this.grid.selModel;
sm.grid = this.grid;

var view = this.grid.getView();
view.mainBody.on('mousedown', sm.onMouseDown, sm);
Ext.fly(view.innerHd).on('mousedown', sm.onHdMouseDown, sm);

return sm;
}
});
// register ptype
Ext.preg('ux-grid-metagrid', Ext.ux.grid.MetaGrid);


MetaGrid comes from (example):

http://extjs-ux.org/repo/authors/mjlecomte/trunk/Ext/ux/metagrid.html