Sencha Inc. | HTML5 Apps

Grid FAQ

Published Sep 17, 2010 | The Sencha Dev Team | FAQ | Medium
Last Updated Jul 29, 2011

This FAQ is most relevant to Ext JS, 2.x, 3.x.

This article is currently due for review
We suspect that this article may be out-of-date or contain incorrect information.
But we are constantly editing articles, so we promise we will get to this one soon.

If you are looking for an introduction to the Ext JS grid component, you might find our comprehensive guide very useful.

This article covers troubleshooting grids, and some of the solutions to common grid issues. These issues might include things like:

  • Grid doesn’t show data
  • Store didn’t load
  • Grid is empty
  • Grid didn’t render
  • Grid not listening to store

Did you define the grid height?

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.

Did you render the grid?

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:

  • use 'renderTo' or 'el' in the grid config to render automatically or
  • defer rendering and explicitly render the grid using grid.render()

Does the element you rendered to exist?

  • Make sure the div you specified to render to exists (it's in the html)
  • Make sure document is ready (code executes after onReady() )
  • If putting the grid in a TabPanel try adding layoutOnTabChange: true to the tabPanel config.

Did you load the store?

//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();
      }
   }
})

Data Record is an Array

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

JSON

  • is the JSON properly formed (paste the JSON response into the checker at www.jslint.com to verify)
  • if the data loads ok in Firefox but not IE, make sure you check the response doesn't have trailing commas.
  • make sure it is NOT a string (JSON = object)
  • Do not use periods in field names for JSON ("my.success"="ext will not decode")
  • make sure the structure of the JSON coincides with what your reader is expecting
    • Does the root node match?
    • Is the root node an array of records?
    • Do the fields in the record coincide exactly with that in the data packet (if reader is looking for "someProperty", your data packet better NOT have "someproperty").
  • Does the reader correctly specify the correct root for the JSON being used?

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
    ]
}
  • To debug the response going into JsonReader
    • In firebug, go to ext-all-debug.js
    • find "getJsonAccessor: function(){"
    • Scroll up a bit to read, set a breakpoint there, and reload the page

Root has no properties

This error indicates that the root specified in the JsonStore is not present in the JSON data.

Try to track down problems using firebug:

  • Were there any errors reported by firebug?
  • Was a request sent out (Check CONSOLE for requests that were POSTED).
  • Was a response received for the request?

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.?

Did the store load?

Add listeners to see if the store loaded or if there were exceptions.

A few of the key listeners to watch for:

Listeners

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);
});

Store related issues

Listeners

Store appears empty even after calling load()

  • Store.load() is 'asynchronous' when using remote data (through an HttpProxy, or a ScriptTagProxy).
  • It returns 'immediately' after the request is sent to the server and continues to process code without waiting. To control postprocessing of returned data, configure the callback for the load call, or add load and loadexception listeners to the store.
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
    }
});

nested JSON

  • Nested JSON example (assumes all nested properties are defined, if you may have undefined/null nested properties see this thread).
//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 Issues

  • Many people will recommend you use JSON.

Xml has a huge performance penalty associated with it, and JSON is just plain simpler (and therefore much easier to manipulate).

  • If there is a specific requirement for xml, check if your server setting the right content-type for the xml (Response.ContentType = "text/xml"). For example have the server return the correct mime type for the XML document:
<mime-mapping>
        <extension>xml</extension>
        <mime-type>text/xml</mime-type>
    </mime-mapping>

How to get additional data sent back with store load?

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);
    }
});

Changing the baseParams

  • changing the baseParams of the store before loading it in a beforeload event
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
         }
      })
   }
}]

Reload a Grid from a different URL?

  • Usually, this question means that different parameters need to be passed to the same server script. The answer is "Do not embed parameters in the DataProxy's URL"

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;
});
  • If you must change the url:
//HttpProxy:
store.proxy.conn.url = 'myNewUrl.php';
//ScriptTagProxy:
store.proxy.url = 'myNewUrl.php';

