See this forum post.
Grid is empty / Populating the grid problems:
Typically you'll specify at least one of the following:
grid = new Ext.grid.EditorGridPanel({ //other configs... //Don't forget to specify the height! //Three options: //(1) Specify height explicitly, height:350 //(2) Enable autoHeight, autoHeight: true //autoHeight resizes the height to show all records //(3) Put grid inside a container who's layout handles sizing, for //example, 'fit', 'border' or 'anchor' (with anchor configuration in //the Grid). TabPanels use 'fit' layout and so will automatically size //Grids to fit.
If you use a Grid as a child item of a Container, you MUST NOT explicitly render the Grid.
If you need to render a grid, use one of the following:
//add listener for grid's render event grid.on({ render:{ scope: this, fn: function() { //load store after the grid is done rendering grid.getStore().load(); } } })
Make sure your data record is an 'array' of items:
var SecurityItemRecord = Ext.data.Record.create([ //note array literal {name: 'type'}, {name: 'itemName'} ]);//OK var SecurityItemRecord = Ext.data.Record.create( //oops, not array {name: 'type'}, {name: 'itemName'} );//NO GOOD
Check the sample response packet from a JSONReader. You will want to set a root option (rows in example below) and then wrap your array in a JSON response. Something like:
{ 'results': 2, 'rows': [ { 'id': 1, 'lastname': 'Smith' }, // a row object { 'id': 2, 'lastname': 'Jones' } // another row object ] }
This error indicates that the root specified in the JsonStore is not present in the JSON data.
If so, did you post it into [www.jslint.com www.jslint.com ] to verify it’s in proper form? Is the response what you expected, shows total number of records, the root has an array, etc.?
Add listeners to see if the store loaded or if there were exceptions.
A few of the key listeners to watch for:
For a more general purpose query to see what events are firing and when on a particular observable object you might implement this utility devised by Aaron Conran:
function captureEvents(observable) { Ext.util.Observable.capture( observable, function(eventName) { console.info(eventName); }, this ); } Ext.onReady(function(){ var grid = new Ext.grid.GridPanel({ ... }); captureEvents(grid); });
myStore.load(); alert(myStore.getCount());//no way! won't work. the store was just //requested, it's not 'back' from the server yet. //add listener to store's load event before you execute store.load(): myStore.on({ 'load':{ fn: function(store, records, options){ //store is loaded, now you can work with it's records, etc. console.info('store load, arguments:', arguments); console.info('Store count = ', store.getCount()); }, scope:this }, 'loadexception':{ //consult the API for the proxy used for the actual arguments fn: function(obj, options, response, e){ console.info('store loadexception, arguments:', arguments); console.info('error = ', e); }, scope:this } });
//For data like this: {totalCount: 45, root: [{foo: {company: 'Company1'}}]} //specify store like this: var store = new Ext.data.Store( { url: 'url', reader: new Ext.data.JsonReader( { root: 'root', totalProperty: 'totalCount', id: 'id', fields: [{name: 'company', mapping: 'foo.company'}] } ) });
Xml has a huge performance penalty associated with it, and JSON is just plain simpler (and therefore much easier to manipulate).
<mime-mapping>
<extension>xml</extension>
<mime-type>text/xml</mime-type>
</mime-mapping>
myStore.load({ params: { //this is only parameters for the FIRST page load, //use baseParams in the store config to have //params applied to EVERY load request. foo: 'bar', start: 0, //pass start/limit parameters for paging limit: 10 }, //scope:myStore callback: function(rec, options, success){ //Note: This function is executed in the scope of myStore //Yoy may like to specify this explicitly by adding scope:myStore //the point here is to get a reference to the reader var reader = this.reader; //now that you have reference to reader, access the jsonData property var jsonData = reader.jsonData; console.dir(jsonData); } });
buttons: [{ text: 'Search', scope: this, handler: function() { store.on('beforeload', function() { store.baseParams = { vin: document.getElementById("vin").value, platenum: document.getElementById("platenum").value, lastname: document.getElementById("lastname").value }; }); store.load({ params:{ start:0, limit:25, someThing:'whatever' //example of extra params } }) } }]
To add parameters to a Grid's server requests add a listener for its Proxy's beforeload event:
var proxy = new Ext.data.HttpProxy ({ url: '/DoSearch.php' }); // Add the HTTP parameter searchTerm to the request proxy.on('beforeload', function(p, params) { params.searchTerm = searchValue; });
//HttpProxy: store.proxy.conn.url = 'myNewUrl.php'; //ScriptTagProxy: store.proxy.url = 'myNewUrl.php';
Ext.TaskMgr.start({ run: store.reload, scope: store, interval: interval });
//create a JSON object: { dataStore1: /*1st json string */, dataStore2: /*2nd json string */ } //decode the json packet... var json = Ext.decode(response.responseText); //load the stores: store1.loadData(json.dataStore1); store2.loadData(json.dataStore2);
//create a JSON object: { dataStore1: /*1st json string */, dataStore2: /*2nd json string */ } //decode the json packet... var json = Ext.decode(response.responseText); //create new proxy data for stores as hendricd mentioned: store1.proxy.data = json.dataStore1; store2.proxy.data = json.dataStore2; //load stores store1.load(); store2.load(); //where stores' proxy has to be defined like this: proxy: new Ext.ux.data.BufferedPagingMemoryProxy([]) //I guess this can be used in case someone is using PMP (paging memory proxy)
var store2 = new Ext.data.Store({ ... }); var store1 = new Ext.data.Store({ ... listeners: { load: function(store) { store2.addRecords({records: store.getRange()},{add: true}); } } });
For example, the first data col comes from Store 1 and the data from Store 2 forms cols 2 and 3. You can use a renderer that finds the data in the second store if the 'other' columns are just 'lookup' data, e.g.:
var store1 = new Ext.data.Store({ ..., fields: ['field1', 'field2'] }); var store2 = new Ext.data.Store({ ... id: 'field2', fields: ['field2', 'fieldA', 'fieldB'] }); var renderA = function(value) { var rec = store2.getById(value); return rec ? rec.get('fieldA') : ''; } var renderB = function(value) { var rec = store2.getById(value); return rec ? rec.get('fieldB') : ''; } var columns = [ {header: 'Field 1', dataIndex: 'field1'}, {header: 'Field A', dataIndex: 'field2', renderer: renderA}, {header: 'Field B', dataIndex: 'field2', renderer: renderB} ];
The store will get updated with an empty array, thus showing nothing. If you return null then the grid does not get updated (the store's data has not changed). So check the response in firebug to make sure the records is shown as [] instead of null.
//**server side: //initialize empty array so grid will always update even if //query returns null $records = array(); //query database for $records //update $records appropriately (do not send back null) //send data (including $records) back to client //**client side (javascript): //grid config: var grid = new Ext.grid.GridPanel({ .... viewConfig:{ .... emptyText:'No rows to display' }, .... });
dataStore.on('load', function() { var el = grid.getGridEl(); if (dataStore.getTotalCount() == 0 && typeof el == 'object') { el.mask('No Data', 'x-mask'); } });
//define a renderer to pass into the column model config later var myCellRenderer = function(value, metaData, record, rowIndex, colIndex, store) { //current value of cell = value //previous value of cell: var preVal = record.modified && (typeof record.modified[colModel.getDataIndex(colIndex)] != 'undefined') ? record.modified[colModel.getDataIndex(colIndex)] : value; //metaData.css : String : A CSS class name to add to the cell's TD element. //metaData.attr : String : An html attribute definition string to apply to // the data container element within the table // cell (e.g. 'style="color:red;"'). metaData.css = 'name-of-css-class-you-will-define'; return value; };
* or specify the renderer inline var companyColumn = { header: "Company", dataIndex: 'company', //renderer: myCellRenderer,//defined above or use inline as shown below renderer: function(value, metaData, record, rowIndex, colIndex, store) { //You provide the logic depending on your needs to add a CSS class //name of your own choosing to manipulate the cell depending upon //the data in the underlying Record object. if (value == 'whatever') { metaData.css = 'name-of-css-class-you-will-define'; } return value; }//end renderer }
//reference to the field that you need to store the previous value: var yourField = 'someFieldName'; //set the previousValue field with the value in yourField: rec.set('previousValue', rec.get(yourField)); //set the new value: rec.set(yourField, newValue); //an example of using decimal precision renderer: function(value, meta, record) { var config = { decimalSeparator: ' ', decimalPrecision: record.get('somefield'), groupingSeparator: ',', groupingSize: 3, currencySymbol: null }; return Ext.util.Format.formatNumber(v, config); }
var companyColumn = { header: "Company", dataIndex: 'company', //specify id for the column with to generate class of 'companyClass'. id: 'companyClass' }
This generates a class which you can then manipulate via css with:
//add this css *after* the core css is loaded .x-grid3-td-companyClass { cursor: text; //change the cursor to be a text cursor }
Example of adding an icon to the column header: //css: /*columns with id specified can be configured*/ /*modify the header row only*/ .x-grid3-hd-classCompany { background: transparent url(../../resources/images/icons/silk/building.png) no-repeat 3px 3px ! important; padding-left:20px; } /*modify the column*/ .x-grid3-td-classCompany { //something } //javascript: colModel: new Ext.grid.ColumnModel([ //instantiate ColumnModel { dataIndex: 'company', header:"Company", /** * We can optionally place an id on a column so we can later * reference the column specifically. This might be useful for * instance if we want to refer to the column later in our code * by 'id' or maybe we set a css style to highlight the column * (.x-grid3-col-classCompanyID). * Setting a background color for the column may conflict with * the dirty record indicator (red triangle) in an editor grid. * You may want to alter the z-index so the red triangle remains * on top (untested). */ id: 'classCompany'// use this to put the icon in the header // makes css class "x-grid3-td-classCompany" // w/o makes css class "x-grid3-td-{column number}" }, ... ])
// Create grid view var gridView = new Ext.grid.GridView({ //forceFit: true, getRowClass : function (row, index) { var cls = ''; var data = row.data; switch (data.company) { case 'Alcoa Inc' : cls = 'yellow-row'// highlight row yellow break; case '3m Co' : cls = 'green-row' break; case 'Altria Group Inc' : cls = 'red-row' break; }//end switch return cls; } }); //end gridView // if using grouping: var gridView = new Ext.grid.GroupingView({ getRowClass : function (record, index) { if(!record){ return ''; } if( record.data.Price<=1000000 ){ return 'yellowcls'; } }, groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})' }); // create the Grid var grid = new Ext.grid.GridPanel({ ... view: gridView, // added new grid view ... }); // to refresh the view grid.getView().refresh(); // or fire store.update //uses the following css: .red-row{ background-color: #F5C0C0 !important; } .yellow-row{ background-color: #FBF8BF !important; } .green-row{ background-color: #99CC99 !important; }
//register a listener for rowclick to add or remove a CSS //class from selected grid rows onrowclick : function (grid, rowIndex, e) { e.stopEvent(); // Stops the browser context menu from showing. var row = this.getView().getRow(rowIndex); var record = this.store.getAt(rowIndex); // use whatever business logic you want to inspect 'row' // here and then either: // 1. Set it's style or add a class name, or // 2. use addClass(), or // 3. use removeClass() }
var record = grid.getSelectionModel().getSelected(); var idx = grid.store.indexOf(record); var row = grid.getView().getRow(idx); var element = Ext.get(row); // Remove the selected row class element.removeClass('x-grid3-row-selected'); // Highlight the row for 3 seconds Ext.fly(row).highlight("ffff9c", { attr: "background-color", easing: 'easeOut', duration: 3 }); // Later on don't forget to add the class back // element.addClass('x-grid3-row-selected') // Don't do this right away or you won't see the highlight
var overdueColumn = { header: "Overdue", dataIndex: 'overdue', renderer: function(value, metaData, record, rowIndex, colIndex, store) { var row = grid.getView().getRow(rowIndex);//HTMLElement var element = Ext.get(row);//Ext.Element if (record.data.overdue) { element.addClass('someCssClass'); }else{ element.removeClass('someCssClass'); } return value; }//end renderer }
If you want to change how a cell displays you have to modify both the renderer and the editor, e.g.
var cm = new Ext.grid.ColumnModel([{ header: "Password", dataIndex: 'password', width: 100, editor: new Ext.form.TextField({ inputType: 'password', allowBlank: false }), renderer: function() { return '*****' } },{ ... }]);
.redcls td { color: whatever; }
.x-grid3-hd-row td, .x-grid3-row td, .x-grid3-summary-row td { font-size: 16px; }
/* Override standard grid styles (add colour to vertical grid lines) */
.x-grid3-col {
border-left: 1px solid #EEEEEE;
border-right: 1px solid #D2D2D2;
}
/* Also remove padding from table data (to compensate for added grid lines) */
.x-grid3-row td, .x-grid3-summary-row td {
padding-left: 0px;
padding-right: 0px;
}
/* Override standard grid styles (add colour to vertical grid lines) */
.vline-on .x-grid3-col {
border-left: 1px solid #EEEEEE;
border-right: 1px solid #D2D2D2;
}
/* Also remove padding from table data (to compensate for added grid lines) */
.vline-on .x-grid3-row td, .x-grid3-summary-row td {
padding-left: 0px !important;
padding-right: 0px !important;
}
#grid-id .x-grid3-...
.your-class .x-grid3-...
.highlight-grid-row table {background-color:#708491;}
.highlight-grid-row .x-grid3-cell-inner {color: #fff;}
.x-grid3-row-over .x-grid3-cell-inner {
font-weight: bold;
}
renderer: function(v, meta) {
meta.css = 'x-align-right';
return v;
}
css:
.x-align-right {text-align: right;}
{ //align the header differently from the data rows align : 'right' header : "Submitted", //CSS string must have the ";" at the end of the string css : "text-align : center;", dataIndex : "Submitted", renderer : Ext.util.Format.dateRenderer('m/d/Y') },{ header : "Request Amount", css : "text-align : right;", dataIndex : "ReqAmt", renderer : Ext.util.Format.usMoney }
*cls: 'foo'*, then
.foo .x-panel-mr { ...}
//Use 1 or 2 here as setting the scrollOffset //to 0 may cause a horizontal scrollbar to appear grid.getView().scrollOffset = 1;
viewConfig: { scrollOffset: 0//or 1, or 2 as mentioned above }
Imagine a scenario where there are columns 'Salutation' and 'Name' which needs to be at 50px width each, and a column 'notes' which should be sized to the grid's size.
Set a width for 'Salutation' and 'Name'. Give an "id" to the 'notes' column and use the autoExpandColumn config option of the GridPanel. The Array grid example in the examples/grid/array-grid.js does just this. Price, Change, % Change, and Last Updated are all fixed width. Company will stretch to fit the rest of the size available. Notice that it has an "id" NOT a dataIndex. The autoExpandColumn config only takes id's, not dataIndex's. Note you can use both an id and dataIndex in your columnModel (dataIndex required for EditorGridPanel) and pass the id of the column to the autoExpandColumn.
Try: viewConfig:{forceFit:true}
The goal is to have the contents of a cell automatically wrap into a fixed defined width according to the column definition instead of having to manually drag the column width to expand the column.
td.x-grid3-td-<'''your id'''> { overflow: hidden; } td.x-grid3-td-<'''your id'''> div.x-grid3-cell-inner { white-space: normal; }
If you want to apply this to the entire grid apply it to the basic td class of the grid.
<style type="text/css"> .x-grid3-cell-inner, .x-grid3-hd-inner { white-space:normal !important; } </style>
// will prevent a "dirty" flag to be displayed on the check box record.modified[this.dataIndex] = undefined;
grid.on('validateedit', function(e) { var myTargetRow = 6; if (e.row == myTargetRow) { e.cancel = true; e.record.data[e.field] = e.value; } })
grid.on('afteredit', afterEdit, this ); function afterEdit(val) { val.record.commit(); };
.x-grid-dirty-cell { background-image:none; }
.x-grid3-dirty-cell { background-image:none; }
Or:
<style type="text/css"> .x-grid3-td-[id-of-column] {background-image:none!important;} </style>
grid.on({ cellclick:{ fn: function(grid, rowIndex, columnIndex, e){ // Get the Record var record = grid.getStore().getAt(rowIndex); // Get field name var fieldName = grid.getColumnModel().getDataIndex(columnIndex); var data = record.get(fieldName); // get grid's <TD> HtmlElement at the specified coordinates var cell = grid.getView().getCell(rowIndex, columnIndex); // get Ext.Element object var el = Ext.get(cell); var classOld = 'x-grid3-dirty-cell'; // toggle the cssClass if (el.hasClass(classOld)) { el.removeClass(classOld); } else { el.addClass(classOld); } } } });
var grid = new Ext.grid.GridPanel({ enableHdMenu: false, //or could use: onContextMenu: Ext.emptyFn, //or could use: enableColumnHide: false,//disable column hide/show option from menu ... }); //or add the following to your gridpanel config: var grid = new Ext.grid.GridPanel({ viewConfig: { headersDisabled: true // disable grid headers } ... });
Ext.override(Ext.grid.GridView, { handleHdDown : function(e, t){ if(Ext.fly(t).hasClass('x-grid3-hd-btn')){ e.stopEvent(); var hd = this.findHeaderCell(t); Ext.fly(hd).addClass('x-grid3-hd-menu-open'); var index = this.getCellIndex(hd); this.hdCtxIndex = index; var ms = this.hmenu.items, cm = this.cm; ms.get("asc").setVisible(cm.isSortable(index)); ms.get("desc").setVisible(cm.isSortable(index)); this.hmenu.on("hide", function(){ Ext.fly(hd).removeClass('x-grid3-hd-menu-open'); }, this, {single:true}); this.hmenu.show(t, "tl-bl?"); } } });
This is not the case if you render to a <center>, so you have to fix it by adding the following to the grid config:
style: 'text-align:left;'
//returns an array of column config objects var columns = grid.getColumnModel().getColumnsBy(function(c){ return c.hidden; });
Ext.override(Ext.grid.ColumnModel, { /** * Returns an array of column config objects for the visibility given. * @param {Boolean} hidden True returns visible columns; False returns * hidden columns (defaults to True). * @param {String} cfg Specify the config property to return (defaults * to the config). * @return {Array} result */ getColumnsVisible : function(visibility, cfg){ var visible = (visibility === false) ? false : true; var r = []; for(var i = 0, len = this.config.length; i < len; i++){ var c = this.config''; var hidden = c.hidden ? true : false; if(hidden !== visible){ r[r.length] = c[cfg] || c; } } return r; } });
usage:
//get array of visible column config objects var visible1 = grid.getColumnModel().getColumnsVisible(); var visible2 = grid.getColumnModel().getColumnsVisible(true); //get array of hidden column config objects var hidden1 = grid.getColumnModel().getColumnsVisible(false); //get array of hidden column config objects (only return the //dataIndex property of each) var hidden2 = grid.getColumnModel().getColumnsVisible(false, 'dataIndex');
var store = this.getStore(); var dex = store.find('name',c.name); var row = this.getView().getRow(dex); //pass the element to Ext.fly() to create a temporary //Ext.Element. From there we have now exposed all of //the methods available from Ext.Fx. The thing to note //about Ext.fly is that it is a once-only thing so you //can not store the value of it safely. As a result //we change the fx method immediately: Ext.fly(row).highlight("ffff9c", { attr: "background-color", easing: 'easeOut', duration: .5 });
//option 1 //======== renderer = function (data, metadata, record, rowIndex, columnIndex, store) { //build the qtip: var title = 'Details for ' + value + '-' + record.get('month') + '-' + record.get('year'); var tip = record.get('sunday_events'); metadata.attr = 'ext:qtitle="' + title + '"' + ' ext:qtip="' + tip + '"'; //return the display text: var displayText = '<span style="color: #000;">' + value + '</span><br />' + record.get('sunday_events_short'); return displayText; }; //option 2 //======== renderer = function (data, metadata, record, rowIndex, columnIndex, store) { var qtip = '>'; if(data >= 0){ qtip = " qtip='yeah'/>"; return '<span style="color:green;"' + qtip + data + '%</span>'; }else if(data < 0){ qtip = " qtip='woops'/>"; return '<span style="color:red;"' + qtip + data + '%</span>'; } return data; }; //option 3 //======== var qtipTpl = new Ext.XTemplate( '<h3>Phones:</h3>', '<tpl for=".">', '<div><i>{phoneType}:</i> {phoneNumber}</div>', '</tpl>' ); renderer = function (data, metadata, record, rowIndex, columnIndex, store) { // get data var data = record.data; // convert phones to array (only once) data.phones = Ext.isArray(data.phones) ? data.phones : this.getPhones(data.phones); // create tooltip var qtip = qtipTpl.apply(data.phones); metadata.attr = 'ext:qtitle="' + title + '"' + ' ext:qtip="' + tip + '"'; //return the display text: return data; };
var row = GridPanel.getView().findRowIndex(targetElement); var col = GridPanel.getView().getCellIndex(targetElement);
See this thread
var yourColumn = { header: "Company", dataIndex: 'company', renderer: function(value, metaData, record, rowIndex, colIndex, store) { //The <a> tag is used to create an anchor to link from //the href attribute is used to address the document to link to //the words between the open and close of the anchor tag will //be displayed as a hyperlink (value). //the target attribute defines where the linked document will //be opened (_blank = open the document in a new browser window) return '<a href="http://www.yourURL.com/" target="_blank">' + value +'</a>'; }//end renderer }
function handleRowSelect(selectionModel, rowIndex, selectedRecord) { //assuming the record has a field named 'url' or build it as you need to var url = selectedRecord.get('url'); //if you want to open another window window.open(url); } grid.getSelectionModel().on('rowselect', handleRowSelect);
function(grid, rowIndex, columnIndex, e) { // Get the Record for the row var record = grid.getStore().getAt(rowIndex); // Get field name for the column var fieldName = grid.getColumnModel().getDataIndex(columnIndex); var data = record.get(fieldName); }
.x-grid3-row td.x-grid3-td-<id-of-column> { background-color: white; padding: 0; } .x-grid3-row td.x-grid3-td-<id-of-column> .x-grid3-cell-inner { border: 1px solid #a9bfd3; padding: 2px 3px 2px 5px; }
See this post
{header: "Controls", width: 60, sortable: false, renderer: function() { return '<div class="controlBtn"> <img src="../shared/icons/fam/cog_edit.png" width="16" height="16" class="control_edit"> <img src="../shared/icons/fam/folder_go.png" width="16" height="16" class="control_go"></div>'; }, dataIndex: 'company'}
grid.on('click', function(e) { var btn = e.getTarget('.controlBtn'); if (btn) { var t = e.getTarget(); var v = this.getView(); var rowIdx = v.findRowIndex(t); var record = this.getStore().getAt(rowIdx); var control = t.className.split('_')[1]; switch (control) { case 'edit': console.log('edit this record - ' + record.id); break; case 'go': console.log('go to this record - ' + record.id); break; } } }, grid);
<style> .controlBtn img { padding-left: 4px; cursor: pointer; } </style>
/** * RowAction plugin for Ext grid * * Contains renderer for an icon and fires events when icon is clicked * * @author Ing. Jozef Sakalos <jsakalos at aariadne dot com> * @date December 29, 2007 * @version $Id: Ext.ux.grid.RowAction.js 126 2008-01-31 03:33:50Z jozo $ * * @license Ext.ux.grid.RowAction is licensed under the terms of * the Open Source LGPL 3.0 license. Commercial use is permitted to the extent * that the code/component(s) do NOT become part of another Open Source * or Commercially licensed development library or toolkit without * explicit permission. * * License details: http://www.gnu.org/licenses/lgpl.html */ Ext.ns('Ext.ux.grid'); /** * @class Ext.ux.grid.RowAction * @extends Ext.util.Observable * * Creates new RowAction plugin * @constructor * @param {Object} config The config object * * @cfg {String} iconCls css class that defines background image */ Ext.ux.grid.RowAction = function(config) { Ext.apply(this, config); this.addEvents({ /** * @event beforeaction * Fires before action event. Return false to cancel the * subsequent action event. * @param {Ext.grid.GridPanel} grid * @param {Ext.data.Record} record Record corresponding to row clicked * @param {Integer} rowIndex */ beforeaction:true /** * @event action * Fires when icon is clicked * @param {Ext.grid.GridPanel} grid * @param {Ext.data.Record} record Record corresponding to row clicked * @param {Integer} rowIndex */ ,action:true }); Ext.ux.grid.RowAction.superclass.constructor.call(this); }; Ext.extend(Ext.ux.grid.RowAction, Ext.util.Observable, { header:'' ,sortable:false ,dataIndex:'' ,width:20 ,fixed:true ,lazyRender:true ,iconCls:'' // private - plugin initialization ,init:function(grid) { this.grid = grid; var view = grid.getView(); grid.on({ render:{scope:this, fn:function() { view.mainBody.on({ click:{scope:this, fn:this.onClick} }); }} }); if(!this.renderer) { this.renderer = function(value, cell, record, row, col, store) { cell.css += (cell.css ? ' ' : '') + 'ux-grid3-row-action-cell'; var retval = '<div class="' + this.getIconCls(record, row, col) + '"'; retval += this.style ? ' style="' + this.style + '"' : ''; retval += this.qtip ? ' ext:qtip="' + this.qtip +'"' : ''; retval += '> </div>'; return retval; }.createDelegate(this); } } // eo function init // override for custom processing ,getIconCls:function(record, row, col) { return this.boundIndex ? record.get(this.boundIndex) : this.iconCls; } // eo function getIconCls // private - icon click handler ,onClick:function(e, target) { var record, iconCls; var row = e.getTarget('.x-grid3-row'); var col = this.grid.getView().getCellIndex(e.getTarget( '.ux-grid3-row-action-cell' )); if(false !== row && false !== col) { record = this.grid.store.getAt(row.rowIndex); iconCls = this.getIconCls(record, row.rowIndex, col); if(Ext.fly(target).hasClass(iconCls)) { if(false !== this.fireEvent( 'beforeaction', this.grid, record, row.rowIndex )){ this.fireEvent('action', this.grid, record, row.rowIndex, e); } } } } // eo function onClick }); // eof
yourStore.commitChanges(); grid.getView().refresh();
tbar: [ { text: 'Delete Contacts', iconCls:'remove', handler : function(t){ //returns array of record objects for selected rows var selected = grid.getSelectionModel().getSelections(); var n = selections.length; if (n > 0) { //specify a refresh limit to control whether the entire //grid is updated or just update rows individually //it may be beneficial to update entire view if number //of selections are high var refreshLimit = 5; // you decide //this sequence only removes records from the client's store //you may want to update the server via ajax as well //so the following sequence would be executed inside //the success callback... //suspend the Grid's events and resume after tasks completed //(helps with performance, otherwise would be much slower) grid.suspendEvents(); //get the store associated with the grid: var store = grid.getStore(); //show message box to confirm delete? //loop through each record and delete for(var i=0; i < selected.length; i++){ //remove record from the cache store.remove(selected[i]);//record = selected[i] //refresh row if under limit if (n <= refreshLimit) { store.fireEvent("update", store, selected[i], Ext.data.Record.COMMIT); } }//end for //turn events back on now that the work has been done grid.resumeEvents(); //since events were disabled, gridView does not know that the //data has changed. We turned events back on, but we need to //manually fire the datachanged event to refresh entire grid if (n > 5) { store.fireEvent("datachanged", store); } } else { //show message box? return false; } }//end handler }//end Delete Contacts
Configure your GridView with the following options:
onLoad: Ext.emptyFn, listeners: { beforerefresh: function(v) { v.scrollTop = v.scroller.dom.scrollTop; v.scrollHeight = v.scroller.dom.scrollHeight; }, refresh: function(v) { v.scroller.dom.scrollTop = v.scrollTop + (v.scrollTop == 0 ? 0 : v.scroller.dom.scrollHeight - v.scrollHeight); } }
var sm = new Ext.grid.CellSelectionModel({ ... onEditorKey : function(field, e){ if(e.getKey() == e.TAB){ e.stopEvent(); return; } this.constructor.prototype.onEditorKey.apply(this, arguments); } }); var grid = new Ext.grid.EditorGridPanel({ sm: sm, ... });
Please see this post , otherwise Option 5 may be preferable if you're tabbing to advance the edited field.
var xg = Ext.grid.EditorGridPanel;//create shorthand reference var grid = new xg ({ ... isCellEditable: function(colIndex, rowIndex) { var field = this.getColumnModel().getDataIndex(colIndex); if (field == 'value') { var record = this.getStore().getAt(rowIndex); if (!record.get('enable_edit').getValue()) { //enable_edit is field in record return false;//return false to deny editing } } return xg.prototype.isCellEditable.call(this, colIndex, rowIndex); } });
var store = new Ext.data.Store({...}); var colModel = new Ext.grid.ColumnModel({ columns: [...], isCellEditable: function(col, row) { var record = store.getAt(row); if (record.get('readonly')) { // replace with your condition return false; } return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row); } }); var grid = new Ext.grid.GridPanel({ store: store, colModel: colModel, ... });
myColumnModel.isCellEditable = function(colIndex, rowIndex){ return !someBooleanStoredSomewhere; };
this.on('beforeedit', Comment.Methods.commentUpdateCheckPermission); commentUpdateCheckPermission : function (commentData) { if (cds.data.items[commentData.row].data.user_ID != Ext.util.Format.uppercase(VMOC.myUUID)) { Ext.MessageBox.alert('Permission denied', 'You do not have permission to edit this comment.'); cds.rejectChanges(); commentData.cancel = true; } }
var gcm = new Ext.grid.ColumnModel;//create shorthand reference var cm = new gcm({ columns: [leafClass, shownOnDrawing, drawingNo], isCellEditable: function(col, row) { var field = this.getDataIndex(col); var record = ds.getAt(row); if ((field == 'shownonDrawing') && (record.get('shownonDrawing') == 'Same As New Part')) { return false; } return gcm.prototype.isCellEditable.call(this, col, row); } });
listeners: { beforeedit: function(object) { if (object.column != 1) { return true; //always allow editing of all columns except 1 (2nd column) } if (object.record.get('fieldNameOfFirstColumn') == 1) { return true; //allow editing } else { return false; //do not allow editing } } }
//First change selModel to cancel selection with something like this: var foo = new Ext.grid.CheckboxSelectionModel({ ... listeners: { beforerowselect : function (sm, rowIndex, keep, rec) { var id = parseInt(rec.id); if (disabledRecords.contains(id)) return false; } } ... }); //"disabledRecords" can be e.g. Ext.util.MixedCollection or anything you want. //Next (optional) highlight disabled records (see docs for Ext.grid.GridView): var gridFrom = new Ext.grid.GridPanel({ ... view: new Ext.grid.GridView({ getRowClass: function (rec, idx, rowParams, store){ var id = parseInt(rec.id); if (disabledRecords.contains(id)) return "disabled-record"; } }) ... }); //"disabled-record" is simple CSS class (e.g. color: gray).
//1) add "if (this.readonly==false)" to the check column model onMouseDown event: Ext.grid.CheckColumn = function(config){ ..... onMouseDown : function(e, t){ if (this.readonly == false) { if (t.className && t.className.indexOf('x-grid3-cc-' + this.id) != -1) { e.stopEvent(); var index = this.grid.getView().findRowIndex(t); var record = this.grid.store.getAt(index); record.set(this.dataIndex, !record.data[this.dataIndex]); this.fireEvent('click', this, e, record) } } }, ........... }; //add the readonly property to the CheckColumn var exampleCheckColumn = new Ext.grid.CheckColumn({ header: "Survivor", dataIndex: 'iCheckColumnId', readonly: false, width: 55 }); //3) add disabling code to the datastore onload event so that if 'tiGridLocked' is true // set the check column to read only and add the beforeedit listener (disableComboGrid // function) to the combo boxes or any editable portion of the grid that is not a checkbox. datastore.on({ 'load': { fn: function(store, records, options){ if(store.reader.jsonData.tiGridLocked){ exampleCheckColumn.readonly = true; grid.addListener('beforeedit',disableComboGrid ); } }, scope: this } }); function disableComboGrid(){ return false; }
//Use the afteredit event of the grideditor Ext.getCmp('yourGridId').on('afteredit', function(e){ if (e.field == 'finishFrom') { //DateField to get Date() object in complete not parsed or formated. var dt = new Date(); e.record.set('finishDate', dt); e.record.set('finishTime', dt.format('H:i A')); } });
grid.getView().refreshRow(record);
//define a renderer to pass into the column model config later var myCellRenderer = function(value, metaData, record, rowIndex, colIndex, store) { //current value of cell = value //previous value of cell: var preVal = record.modified && (typeof record.modified[colModel.getDataIndex(colIndex)] != 'undefined') ? record.modified[colModel.getDataIndex(colIndex)] : value; //metaData.css : String : A CSS class name to add to the cell's TD element. //metaData.attr : String : An html attribute definition string to apply to // the data container element within the table // cell (e.g. 'style="color:red;"'). metaData.css = 'name-of-css-class-you-will-define'; return value; };
//reference to the field that you need to store the previous value: var yourField = 'someFieldName'; //set the previousValue field with the value in yourField: rec.set('previousValue', rec.get(yourField)); //set the new value: rec.set(yourField, newValue); //an example of using decimal precision renderer: function(value, meta, record) { var config = { decimalSeparator: ' ', decimalPrecision: record.get('somefield'), groupingSeparator: ',', groupingSize: 3, currencySymbol: null }; return Ext.util.Format.formatNumber(v, config); }
keys: { key: 'a', ctrl: true, stopEvent: true, handler: function() { grid.getSelectionModel().selectAll(); } }
grid.getStore().on('load', function() { expander.expandRow(0); }); //(and set deferRowRender:false in the grid config)
myColumnModel.moveColumn(oldIndex, newIndex);
var record = grid.getStore().getAt(rowIndex);
//specify the id when defining the reader. Example: var reader = new Ext.data.JsonReader({ root:'data', totalProperty: 'count', id: 'primaryKey', fields: [...] }); //where the returned object might be of the form: { images: [ {primaryKey: '1001', size:46, color: 'blue'}, {primaryKey: '1002', size:63, color: 'red'} ] } //You can then access the values by using getById(id) instead of getAt(index). Example: var record = store.getById('1001'); //get the size: var recSize = record.get('size');//46 //or var recColor = store.getById('1002').data.color;//red
//get selection Model var selectionModel = grid.getSelectionModel(); //get the selected record var record = selectionModel.getSelected(); //get the index of selected record var idx = grid.store.indexOf(record) ];
First, add an extra CSS rule:
<style type="text/css"> .x-selectable, .x-selectable * { -moz-user-select: text!important; -khtml-user-select: text!important; } </style>
Next, I use the new rule in the grid config:
var grid = new Ext.grid.GridPanel({ viewConfig: { templates: { cell: new Ext.Template( '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} x-selectable {css}" style="{style}" tabIndex="0" {cellAttr}>', '<div class="x-grid3-cell-inner x-grid3-col-{id}" {attr}>{value}</div>', '</td>' ) } }, ... });
if (!Ext.grid.GridView.prototype.templates) { Ext.grid.GridView.prototype.templates = {}; } Ext.grid.GridView.prototype.templates.cell = new Ext.Template( '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} x-selectable {css}" style="{style}" tabIndex="0" {cellAttr}>', '<div class="x-grid3-cell-inner x-grid3-col-{id}" {attr}>{value}</div>', '</td>' );
If you want the header to be selectable you'll also have to change the hcell template. For grouping you might need to put the template definition into the GroupingView component config instead of the viewConfig.
Sending modified data to the server is not built into the grid. You have to write it yourself, because there's so many different ways you might want to do it (commit every change to the server or do you want to save all changes together (e.g. using a Save button):
var data = []; Ext.each(grid.getStore().getModifiedRecords(), function(record){ data.push(record.data); }); Ext.Ajax.request({ url: '....', params: {data: Ext.encode(data)}, success: function(){ ... }, failure: function(){ ... } });
Ext.grid.AnimatedGridView = Ext.extend(Ext.grid.GridView, { initComponent: function(){ Ext.grid.AnimatedGridView.superclass.initComponent.apply(this, arguments); }, insertRows: function(dm, firstRow, lastRow, isUpdate){ Ext.grid.AnimatedGridView.superclass.insertRows.apply(this, arguments); var rowAdded = Ext.get(this.getRow(firstRow)); if (rowAdded) { rowAdded.slideIn(); } }, removeRow: function(rowIndex){ var rowToRemove = Ext.get(this.getRow(rowIndex)); var gridView = this; rowToRemove.slideOut('t', { remove: true }); } });
myGrid.getStore().insert(0, new myRecord( { id: some_id, ... }, some_id // same as id specified for data object above ) );
var store = new Ext.data.Store({ ... listeners: { datachanged: function(store){ store.addNewRecord(); }, update: function(store, record, type){ if(record == store.newRecord){ store.addNewRecord(); } }, remove: function(store, record, index){ if(record == store.newRecord){ store.addNewRecord(); } }, clear: function(store){ store.addNewRecord(); }, delay: 1 }, addNewRecord: function(){ this.newRecord = new this.recordType({ // default values for fields }); this.add(this.newRecord); } });
var grid = new Ext.grid.EditorGridPanel({ ... viewConfig: { getEditorParent: function() { return this.mainWrap.dom; } } });
sortInfo:{field: 'fieldname', direction: 'ASC'} //or call: store.setDefaultSort('fieldname', 'ASC');
//current sort +-+-------+ |1|First | |2|Last | |3|Second| +-+-------+ //after sort we want +-+-------+ |1|First | |3|Second| |2|Last |
sortType: function(value) { switch (value.toLowerCase()) { case 'first': return 1; case 'second': return 2; default: return 3; } }
// this solution handles sort via the column header context menus as well: store.sort = store.sort.createInterceptor(_new_storeSort); function _new_storeSort(fieldName, dir){ if (this.lastOptions.params) this.lastOptions.params.start = 0; return true; }
/* By specifying the start/limit params in ds.load * the values are passed here * if using ScriptTagProxy the values will be in $_GET * if using HttpProxy the values will be in $_POST (or $_REQUEST) * the following two lines check either location, but might be more * secure to use the appropriate one according to the Proxy being used */ $start = (integer) (isset($_POST['start']) ? $_POST['start'] : $_GET['start']); $end = (integer) (isset($_POST['limit']) ? $_POST['limit'] : $_GET['limit']); //check for the 'sort' and 'dir' in the POST array. //default to ASC if not set $sortDir = isset($_POST['dir']) ? $_POST['dir'] : 'ASC'; //default to company name if not set $sortBy = isset($_POST['sort']) ? $_POST['sort] : 'company'; $sql_count = 'SELECT * FROM ' . $table; $sql = $sql_count . ' ORDER BY ' . $sortBy. ' ' . $sortDir . ' LIMIT '; $sql .= $start . ', '. $end; $result_count = mysql_query($sql_count); $rows = mysql_num_rows($result_count);
// create the data store var store = new Ext.data.SimpleStore({ ... //any baseParams specified are posted with every load of the store baseParams:{ task: 'read', //action to complete module: 'admin' } });
myStore.on({ 'beforeload': { fn: function(store, options){ console.info('store beforeload fired, arguments:', arguments); options.params || (options.params = {}); //assert params Ext.apply(options.params, { //apply stuff to params //assuming pageNumber has been calculated into this var pageNo: this.pageNumber }); }, scope: this } });
store.paramNames = { start: 'offset', limit: 'max', sort: 'sort', dir: 'dir' } pagingToolbar.paramNames = { start: 'offset', limit: 'max' }
var myData = [ ['Apple',29.89,0.24,0.81,'9/1 12:00am'], //etc. ]; var inlineStore = new Ext.data.Store({ proxy: new Ext.data.PagingMemoryProxy(myData), reader: myReader }); //Paging toolbar within grid constructor: bbar: new Ext.PagingToolbar({ pageSize: 20, store: inlineStore, displayInfo: true })
store.add(newRecord); store.totalLength++; pagingToolbar.updateInfo();
//ext-all.css (line 14) //ext 2.2+ .x-tbar-loading { background-image:url(../images/default/grid/refresh.gif) !important; } //ext pre 2.2 .x-tbar-loading{background-image:url(../images/default/grid/done.gif)!important;}
/** * add listeners to the store * add listener for load, update, add, and remove events. * Each time an event is fired, copy the contents of the * store to a hidden form field. */ function CopyStoreToForm(store){ var json = ''; store.each(function(store){ json += Ext.util.JSON.encode(store.data) + ','; }); json = json.substring(0, json.length - 1); Ext.getDom('GridData').value = json }; /** * When the form is posted, you have the current state of * the store on the server */
//DO NOT RENDER. //NEVER RENDER. //add Panels to Containers. //This is the principle you must use. mainPanel.add(grid); mainPanel.setActiveTab(grid); /** *Don't use render(), applyToMarkup(), renderTo, applyTo or el in * a layout! Instead of rendering a component, add it to the * container and relayout, e.g. */ Ext.getCmp('centre-panel').add(grid); Ext.getCmp('centre-panel').doLayout();
//grid object var g = new Ext.grid.GridPanel({ title:'I will be the tab label'; }); var tabs2 = new Ext.TabPanel ({
... renderTo : document.body, activeTab : 0, width : 200,//a number ('100%' is not a number) height :150, frame :true, layoutOnTabChange : true,//do a layout of tab items as tabs are changed deferredRender :true, //whether or not each tab is rendered only when first accessed (defaults to true). defaults :{autoScroll: true}, items : g //the grid object
|
tabs.getEl().mask('Loading...', 'x-mask-loading'); ... tabs.getEl().unmask();
var reader = new Ext.data.ArrayReader({}, [ //combine two fields into one //the first field does not have obj. //any additional fields that are combined use a preceding obj. //sorting will be by the first field specified (first_name in this example) {name: 'full_name', type: 'string', mapping: 'first_name + " " + obj.last_name'}, {name: 'age'} ]); var grid = new Ext.grid.GridPanel({ store: new Ext.data.Store({ reader: reader, data: Ext.grid.dummyData }), columns: [ {header: 'Full Name', dataIndex: 'full_name'}, {header: 'Age', dataIndex:'age'} ] });
//sample json: {"purchases": { "Item":"Book", "classfication":[{ "class":"novel", "authors":[ {"name":"john Andrews"}, {"name":"mike Matthews"} ] }] } } reader: new Ext.data.JsonReader({ root: 'purchases', fields: [ {name: 'item', mapping: 'Item'}, {name: 'class', mapping: 'classfication.class'}, //combine authors into one column separated with comma { name: 'authors', mapping: 'classfication.authors', convert: function(v){ var r = []; for (var i = 0, len = v.length; i < len; i++) { r.push(v[i].name); } return r.join(', '); } } ] })
Ext.form.ComboBox({ ... valueField: undefined, ... });
... { name: "user_account", hidden: false, hideable: true, header: "User Account", editor: { xtype: "combo", typeAhead: true, triggerAction:"all", lazyRender: true, listClass: "x-combo-list-small", store:[ [ "0" , "Staff Account" ], //the value at index 0 is //assumed to be the combo value [ "1" , "Admin Account" ], //the value at index 1 is //assumed to be the combo text [ "2" , "Super Account" ] ] }, width:150 }
//listening for the grid's 'validateedit' event 'validateedit': function(e){ var rec = e.record; //looking into the store of the combo var store = rec.fields.items[e.column].editor.store; if(store && store instanceof Array && store[0] instanceof Array){ for(var opt = 0; opt < store.length; opt++){ var option = store[opt]; if(option[0] == e.value){ //setting the value to the 'textual' value of the selection //using rec.set(fieldName, newValue) to set it how you want rec.set(e.field, option[1]); //return false so that the EditorGridPanel thinks it was //an invalid edit and does not do the change itself return false; } } } }
So, the value of the Combo will be "0", "1" or "2" and that is what will be displayed in the Grid. Two options:
footerCfg: {cls: 'whatevere', html: 'Whatever'}
footer: true
var checkBox_column_name = new Ext.grid.CheckColumn({ header:'My header', dataIndex:'my_bool_colum', width:55 });
var colModel = new Ext.grid.ColumnModel([ <your_other_columns_here>, checkBox_column_name ]);
var grid = new Ext.grid.EditorGridPanel({ <other_grid_configs>, cm = colModel, plugins:[<any_other_plugins>, checkBox_column_name], <more_grid_configs> });
Ext.grid.CheckColumn = function(config){ Ext.apply(this, config); if(!this.id){ this.id = Ext.id(); } this.renderer = this.renderer.createDelegate(this); }; Ext.grid.CheckColumn.prototype ={ init : function(grid){ this.grid = grid; this.grid.on('render', function(){ var view = this.grid.getView(); view.mainBody.on('mousedown', this.onMouseDown, this); }, this); }, onMouseDown : function(e, t){ if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){ e.stopEvent(); var index = this.grid.getView().findRowIndex(t); var record = this.grid.store.getAt(index); record.set(this.dataIndex, !record.data[this.dataIndex]); } }, renderer : function(v, p, record){ p.css += ' x-grid3-check-col-td'; return '<div class="x-grid3-check-col' + (v?'-on':'') + ' x-grid3-cc-' + this.id + '"> </div>'; } };
bridgeSummGrid.addListener ( 'keydown', function(evnt) { var keyPressed = evnt.getKey(); if (evnt.ctrlKey) { /* * After trial and error, the ctrl+c combination seems to be code 67 */ if (keyPressed == 67) { var str = ""; /* * Get a list of all the visible columns */ var col; var visibleCols = bridgeSummGrid.getColumnModel().getColumnsBy( function (columnConfig, index) { if (columnConfig.hidden) return false; else return true; } ); var rec = 0 ; var selRecords = bridgeSummGrid.getSelectionModel().getSelections(); for ( rec = 0; rec < selRecords.length; rec++) { for (col = 0; col< visibleCols.length; col++) { var colIdxName = ""; colIdxName += visibleCols[col].dataIndex; /* Excel needs a tab in between columns */ str += selRecords[rec].get(visibleCols[col].dataIndex) + "\t"; } str = str + "\n"; } copy(str); } } } );
var record = editorGridPanel.getStore().getAt(row); record.set('fieldname', newValue);
grid.on("mouseover", function(e, t) { var row; if((row = this.findRowIndex(t)) !== false){ // if row above does not work try: // if((row = this.getView().findRowIndex(t)) !== false){ //getView is added var record = this.store.getAt(row); var id = record.get('Id'); } }, grid);
viewConfig: { forceFit: true }
autoHeight: false
Note: If really don't want the grid to have a fixed height then you'll have to manually set the grid height based on the number of displayed rows.
grid.getStore().on({ beforeload: function() { this.prevScrollState = this.getScrollState(); this.prevScrollState.top = 0; }, load: function() { this.restoreScroll(this.prevScrollState); delete this.prevScrollState; }, scope: grid.getView() });
/** * The onLoad() function of the grid calls scrollToTop(). * By eliminating scrollToTop(), the grid no longer moves when it is reloaded. * The only drawback is that you can no longer call scrollToTop() on the grid. */ onLoad: Ext.emptyFn, listeners: { beforerefresh: function(v) { v.scrollTop = v.scroller.dom.scrollTop; v.scrollHeight = v.scroller.dom.scrollHeight; }, refresh: function(v) { //v.scroller.dom.scrollTop = v.scrollTop; v.scroller.dom.scrollTop = v.scrollTop + (v.scrollTop == 0 ? 0 : v.scroller.dom.scrollHeight - v.scrollHeight); } }
Ext.override(Ext.grid.GridView, { //custom method to restore scroll to the top funcationlity //just call the scrollTop() method. scrollTop : function() { this.scroller.dom.scrollTop = 0; this.scroller.dom.scrollLeft = 0; }, scrollToTop : Ext.emptyFn });
grid.getView().scroller.scrollTo('top', 0); //Using this approach means you can animate the scroll, if you choose: grid.getView().scroller.scrollTo('top', 0, true); //even more animation: grid.getView().scroller.scrollTo('top', 0, { easing: 'bounceOut' });
.ext-gecko .x-grid-editor { position: relative !important; float: left; }
or try:
Ext.override(Ext.grid.GridView, { getEditorParent: function(ed) { return this.mainWrap.dom; } });
cId = selModel.getSelections()[0].data.id; grid.getView().focusRow(ds.indexOfId(cId));
grid.getView().focusRow(grid.getStore().getCount() - 1);
tabs.getEl().mask('Loading...', 'x-mask-loading'); ... tabs.getEl().unmask();