Contents
[hide ]- 1 More Grid related questions
- 2 Debugging
- 3 Store related issues
- 3.1 Listeners
- 3.2 Store appears empty even after calling load()
- 3.3 nested JSON
- 3.4 XML Issues
- 3.5 How to get additional data sent back with store load?
- 3.6 Changing the baseParams
- 3.7 Reload a Grid from a different URL?
- 3.8 Automatically reload store / grid
- 3.9 Load multiple stores with one AJAX request?
- 3.10 Load data to a grid from two different server calls/stores
- 3.11 Grid still shows records even when it should be empty
- 4 Appearance
- 4.1 Grid Doesn’t show images correctly (checkboxes, etc.)
- 4.2 To modify a cell/row/column
- 4.3 To modify a column (or just the header of a column)
- 4.4 How To modify the styling of a row
- 4.5 To modify text
- 4.6 Adding border lines to cells in grid
- 4.7 Styling a Grid
- 4.8 Getting rid of empty column/reserved space for scrollbar on right side of grid
- 4.9 "Auto size" columns to the largest element in them
- 4.10 Mixing fixed width and variable width columns
- 4.11 How to wrap contents within a cell of a fixed width grid column?
- 4.12 Dirty Record / Red Flag (modifying, etc.)
- 4.13 Hide header context menu entirely
- 4.14 Hide header context menu sort options
- 4.15 Hide a column...or row
- 4.16 Grid in centered div
- 4.17 How to retrieve all hidden columns
- 4.18 FX
- 4.19 Add ToolTip or Qtip
- 4.20 Decimal Precision
- 5 Behavior
- 5.1 Selecting a row in grid once rendered
- 5.2 Links inside grid cells
- 5.3 Add click handlers to specific grid cells
- 5.4 How to add or remove a grid column
- 5.5 How to add a column with tools for each record?
- 5.6 Deleting Records/Rows
- 5.7 Make grid resize with the window
- 5.8 Resize the column width when double click header
- 5.9 Maintain GridPanel scroll position across Store reloads
- 5.10 Disabling column move for particular column
- 5.11 Disable Tab key from activating editor
- 5.12 Disable editing of particular rows, columns, etc
- 5.13 Change one cell and update others in same row
- 5.14 Manually refresh a row
- 5.15 Previous or Old value in editor grid cell
- 5.16 Key Maps
- 5.17 Expand Certain Rows
- 5.18 Moving grid columns
- 5.19 How to drag/drop/reorder rows?
- 5.20 Get a record from the grid by row index
- 5.21 Get the index of a selection.
- 5.22 Save before closing
- 5.23 How to select text in the grid (with the mouse) so that it can be copied to the clipboard
- 5.24 Saving
- 5.25 Animating
- 5.26 Adding a new Record
- 5.27 Last row of grid not editable
- 6 Sorting issues
- 7 Paging, Paging toolbar, Total record count
- 7.1 Basics
- 7.2 Sending additional data
- 7.3 Altering Params
- 7.4 Customize Paging parameter names
- 7.5 Paging with local data (Paging Memory Proxy, or Paging Store)
- 7.6 PagingToolbar does not update total
- 7.7 Add Paging Toolbar Dynamically
- 7.8 Change Paging Toolbar Store
- 7.9 Paging Toolbar Loading Indicator
- 7.10 More Toolbar Stuff
- 8 Grid inside another component
- 9 Miscellaneous
- 9.1 Performance Tips
- 9.2 Combining Fields into One Column
- 9.3 ComboBox displays <div class="x-grid-col-1 x-grid-cell-inner">
- 9.4 Display a footer bar on a grid (replicate the title bar)
- 9.5 How to make a check box column in grid
- 9.6 Copying grid text to clipboard
- 9.7 Showing totals for columns
- 9.8 Problems with grouping
- 9.9 Automatically refreshing a grid
- 9.10 Updating a cell (actually the store)
- 9.11 Scrolling issues (Horizontal Scrolling, Scrolling to particular row)
- 9.12 Mask Issues (not centering, etc.)
- 10 Where can I learn more about grids?
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:
- height
- autoHeight
- or within a container with a size-managing layout , eg 'fit', 'border' or 'anchor'
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?
- call store.load()
//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:
- Store ‘datachanged ’
- Store ‘load ’
- Store ‘loadexception ’ - What does loadexception mean?
- Proxy ‘load ’ / ‘loadexception ’
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?
- Use reader's jsonData .
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
- Automatic reload
- Or simply:
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.
- Option 1 (see this thread )
//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
- use a renderer (an "interceptor" method which can be used transform data (value, appearance, etc.) before it is rendered) in combination with the ColumnModel setRenderer() metadata object :
//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; }
- 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 .
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 :
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
- one technique involves overriding Ext.Tooltip
- adding a qtip to grid cell:
//option 1 //======== renderer = function ( data, metadata, record, rowIndex, columnIndex, store) { //build the qtip: var title = 'Details for ' + value + '-' + record.get ( 'month' ) + '-' + record.get ( 'year' ) ; var tip = record.get ( 'sunday_events' ) ; metadata.attr = 'ext:qtitle="' + title + '"' + ' ext:qtip="' + tip + '"' ; //return the display text: var displayText = '<span style="color: #000;">' + value + '</span><br />' + record.get ( 'sunday_events_short' ) ; return displayText; } ; //option 2 //======== renderer = function ( data, metadata, record, rowIndex, columnIndex, store) { var qtip = '>' ; if ( data >= 0 ) { qtip = " qtip='yeah'/>" ; return '<span style="color:green;"' + qtip + data + '%</span>' ; } else if ( data < 0 ) { qtip = " qtip='woops'/>" ; return '<span style="color:red;"' + qtip + data + '%</span>' ; } return data; } ; //option 3 //======== var qtipTpl = new Ext.XTemplate ( '<h3>Phones:</h3>' , '<tpl for=".">' , '<div><i>{phoneType}:</i> {phoneNumber}</div>' , '</tpl>' ) ; renderer = function ( data, metadata, record, rowIndex, columnIndex, store) { // get data var data = record.data ; // convert phones to array (only once) data.phones = Ext.isArray ( data.phones ) ? data.phones : this .getPhones ( data.phones ) ; // create tooltip var qtip = qtipTpl.apply ( data.phones ) ; metadata.attr = 'ext:qtitle="' + title + '"' + ' ext:qtip="' + tip + '"' ; //return the display text: return data; } ;
- adding qtip to the header area see ColumnModel.tooltip
- to use a ToolTip check this technique or this one (make sure to read follow up posts for patches
- You can get the row/column of the Grid using
var row = GridPanel.getView ( ) .findRowIndex ( targetElement) ; var col = GridPanel.getView ( ) .getCellIndex ( targetElement) ;
Decimal Precision
- See this thread
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 }
- Option 2. Use the rowselect event of Ext.grid.RowSelectionModel. You might use:
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
- See this thread
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
- See: this thread
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; } ;
- Option/Example 4 (using beforeedit )
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?
- See this thread
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.
- use store.indexOf()
//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
- See this thread
How to select text in the grid (with the mouse) so that it can be copied to the clipboard
- See this thread
- here is the method Condor uses to re-enable selectability:
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
- See this thread
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
- Check the store configuration (sortInfo and sort type ) or use setDefaultSort()
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
- You can also add a listener to the store's beforeload event so you can alter parameters dynamically:
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
- See here
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
|
- 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
- From this thread :
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
- See this thread
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
- See: this thread
Updating a cell (actually the store)
- You actually update the store, not a cell using:
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
- Scrolling to a particular row use focusRow() :
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 ( ) ;
Where can I learn more about grids?
Tutorials
- Editor Grid (php/MySQL back end)
- Beginning Using the Grid Component
- Beginners DataGrid: (5 parts)
- Using Ext grid form dialog to achieve paging list, create, edit, delete function
- Basics of Paging With the Grid Component
- Dynamic Columns in a Grid
- Using Ext Grid with Ruby on Rails
- Editable grid data in a struts application
Examples / Online demos
Blogs
- Introduction to the Ext Grid Object
- Mixing Ext’s Grid with JQuery’s Flot
- Learn ExtJS AJAX: Defining grid properties at run time
- Quizzpot: Learning ExtJS from scratch
Extensions - Grid related
Filtering
- Grid Filter (Plugin)
- Grid Search (Plugin)
- Cherry On Ext: yet another grid filtering tool (and not only)
- Searching and Filtering
- Header Row Filtering
Data Manipulation
- Paging Store
- Timelines - Grid undo/redo feature
- WriteStore (Does for writing-to-the-server what Ext.data.Store does for reading-from-the-
- EditorGrid Validation Plugin
- Grid Form Plugin (edit grids via popup form)
- MultiRowUpdateSelectionModel - update multiple rows simultaneusly with the same values.
Drag and drop
- Grid to Grid: 1 , 2
- With Grouping
- thread
- demo
Presentation, views, etc.
- Grouped Header Grid: 1 , 2
- LiveGrid (scrolling instead of paging grid)
- Paging Row Numberer
- Improvements to Paging Toolbar
- GridView Override adding 'showAll' button and 'columnViews' filter sets.
- Ext.ux.grid.SubTableRowExpander: Foldable Rows for Grid
- Grid Summary Plugin With Fixed Summary Row
- Multiple level of grouping in grids
- Dynamic Grid
- Grid with locking column [Update 3 ]
- Extending the RowExpander Plugin
- Multi-level or Tree Grouping
- Ext.grid.RadioColumn
- Calculated fields
- Rating - Grid Custom Column Renderer
- Grid ProgressBar Selection Model
- "Smart" CheckBoxSelectionModel
- Grid Menu overflows visible page