Automatically reload store / grid

Ext.TaskMgr.start({
  run: store.reload,
  scope: store,
  interval: interval
});

Load multiple stores with one AJAX request?

  • There is also an example using XML here
  • display data to multiple data stores from a single json string that a returned as part of a single http request.
//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);
    • Option 2
//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)

Load data to a grid from two different server calls/stores

  • Adding records from Store 2 to Store 2.
var store2 = new Ext.data.Store({
  ...
});
var store1 = new Ext.data.Store({
  ...
  listeners: {
    load: function(store) {
      store2.addRecords({records: store.getRange()},{add: true});
    }
  }
});
  • Using records from Store 2 with Store 1.

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}
];

Grid still shows records even when it should be empty

  • Check that you are returning an empty array [] and not null in the response from your server.

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'
    },
    ....
});
  • You can assign grid.getView().emptyText = 'some text' dynamically before re/loading the store.
  • To make it even more obvious that the grid is empty, add a mask on the grid with a message in the middle if there are zero rows - quite hard for the user to miss:
dataStore.on('load', function() {
	var el = grid.getGridEl();
	if (dataStore.getTotalCount() == 0 && typeof el == 'object') {
		el.mask('No Data', 'x-mask');
	}
});

Appearance

Grid Doesn’t show images correctly (checkboxes, etc.)
  • Are CSS files attached correctly? (Check Firebug, XHR for any red links)
  • The css references may be incorrect. Check the relative paths. (use ./ instead of /)
To modify a cell/row/column
//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
}
  • If you need to store the previous value temporarily to use in a renderer after you have committed changes, you will need to create an extra field that holds the previous value because the internal record modified field value is cleared when you commit:
//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);
}
To modify a column (or just the header of a column)
  • specify an id in the column model for each field that you would like to modify. A CSS class will be generated in the form x-grid3-td-{COLUMNID}. For related threads with examples see: 1, 2.
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}"
    },
    ...
])
How To modify the styling of a row
  • use the Grid's GridView to override the gridGridViewgetRowClass and return a css class name string. (Note: to get this working with the Ext.grid.RowExpander plugin, put the getRowClass function in the RowExpander config options instead of GridView or GroupingView.)
// 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; }
  • If you want to do this dynamically (example: change row color when a click event on a cell is raised), utilize the GridView to retrieve a reference to the row using getRow().
//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()
    }
  • If you want an Fx highlight for a selected row do the following:
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
  • Another example where the row appearance is changed dynamically if one of the cell's values change by using a renderer. In this case if the record's overdue field is true then a class is applied to the row.
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
}
  • Password fields. An EditorGridPanel uses a 'renderer' to display a cell value and an 'editor' to edit the cell value.

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 '*****'
	}
},{
	...
}]);
To modify text
  • To change the internal cell text color to whatever.
.redcls td {
    color: whatever;
}
  • To change the font.
.x-grid3-hd-row td, .x-grid3-row td, .x-grid3-summary-row td {
    font-size: 16px;
}
Adding border lines to cells in grid
  • See this thread
  • To affect all grids try overriding the css to remove the left/right padding from the table data grid cells.
