Ext grid FAQ

Contents

[hide ]

More Grid related questions

See this forum post .


Debugging

Grid is empty / Populating the grid problems:

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

Likely problems to check

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 grid GridView getRowClass 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; }
//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.

FX
  • 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;    
}
;
  • You can get the row/column of the Grid using
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

  • 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 I ndex (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
 (
{

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

 

 

  • 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

Screencasts

Screencasts

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值