/* 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;
}
  • TO affect some grids specify cls : 'vline-on' in the grid config and specify the css as:
/* 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;
}
Styling a Grid
  • Give the grid an id and base classes on that id. E.g.:
#grid-id .x-grid3-...
  • Add an additional class to GridPanel:
.your-class .x-grid3-...
  • Changing font color:
.highlight-grid-row table {background-color:#708491;}
.highlight-grid-row .x-grid3-cell-inner {color: #fff;}
  • Font change on row mouseover:
.x-grid3-row-over .x-grid3-cell-inner {
    font-weight: bold;
}
  • Font alignment:
renderer: function(v, meta) {
  meta.css = 'x-align-right';
  return v;
}
css:
.x-align-right {text-align: right;}
  • Style header different from rows, etc. see this feature request thread
  • A pitfall of some suggestions is that it affects all instances of grids on the page. Another approach is presented here. Basically, each Column element has a "css" property which can be set on a column by column basis. The example below shows a "Submitted" column to have data rows centered and the "Request Amount" column to have data rows to be right aligned.
{
    //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
}
  • To change the styling of the edges of a panel:
*cls: 'foo'*, then
.foo .x-panel-mr { ...}
Getting rid of empty column/reserved space for scrollbar on right side of grid
  • A blank space is reserved to the right of grids in case there is so much data that a vertical scrollbar is needed. gridView.scrollOffset
  • if you have a fixed number of items in your grid, and you know you won't need scrollbars, you could do something like:
//Use 1 or 2 here as setting the scrollOffset
//to 0 may cause a horizontal scrollbar to appear
grid.getView().scrollOffset = 1;
  • If the window is resizable or the data is dynamic the above may not work. Try checking and resizing the columns (if needed) whenever the grid is resized or when the data is changed.
  • If you are sure that a vertical scrollbar will never be needed you can add:
viewConfig: {
scrollOffset: 0//or 1, or 2 as mentioned above
}
"Auto size" columns to the largest element in them
Mixing fixed width and variable width columns

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.

  • Solution 1:

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.

  • Solution 2:

Try: viewConfig:{forceFit:true}

How to wrap contents within a cell of a fixed width grid column?

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.

  • Give the column you want to wrap an id
  • Then define CSS classes like this:
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.

  • Or try:
<style type="text/css">
 .x-grid3-cell-inner, .x-grid3-hd-inner { white-space:normal !important; }
</style>
Dirty Record / Red Flag (modifying, etc.)
  • To disable "dirty cell mark" for a record field. Use the following line when you modify a cell you do not wish to have a "dirty" marker. This will get the record to think that this cell has not been modified without having to commit the entire record.
// will prevent a "dirty" flag to be displayed on the check box 
record.modified[this.dataIndex] = undefined;
  • To remove the red triangle only from some records (not all), hook into the grid's validateedit event and cancel it if the edit occurred on a targeted row, then set the field's new value in the Record directly:
grid.on('validateedit', function(e) {
  var myTargetRow = 6;
 
  if (e.row == myTargetRow) {
    e.cancel = true;
    e.record.data[e.field] = e.value;
  }
})
  • Change the cell style back to the one without the little red corner and update the data store with the new value (Does not execute an XHR to send/commit data to the server.)
grid.on('afteredit', afterEdit, this );
function afterEdit(val) {
    val.record.commit();
};
  • Change the CSS to never show the dirty flag:
.x-grid-dirty-cell {
     background-image:none;
}
  • Note: For EXT 3.0+
.x-grid3-dirty-cell {
     background-image:none;
}

Or:

<style type="text/css">
  .x-grid3-td-[id-of-column] {background-image:none!important;}
</style>
  • To retain an editor grids edited value if validation fails see this thread.
  • More on validation here
  • To mark a cell invalid check here or use:
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);
                }
            }
        }
    });
Hide header context menu entirely
  • To disable the popup menu that shows (Sort Ascending/Descending, Lock/Unlock Column):
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
    }
    ...
});
Hide header context menu sort options
  • Courtesy of Condor:
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?");
		}
	}
});
Hide a column...or row
  • To hide a column:
    • use: myGrid.getColumnModel().setHidden(0, true);
    • Navigate to: array-grid demo
    • In Firebug's console type: Ext.getCmp('ext-comp-1001').getColumnModel().setHidden(0, true)
    • Ext.getCmp('ext-comp-1001') returns the GridPanel object.
    • getColumnModel() gets it's column model.
    • setHidden(0, true) hides column 0 (0 = the first column).
  • To hide a row:
    • Apply a custom filter to the Store using filter or filterby. Introduce a field in the record (for example record.data.visible) and filterby that field. If you want, you can get more aggressive and use store.remove() to actually remove that record from the store instead of just hiding it.
Grid in centered div
  • ExtJS makes some assumptions about the styling of element components are rendered to. One of the assumptions is that the text-align is set to left.

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;'
How to retrieve all hidden columns
  • simple way:
//returns an array of column config objects
var columns = grid.getColumnModel().getColumnsBy(function(c){
  return c.hidden;
});
  • reusable way:
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');
  • Note: if the user moves columns around the resulting array reflects the current displayed order.
  • To save user preferences see this thread.
FX
  • Highlight rows/columns on mouseover see this thread
  • Highlight a row
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
});
Add ToolTip or Qtip
//option 1
//========
renderer = function (data, metadata, record, rowIndex, columnIndex, store) {
	//build the qtip:
    var title = 'Details for&nbsp;' + 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);
Decimal Precision

Behavior

Selecting a row in grid once rendered

See this thread

Links inside grid cells

  • Option 1. Define a custom renderer
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);

Add click handlers to specific grid cells

  • In addition to suggestions listed below, also consider related extensions rowactions and cellactions provided by Saki at http://www.extjs.eu
  • If you are using editor grid, the default selection model is the cell selection model. With that you could listen to beforecellselect or cellselect. The listener is passed the row and column indexed clicked on. The column index may be used to determine which field was clicked on (Note that columns may be reordered by the user so using the column index as the field index is unsafe...may want to use an id).
  • Add a listener for the Grid's the cellclick event.
The listener is passed the row and column indexed clicked on. The column index may be used to determine which field was clicked on (Columns may be reordered by the user so using the column index as the field index is unsafe)
A cellclick event handler must find the relevant information like this:
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);
    }
  • An example using cellclick event to change cell color.
  • Change the CSS so all cells look like an editor is active, e.g.
.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;
}

How to add or remove a grid column

See this post

How to add a column with tools for each record?

  • Approach 1
    • Use the array grid example in your packaged download array-grid.js
    • Add an extra column to the column model definition and a custom renderer.
{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'}
    • Then you would setup an event handler on the click event.
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);
    • Add the following CSS rule in array-grid.html to give some padding and change the cursor.
<style>
   .controlBtn img {
      padding-left: 4px;
      cursor: pointer;
   }
</style>
    • Using this same method you could add as many tools as you would like in the controls section and always give them the css class "controls_{toolname}". Ideally you would break this out into an XTemplate so that you could simply pass in whatever tools you would like to use and output them on the grid as well.
  • Approach 2 utilizes a plugin as discussed here  :
/**
 * 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

Deleting Records/Rows

  • Option 1: Remove records from the backend using Ext.Ajax.request sending a delete request. In the success handler of that request tell the store to reload.
  • Option 2: Same as Option 1, except in the success handler do
yourStore.commitChanges(); 
grid.getView().refresh();
  • Option 3: Illustrates how you would either:
    • regenerate the entire grid using the store's datachanged event (presuming you have several rows selected) or,
    • refresh specific rows (not the entire grid) using the store's update event which may be more efficient for a few rows (update event makes the GridView update each row separately). GridView monitors this event.
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

Make grid resize with the window

  • Place the Grid in a "Viewport container" with the configuration of layout:'fit'
  • Try using forceFit: true in your viewConfig
  • Catch the viewport afterLayout event and call the doLayout method on each panel / gridPanels of interest.
  • For more on layout issues see this thread.

Resize the column width when double click header

Maintain GridPanel scroll position across Store reloads

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);
    }
}

Disabling column move for particular column

Disable Tab key from activating editor

  • You could override the onEditorKey method of the selection model, e.g.
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,
    ...
});

Disable editing of particular rows, columns, etc

Please see this post, otherwise Option 5 may be preferable if you're tabbing to advance the edited field.

  • Use beforeedit and return false to stop the edit
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);
  }
});
  • Option/Example 2
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,
  ...
});
  • Option/Example 3
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;
        }
    }
  • Option/Example 5 (disable particular column)
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);
   }
});
  • Option/Example 6
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
    }
  }
}
  • Option/Example 7
//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).
  • I have an editor grid with Checkbox columns as well as combo box columns. I would like to have this grid be editable up to a specific time and then when that time has passed it will no longer be editable. Users will be able to see their selections just not make any changes after the deadline has passed. I can disable the combo box column selections by adding the beforeedit listener to the data store, but this doesn't disable the checkbox columns.
//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;
}

Change one cell and update others in same row

  • change other cells in same row after editing a cell. For example to set current date and time in two cells in the same row. Fields are "finishDate" and "finishTime":
//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'));
    }
});

Manually refresh a row

  • You can refresh a row manually by calling:
grid.getView().refreshRow(record);

Previous or Old value in editor grid cell

  • How to get the previous value after editing a grid cell
//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;
};
  • If you need to store the previous value temporarily after you have committed changes, you will need to create an extra field that holds the previous value because the internal record modified field value is cleared when you commit:
//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);
}

Key Maps

  • Ctrl-A to select all rows:
keys:
{
     key: 'a',
     ctrl: true,
     stopEvent: true,
     handler: function() {
        grid.getSelectionModel().selectAll();
     }
}

Expand Certain Rows

  • Expand the first row when the store is loaded:
grid.getStore().on('load', function() {
  expander.expandRow(0);
});
//(and set deferRowRender:false in the grid config)

Moving grid columns

myColumnModel.moveColumn(oldIndex, newIndex);

How to drag/drop/reorder rows?

Get a record from the grid by row index

  • by row index:
var record = grid.getStore().getAt(rowIndex);
  • by id of the record object: use store.getById(some_id) where some_id is the id of the Record to find (autogenerated unless you specify it).
//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 the index of a selection.

//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) ];

Save before closing

How to select text in the grid (with the mouse) so that it can be copied to the clipboard

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>'
         )
      }
   },
   ...
});
  • or, if you want this as default behaviour, you could use:
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.

Saving

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(){
        ...
    }
});

Animating

  • Animating when rows are added/removed.
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
        });
    }
});

Adding a new Record

Specify the id

  • To add a new Record you may want to specify the id that is assigned to the new Record, rather than the id being autogenerated for you. An Ext.data.Record constructor has 2 parameters: the data object and the id, so you will use:
myGrid.getStore().insert(0,
    new myRecord(
        {
            id: some_id,
            ...
        },
        some_id // same as id specified for data object above
    )
);

Adding a new row to editorgrid by tabbing on last row/column

Using listeners

  • A few ways to use listeners to add a record to the grid:
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);
    }
});

Last row of grid not editable

  • If the editor doesn't exactly fit the cell and causes a vertical scroll the editor is cancelled by the scroll, try this to fix the problem:
var grid = new Ext.grid.EditorGridPanel({
  ...
  viewConfig: {
    getEditorParent: function() {
      return this.mainWrap.dom;
    }
  }
});

Sorting issues

sortInfo:{field: 'fieldname', direction: 'ASC'} 
//or call:
store.setDefaultSort('fieldname', 'ASC');
  • Also check if the sort type was set.
  • If it’s only sorting the current page and you want to sort on entire database query then remoteSort should be set to true (remoteSort defaults to local sorting)
  • Check that data Index (the property of ColumnModel which keys to a field name in the Record definition) is Camelcase.
  • Custom Sort:
Column Data:
//current sort
+-+-------+
|1|First |
|2|Last |
|3|Second|
+-+-------+
 
//after sort we want
+-+-------+
|1|First |
|3|Second|
|2|Last |
In record declaration use native toLowerCase():
sortType: function(value)
{
   switch (value.toLowerCase())
   {
      case 'first': return 1;
      case 'second': return 2;
      default: return 3;
   }
}
  • When sorting columns by clicking column header on a grid with a pagingtoolbar, if you're on page 2 and click on column header to sort, you end up on page 2 with the new sort. If you prefer to go to page 1 with new sort criteria the idea is to reset paging start param:
// 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;
}

Paging, Paging toolbar, Total record count

Basics

  • Read the API Docs
  • When you use paging, make sure the grid and the pagingtoolbar are both configured to use the SAME store.
  • Make sure you configure the store and reader objects accordingly, especially the reader's totalProperty (Json reader) / totalRecords (XML reader) property.
  • Make sure the data that you return from the server is commensurate with the amount of data expected by the js otherwise you'll have strange behavior. For example, if you're trying to limit the page size to 5, you should set the pageSize to 5, specify limit:5 with the initial store load (see next item). On the server side, make sure you then only send back 5 items, because if you return a different number of records the paging will act strange since all the data returned will still get loaded into the grid regardless of what pageSize/limit you specified.
  • Check using Firebug what the client is sending to the server in the console tab POST tab. Retrieve that information server side to generate the necessary sql to return the appropriate data. Something to the effect of:
/* 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);

Sending additional data

  • If you are sending additional information to the server you may want to also use baseParams so the additional data is sent with all subsequent page requests.
// 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' 
        }
    });

Altering Params

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
    }
});

Customize Paging parameter names

  • To customize the paging parameter names you have to override Store.paramNames, use:
store.paramNames = {
  start: 'offset',
  limit: 'max',
  sort: 'sort',
  dir: 'dir'
}
pagingToolbar.paramNames = {
  start: 'offset',
  limit: 'max'
}

Paging with local data (Paging Memory Proxy, or Paging Store)

  • Paging Store
  • If you want to page with inline data (local data) try the following using the examples included at examples/locale/PagingMemoryProxy.js:
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
})

PagingToolbar does not update total

  • When adding a record to the binding store the grid will show the new data correctly, but the pagingtoolbar won´t update totalcount etc. Adding a record also doesn't update the totalLength of the store. Use:
store.add(newRecord);
store.totalLength++;
pagingToolbar.updateInfo();

Add Paging Toolbar Dynamically

  • To add a paging toolbar dynamically use: gridpanel.getTopToolbar().add() or gridpanel.getBottomToolbar().add()

Change Paging Toolbar Store

  • To change the store a paging toolbar is bound to dynamically use: PagingToolbar. bind(Ext.data.Store) to bind the toolbar to the new datastore after doing a reconfigure.

Paging Toolbar Loading Indicator

  • The loading-indicator was also refresh button up til Ext 2.2. Ext 2.2+ uses a button with arrows for refreshing. By using firebug to inspect the css for that button you can alter as you choose:
//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;}
  • To hide the loading indicator: myToolbar.loading.hide()
  • To get the start for the currently displayed page try using pagingToolBar.cursor.
  • To get the current page number: start / limit + 1 = page number
  • CheckBox Selection Model issues see this thread

More Toolbar Stuff

Grid inside another component

Grid within form

  • Situation: EditorGridPanel in a FormPanel and want to send the data from the grid with the rest of the form submission. See this thread. Or another approach:
/**
 * 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
 */

Grids inside Tab Panels

  • Remember that a Grid Panel IS a Panel. So, do not wrap a grid inside another panel, thus leaving the Grid without a layout manager (also known as "over nesting").
  • A GridPanel is like any other panel, you can add it directly as an item to a tab. More in API Docs.
//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();
  • Set layoutOnTabChange on the Tab Panel (Set to true to do a layout of tab items as tabs are changed.)
//grid object
var g = new Ext.grid.GridPanel({
    title:'I will be the tab label';
});
var tabs2 = new Ext.TabPanel ({
<font color="green">...
   <a href="http://extjs.com/deploy/dev/docs/?class=Ext.TabPanel&member=renderTo">renderTo</a>: document.body,
   <a href="http://extjs.com/deploy/dev/docs/?class=Ext.TabPanel&member=activeTab">activeTab</a>: 0,
   <a href="http://extjs.com/deploy/dev/docs/?class=Ext.TabPanel&member=width">width</a>: 200,//a number ('100%' is not a number)
   <a href="http://extjs.com/deploy/dev/docs/?class=Ext.TabPanel&member=height">height</a>:150,
   <a href="http://extjs.com/deploy/dev/docs/?class=Ext.TabPanel&member=frame">frame</a>:true,
   <a href="http://extjs.com/deploy/dev/docs/?class=Ext.TabPanel&member=layoutOnTabChange">layoutOnTabChange</a>: true,//do a layout of tab items as tabs are changed
   <a href="http://extjs.com/deploy/dev/docs/?class=Ext.TabPanel&member=deferredRender">deferredRender</a>:true, //whether or not each tab is rendered only when first accessed (defaults to true). 
   <a href="http://extjs.com/deploy/dev/docs/?class=Ext.TabPanel&member=defaults">defaults</a>:{autoScroll: true},
   <a href="http://extjs.com/deploy/dev/docs/?class=Ext.TabPanel&member=items">items</a>: g //the grid object

  • See an example in the demos (layout-browser example, combination.js) and grid in tab example here.
  • Masking the tab on loading:
tabs.getEl().mask('Loading...', 'x-mask-loading');
...
tabs.getEl().unmask();
  • If any tab items have a layout specified, add hideMode:'offsets' on individual cards (items). The default hideMode uses display:none to hide elements which may incapacitate Ext's ability to calculate items' width/height. As a general rule: Any attempt to render into a display:none container === rendering problems.

Miscellaneous

Performance Tips

  • Suspend events on the grid (or store) during operations. Then programmatically refresh each views and then restore events. You'll get MUCH better performance. Especially helpful during drag / drop of rows and when the number of rows gets larger.

Combining Fields into One Column

  • Concatenate the two elements in the Record definition.
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'}
        ]
   });
  • The convert function in a field definition now receives in the second parameter the whole of the data object that is being used to create the Record, so you can create any value you like in a field. In addition to the snippet below, also see this example.
//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(', ');
            }
        }
    ]
})
  • For another alternative that allows sorting on the resulting "calculated" field see this thread

ComboBox displays <div class="x-grid-col-1 x-grid-cell-inner">

  • As an alternative, see this thread.
  • to set up a ComboBox Editor in a grid so it allows other (custom) values that are not in the combobox's store:
Ext.form.ComboBox({
  ...
  valueField: undefined,
  ...
});
  • See this thread for another approach.
  • Here is a portion of a column model:
...
   {
      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
   }
  • How do I get the value of the combo's selection (instead of the text) displayed after an edit is done on the combo?
  • Set up the Grid to actually store "0", "1" or "2" and use a column renderer to display the textual representation.
  • Add a listener to the EditorGridPanel's 'validateedit' event, and pull the textual value out of the combo, and manually set it into the record, then return false so that EditorGridPanel does not go ahead with its change (grid does not think it was a 'true' edit).
//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;
         }
      }
   }
}
  • Getting the comboBox to display the displayField instead of their 'value' after the user makes a change.
  • An EditorGridPanel calls getValue() on the form Field (in this case a comboBox) that it is using as the column editor at the end of the edit. If there is a hidden field, getValue() returns that field, not the displayed text.

So, the value of the Combo will be "0", "1" or "2" and that is what will be displayed in the Grid. Two options:

  • Give the Combo a hiddenName as the name of the field you wish submitted.
  • If you use name, it is the visible input which gets that name (which is a normal <input type="text">) and is submitted. hiddenName is used to create an <input type="hidden"> into which the valueField is stored.

Display a footer bar on a grid (replicate the title bar)

  • Use a config which is a DomHelper element creation specification so you can put anything there (It's up to you to make it look right.).
footerCfg: {cls: 'whatevere', html: 'Whatever'}
  • Or use the following and after rendering (perhaps in a listener), you can render Components into it.
footer: true

How to make a check box column in grid

  • Create instance for each check box column before creating instance of grid::
var checkBox_column_name = new Ext.grid.CheckColumn({
   header:'My header',
   dataIndex:'my_bool_colum',
   width:55
});
  • Specify this column in the grid's ColumnModel
var colModel = new Ext.grid.ColumnModel([
   <your_other_columns_here>,
   checkBox_column_name
]);
  • Specify this column in grid plugins(each column, because of declaration in plugins section you don't need to define it's editor in column definition):
var grid = new Ext.grid.EditorGridPanel({
   <other_grid_configs>,
   cm = colModel,
   plugins:[<any_other_plugins>, checkBox_column_name],
   <more_grid_configs>
});
  • Finally, don't forget to grab the source code for this plugin and place it somewhere in your code before you call new Ext.grid.CheckColumn. You can find the source code just below or from the end of the file ext\examples\grid\edit-grid.js (around line 137):
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>';
    }
};

Copying grid text to clipboard

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);
       }
     }
   }
  );

Showing totals for columns

Problems with grouping

  • make sure that the field you are grouping on is in the column Model.
  • If you don't want it showing, put hidden:true

Automatically refreshing a grid

Updating a cell (actually the store)

var record = editorGridPanel.getStore().getAt(row);
record.set('fieldname', newValue);
  • If this Field is an editor, you can collect the Grid cell's value in a beforeedit event handler (see Listeners in following post). See getSelectedCell() in API docs.
  • If using mouseover:
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);
  • If you modify a record in the store and want the associated row in the GridPanel (not the entire grid) to be refreshed use the store's update() method.
  • To remove a record completely (delete a row) look to the store.remove() method.

Scrolling issues (Horizontal Scrolling, Scrolling to particular row)

No horizontal scrollbar

  • If you don't want horizontal scrollbar:
viewConfig: {
    forceFit: true
}
  • The grid doesn't show a horizontal scrollbar. Due to browser limitations it is not possible to show a horizontal scrollbar in a grid configured with autoHeight:true. Give your grid a fixed width and height (if it is not already set by the container layout) and configure it with:
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 paging with horizontal scrollbar

  • If you have a GridPanel with horizontall scroll and when go to another page the horizontal scroll reverts to the left side position you can try this code to prevent it from scrolling back:
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()
});

Maintaining scroll position when store is reloaded

  • To maintain scroll position when store is reloaded, configure the GridView for the Grid by using grid's viewConfig with the following:
/**
 * 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);
    }
}
  • or override the gridview, re-implementing a custom method to enable scrolling to top:
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
});
  • If you don't want to override or you want to add animation then you could access the gridView's scroller which implements Ext.element and then use it's built-in scrollTo() method. Assuming "grid" is a variable holding a reference to the grid:
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' });

Horizontal scrolling in editable grid

  • If horizontal scrolling is screwing up your editable grid, try adding the following to your css (to override ext-all.css):
.ext-gecko .x-grid-editor {
    position: relative !important; 
    float: left;
}

or try:

Ext.override(Ext.grid.GridView, {
  getEditorParent: function(ed) {
    return this.mainWrap.dom;
  }
});

Scroll to particular row

cId = selModel.getSelections()[0].data.id;
grid.getView().focusRow(ds.indexOfId(cId));

Scroll to last row

  • Make sure you do this AFTER the rows are rendered:
grid.getView().focusRow(grid.getStore().getCount() - 1);

Mask Issues (not centering, etc.)

  • To mask a the tab on loading:
tabs.getEl().mask('Loading...', 'x-mask-loading');
...
tabs.getEl().unmask();
  • Mask not centering, see this also this.

Where can I learn more about grids?

Tutorials

Examples / Online demos

Blogs

Extensions - Grid related

Filtering

Data Manipulation

server. [1, 2, 3])

Drag and drop

Presentation, views, etc.

Actions

Exporting

Generation

Printing

Share this post:
Leave a reply

Written by The Sencha Dev Team

1 Comment

Bratin

3 years ago

Any way to implement sorting, searching with table( using XTemplate)?

Leave a comment:

Commenting is not available in this channel entry.