天天看點

ext grid 問題(一) EN

這是我在http://www.extjs.com/forum/showthread.php?t=27784上面找到的英文的grid的常見問題及解決方法

部分中文對照:http://www.iteye.com/topic/197071

FAQ: Grid (compiled questions, examples/tutorials, listeners, extensions)

--------------------------------------------------------------------------------

Here is some lessons learned / compiled questions from some of the repetitive questions posted in the forums about grids.

Organization:

Frequently asked Questions

Additional Resources for Grids (examples, tutorials, user extensions, etc.)

Helpful listeners related to Grids

Feel free to let me know about any mistakes I might have inadvertently made when composing this. Also please contribute your own pitfalls here as well. This might make it easier for someone to have a good starting point on where to turn to when their grids don't work.

I'll try to update the first few threads of this post appropriately if anyone posts more questions/solutions as time goes on so the 'answers' don't get buried in this thread.

Frequently asked questions about grids:

Grid is empty

Grid Doesn’t show images correctly

Sorting issues

Grids inside Tab Panels

Links inside grid cells

Combining Fields into One Column

How to modify appearance, behavior of specific rows or columns?

How to add click handlers to specific Grid cells?

Store related issues (appears empty even after calling load(), listeners, nested JSON)

Grid size issues (make grid resize with the window, etc.)?

How to Update a cell (actually the store)?

How to reload a Grid from a different URL?

XML Issues

Grid still shows records even when it should be empty

How to debug what is going into Json Reader?

How to drag/drop/reorder rows?

Scrolling issues (Horizontal Scrolling, Scrolling to particular row)

Mixing fixed width and variable width columns

How to hide a column...or row?

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

How to add a column with tools for each record?

Toolbar: centering, multiple toolbars

How to delete all selected rows?

Problems with paging or total record count

Get a record from the grid by row index ... or by id

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

"Auto size" columns to the largest element in them

How to Disable editing

What is difference between contentEl, applyTo and renderTo?

Dirty Record / Red Flag display (modifying, etc.)

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

Adding border lines to cells in grid

Getting rid of the scrollbar (empty column) on right side of grid

How to use a footer with a grid

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

comboBoxes in Grids

Styling a Grid

How to make a check box column in grid

How to get the index of a selection

How to change the baseParams

How to get additional data sent back with store load?

Load multiple stores with one AJAX request?

Grid within form

1. 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 using one of the following? (height, autoHeight, or within a container with layout:‘fit’)

grid = new Ext.grid.EditorGridPanel({

//other configs...

//Don't forget to specify the height!

//Three options: (1) specify height explicitly, (2) enable autoHeight, (3) put grid inside a container with layout:'fit'

autoHeight:true //autoHeight resizes the height to show all records

//-or-

height:350

Did you load the store? store.load()

Did you render the grid? Render a grid using 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?

If putting the grid in a tab panel try adding "layoutOnTabChange: true" to the tabPanel config.

If using JSON, check it:

is the JSON properly formed (paste the JSON response into the checker at www.jslint.com to verify)

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 conincides with what your reader is expecting (array of records under root node, etc.)

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': [{"CodigoProduto":"3009","TipoProduto":"FERT","DescricaoProduto":"7 UP Reg. 12 GR 1,50 PET",

"UnidadeMedidaProduto":"TAB","Sabor":"V-REGULAR","CodigoMarca":"35","ProdutoPesoBruto":"19.448",

"ProdutoPesoLiquido":"18.760","ProdutoPesoUM":"KG","ProdutoVolume":"18.000","ProdutoVolumeUM":"L",

"ProdutoUM_PAL":"36","Eliminar":""}]

}

Make sure your data record is an array of items:

var SecurityItemRecord = Ext.data.Record.create([{name: 'type'}, {name: 'itemName'}]);//OK

var SecurityItemRecord = Ext.data.Record.create({name: 'type'}, {name: 'itemName'});//NO GOOD

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 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. See next post for listeners to add.

A few of the key listeners to watch for:

Store ‘datachanged’

Store ‘load’

Store ‘loadexception’ - What does loadexception mean?

Proxy ‘load’ / ‘loadexception’

You can also add the ajax and grid listeners shown in following post

2. 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 /)

3. 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 dataIndex (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:

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 then the solution is to reset paging in GridPanel's headerclick event:

//use an event that occurs early enough to change the params so they are used in the store.load() call.

'headerclick' : function(grid, columnIndex, event) {

if (grid.store.lastOptions.params.start) {

grid.store.lastOptions.params.start = 0;

}

}

or use this solution because it also handles sort via the column header context menus:

store.sort = store.sort.createInterceptor(_new_storeSort);

function _new_storeSort(fieldName, dir){

if (this.lastOptions.params) this.lastOptions.params.start = 0;

return true;

}

4. Grids inside Tab Panels

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

Ext.getCmp('centre-panel').add(grid);

Ext.getCmp('centre-panel').doLayout();

Make sure the Grid Panel IS the Tab Panel (Grid is a panel component already so there is no need to wrap it with another panel).

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

5. Links inside grid cells

Define a custom renderer or

You could use the rowselect event of Ext.grid.RowSelectionModel. You might do something like this:

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

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

For another alternative that allows sorting on the resulting "calculated" field see this thread

7. How to modify appearance, behavior of specific rows or columns?

You can use a function of your own to add a CSS class name of your own choosing a Grid row depending upon the data in the underlying Record object.

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 example the array-grid example has a column with an id of 'company'. If you wanted to change the cursor to be a text cursor add this css after the core css is loaded:

.x-grid3-td-company {

cursor: text;

}

Override the getRowClass

Use a function in the Grid's View and return a class name string.

// Create grid view to highlight rows

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'

break;

case '3m Co' :

cls = 'green-row'

break;

case 'Altria Group Inc' :

cls = 'red-row'

break;

}//end switch

return cls;

}

}); //end gridView

// create the Grid

var grid = new Ext.grid.GridPanel({

...

view: gridView, // added new grid view

...

});

//uses the following css:

.red-row{ background-color: #F5C0C0 !important; }

.yellow-row{ background-color: #FBF8BF !important; }

.green-row{ background-color: #99CC99 !important; }

Another snippet:

var shownOnDrawing = {

header: "Shown on Drawing ?",

dataIndex: 'shownonDrawing',

width: 160,

align: 'center',

renderer: function(value, meta) {

if (value == 'Same As New Part') {

meta.css = 'some-class';

}

return value;

},

editor: new Ext.form.ComboBox({

typeAhead: true,

triggerAction: 'all',

transform:'Drawing',

lazyRender:true

})

}

See example here.

If you want to do this dynamically (example: change row color when a click event on a cell is raised):

Get the row from the View (http://extjs.com/deploy/dev/docs/?cl...&member=getRow).

Set it's style or add a class name.

To get this working with the Ext.grid.RowExpander plugin, put the getRowClass function in the RowExpander config options instead of GridView or GroupingView.

To add or remove a CSS class from selected grid rows use addClass or removeClass:

//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 use addClass or removeClass

}

Adding an icon to the column header:

//css:

.x-grid3-hd-classCompany {

background: transparent url(../../resources/images/icons/silk/building.png) no-repeat 3px 3px ! important;

padding-left:20px;

}

.x-grid3-td-classCompany {

//something

}

//javascript:

colModel: new Ext.grid.ColumnModel([ //instantiate ColumnModel

{

dataIndex: 'company',

header:"Company",

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}"

},

...

])

8. How to add click handlers to specific Grid cells?

Add a listener for the Grid's 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) {

var record = grid.getStore().getAt(rowIndex); // Get the Record for the row

// Get field name for the column

var fieldName = grid.getColumnModel().getDataIndex(columnIndex);

var data = record.get(fieldName);

}

9. Store related issues (appears empty even after calling load(), listeners, nested JSON)

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, use a callback to the load call, or add listeners to the Store's "load" and "loadexception"] (most problems will be indicated here) events.

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:

myStore.on({

'load':{

fn: function(store, records, options){

//store is loaded, now you can work with it's records, etc.

console.log('Data Store listener fired (load), arguments:',arguments);

console.log('Store count = ',store.getCount());

}

,scope:this

}

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

}

)

});

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

mjlecomte

View Public Profile

Send a private message to mjlecomte

Find all posts by mjlecomte

#3 02-27-2008, 09:12 PM

mjlecomte

Ext Premium Member Join Date: Jul 2007

Posts: 2,067

11-20

--------------------------------------------------------------------------------

11. Updating a cell (actually the store)

You actually update the store, not a cell:

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.

12. How to 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;

});

You could also try:

store.proxy.conn.url = 'myNewUrl.php';

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

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

/

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

Ext.ux.grid.RowAction = function(config) {

Ext.apply(this, config);

this.addEvents({

beforeaction:true

,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

22. Toolbar: centering, multiple toolbars?

Centering the toolbar elements:

Option 1:

myToolbar.getEl().child("table").wrap({tag:'center'})

Option 2:

var t = myGridPanel.getBottomToolbar().el;

var c = t.createChild("<center></center>");

c.appendChild(t.child("table"));

For multiple toolbars example see: http://gridsearch.extjs.eu/

23. How to delete all selected rows?

Ext.each(contactgrid.getSelectionModel().getSelections(), contactgrid.getStore().remove, contactgrid.getStore());

tbar: [

{

text: 'Delete Contacts',

iconCls:'remove',

handler : function(t){

console.log(' inside "Delete Contacts" with arguments = ',arguments);

grid = contactgrid;

//get the store associated with the grid:

store = grid.getStore();

//returns array of record objects for selected rows (all info for row)

var selections = grid.selModel.getSelections();

console.log(' grid = ',grid);

console.log(' selections = ',selections);

var n = selections.length;

for(var i = 0; i < n; i++){

console.log(' Number Selected =',n);

console.log(' selections =',selections);

console.log(' store before remove attempt #',i,': ',store,' store has ',store.getCount(),' records');

store.remove(selections[i]);

console.log(' store after remove attempt #',i,': ',store,' store has ',store.getCount(),' records');

}//end for

}//end handler

}//end Delete Contacts

24. Issues with paging, paging toolbar, or total record count

To get the start for the currently displayed page try using pagingToolBar.cursor.

Paging is typically handled on the server side.

The idea is to reduce the amount of data exchanged with the client.

Make sure the grid and the pagingtoolbar are using the SAME store.

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, but your JSON contains 10 records the paging will act strange since all the data returned will still get loaded into the grid.

To use paging, pass the paging requirements to the server when the store is loaded.

bbar: new Ext.PagingToolbar({

pageSize: 25,

...

})

store.load({params:{start: 0, limit: 25}});//pass params for first page load

//or if you want to use autoLoad in the store's configuration:

autoLoad: {params:{start: 0, limit: 25}}

To get the page number: start / limit + 1 = page number

Check using Firebug what all is sent out in the POST and retrieve that information server side to generate the necessary sql to return the appropriate data. Something to the effect of:

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

$sortDir = isset($_POST['dir']) ? $_POST['dir'] : 'ASC';//default to ASC if not set

$sortBy = isset($_POST['sort']) ? $_POST['sort] : 'company';//default to company name if not set

$sql_count = 'SELECT * FROM ' . $table;

$sql = $sql_count . ' ORDER BY ' . $sortBy. ' ' . $sortDir . ' LIMIT ' . $start . ', . $end;

$result_count = mysql_query($sql_count);

$rows = mysql_num_rows($result_count);

If you are sending additional information to the server you may want to also use baseParams so the additional data is sent with subsequent page requests or add a listener to the store's beforeload event:

store.on({

...

,'beforeload':{

fn: function(store, options){

console.info('Data Store listener fired fired (beforeload), arguments:',arguments);

options.params || ( options.params = {} ); //assert params

Ext.apply ( options.params, {

//apply stuff to params

pageNo: this.pageNumber //assuming pageNumber has been calculated into this var

});

}

,scope:this

}

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'

}

If you want to page with inline data (local data) try the following:

Include examples/locale/PagingMemoryProxy.js

use:

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

})

25. Get a record from the grid by row index

by row index:

var record = grid.getStore().getAt(rowIndex);

by id: Don't confuse these:

id of the record object. store.getById(some_id) where some_id is the id of the Record to find (autogenerated unless you specify it ...which you probably have not).

some field inside the record that you called "id". record.get(some_unique_key_name) Where the some_unique_key_name field could have been called "key", "primaryKey", or whatever....even "id". I purposefully do not call any of my fields "id" just to reduce the confusion in my head.

26. Load data to a grid from two diferent 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}

];

27. "Auto size" columns to the largest element in them

Option 1

Option 2

28. Disable editing of particular rows

Use beforeedit and return false to stop the edit

var grid = new Ext.grid.EditorGridPanel({

...

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 Ext.grid.EditorGridPanel.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 cm = new Ext.grid.ColumnModel({

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 Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);

}

});

Option/Example 6

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

}

29. What is difference between contentEl, applyTo and renderTo?

For most use cases you should be able to use the contentEl config option with a div in your markup with a css class of x-hidden.

contentEl - This config option is used to take existing content and place it in the body of a new panel. It is not going to be the actual panel itself. (It will actually copy the innerHTML of the el and use it for the body). You should add either the x-hidden or the x-hide-display CSS class to prevent a brief flicker of the content before it is rendered to the panel.

applyTo - This config option allows you to use pre-defined markup to create an entire Panel. By entire, I mean you can include the header, tbar, body, footer, etc. These elements must be in the correct order/hierarchy. Any components which are not found and need to be created will be autogenerated.

renderTo - This config option allows you to render a Panel as its created. This would be the same as saying myPanel.render(ELEMENT_TO_RENDER_TO);

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

record.modified[this.dataIndex] = undefined; // will prevent a "dirty" flag to be displayed on the check box

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;

}

Or:

<style type="text/css">

.x-grid3-td-[id-of-column] {background-image:none!important;}

</style>

mjlecomte

View Public Profile

Send a private message to mjlecomte

Find all posts by mjlecomte

#5 02-27-2008, 09:46 PM

mjlecomte

Ext Premium Member Join Date: Jul 2007

Posts: 2,067

31-

--------------------------------------------------------------------------------

31. Editor Grid - ComboBox displays <div class="x-grid-col-1 x-grid-cell-inner">

Most likely the dataIndex is missing or improperly assigned in the grid's ColumnModel definition for that particular column. If the dataIndex is omitted the editor will, by default, gather the cell's innerHTML as the editable content.

32. Adding border lines to cells in grid

Try overriding the css with:

.x-grid3-cell {

border-right: solid 1px #EDEDED;

}

Or try overriding the css to remove the left/right padding from the table data grid cells.

.x-grid3-col {

border-left: 1px solid #EEEEEE;

border-right: 1px solid #D2D2D2;

}

.x-grid3-row td, .x-grid3-summary-row td {

padding-left: 0px;

padding-right: 0px;

}

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

http://extjs.com/deploy/dev/docs/?cl...r=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:

grid.getView().scrollOffset = 1;//Use 1 or 2 here as setting the scrollOffset to 0 may cause a horizontal scrollbar to appear

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}

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

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

See this thread

36. comboBoxes in Grids

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

'validateedit': function(e){//listening for the grid's 'validateedit' event var rec = e.record; var store = rec.fields.items[e.column].editor.store;//looking into the store of the combo 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){ rec.set(e.field, option[1]);//setting the value to the 'textual' value of the selection using rec.set(fieldName, newValue) to set it how you want return false;//return false so that the EditorGridPanel thinks it was an invalid edit and does not do the change itself } } }}

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.

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

38. 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>'; }};

39. How to 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);

40. 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, } }) } }]

41. How to get additional data sent back with store load?

Use reader's jsonData.

42. Load multiple stores with one AJAX request?

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: ,

dataStore2:

}

//decode the json packet...

var json = Ext.decode(response.responseText);

//load the stores:

store1.loadData(json.dataStore1);

store2.loadData(json.dataStore2);

43. Grid within form

Situation: EditorGridPanel in a FormPanel and want to send the data from the grid with the rest of the form submission.

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

mjlecomte

View Public Profile

Send a private message to mjlecomte

Find all posts by mjlecomte

#6 02-27-2008, 09:47 PM

mjlecomte

Ext Premium Member Join Date: Jul 2007

Posts: 2,067

Resources

--------------------------------------------------------------------------------

Where can I learn more about grids?

API docs

Source files

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

Extensions - Grid related

Filtering:

Grid Filter (Plugin)

Grid Search (Plugin)

Cherry On Ext: yet another grid filtering tool (and not only)

Searching and Filtering

Data Manipulation

Timelines - Grid undo/redo feature

WriteStore (Does for writing-to-the-server what Ext.data.Store does for reading-from-the-

server. [1, 2, 3])

EditorGrid Validation Plugin

Grid Form Plugin (edit grids via popup form)

Drag and drop

Grid to Grid: 1, 2

With Grouping

Presentation, views, etc.

Grouped Header Grid: 1, 2

LiveGrid (scrolling instead of paging grid)

Paging Row Numberer

GridView Override adding 'showAll' button and 'columnViews' filter sets.

Ext.ux.grid.SubTableRowExpander: Foldable Rows for Grid

Grid Summary Plugin With Fixed Summary Row

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

Actions:

Grid CellActions Plugin

Grid RowActions Plugin

Ext.ux.GridRowDeleter

Exporting:

Grid to Excel Export

Generation:

Autogrid (now with serverside column storage)

Generated object from mysql => grid, filter, form

Printing: 1, 2

Screencasts - http://extjs.com/learn/Screencasts

General FAQ - http://extjs.com/learn/Ext_FAQ

mjlecomte

View Public Profile

Send a private message to mjlecomte

Find all posts by mjlecomte

#7 02-27-2008, 09:47 PM

mjlecomte

Ext Premium Member Join Date: Jul 2007

Posts: 2,067

Listeners

--------------------------------------------------------------------------------

Listeners can be very handy both to understand the process of what's going on and for debugging.

Here are some listeners that may be very helpful. I've highlighted in red what might be the more 'important' ones if you're just trying to figure out why the grid won't work. In each case "store","myProxy", and "grid" should correspond to the variable that you have assigned in your project.

Store Listeners

store.on({

'load':{

fn: function(store, records, options){

console.log('01 - Data Store listener fired (load), arguments:',arguments);

console.log(' this:',this);

}

,scope:this

}

,'loadexception':{

fn: function(httpProxy, dataObject, args, exception){

console.log('** - Data Store listener fired (loadexception), arguments:',arguments);

}

,scope:this

}

//add remaining events for education:

,'add':{

fn: function(store, records, index){

console.log('Data Store listener fired (add), arguments:',arguments);

}

,scope:this

}

,'beforeload':{

fn: function(store, options){

console.log('Data Store listener fired fired (beforeload), arguments:',arguments);

}

,scope:this

}

,'clear':{

fn: function(store){

console.log('Data Store listener fired fired (clear), arguments:',arguments);

}

,scope:this

}

,'datachanged':{

fn: function(store){

console.log('11 - Data Store listener fired fired (datachanged), arguments:',arguments);

console.log(' If you set a breakpoint here the entire grid will be rendered without data');

console.log(' ...about to "refresh" grid body');

}

,scope:this

}

,'remove':{

fn: function(store, record, index){

console.log('Data Store listener fired fired (remove), arguments:',arguments);

}

,scope:this

}

,'update':{

fn: function(store, record, operation){

console.log('Data Store listener fired fired (update), arguments:',arguments);

}

,scope:this

}

});

Ajax Listeners

Ext.Ajax.on({

//Fires before a network request is made to retrieve a data object:

'beforerequest':{

fn: function(connection, options){

console.log('03 - Ajax listener fired (beforerequest), arguments(connection, options):',arguments);

}

,scope:this

}

//Fires if the request was successfully completed:

,'requestcomplete':{

fn: function(connection, response, options){

console.log('10 - Ajax listener fired (requestcomplete), arguments(connection, response, options):',arguments);

}

,scope:this

}

//Fires if an error HTTP status was returned from the server. See HTTP Status Code

//Definitions for details of HTTP status codes:

,'requestexception':{

fn: function(connection, response, options){

console.log('Ajax listener fired (requestexception), arguments:(connection, response, options)',arguments);

}

,scope:this

}

});

Proxy Listeners

myProxy.on({

'beforeload':{

fn: function(store, options){

console.log('02 - Proxy listener fired (beforeload), arguments:',arguments);

}

,scope:this

}

,'load':{

fn: function(store, options){

console.log('Proxy listener fired (load), arguments:',arguments);

}

,scope:this

}

,'loadexception':{

fn: function(store, options){

console.log('Proxy listener fired (loadexception), arguments:',arguments);

}

,scope:this

}

});

Grid listeners

grid.addListener({

'activate':{

fn: function(panel){

console.log('Grid listener fired (activate), arguments:',arguments);

}

,scope:this

}

,'add':{

fn: function(container, component, index){

console.log('Grid listener fired (add), arguments:',arguments);

}

,scope:this

}

,'afteredit':{

fn: function(event){

console.log('Grid listener fired (afteredit), arguments:',arguments);

}

,scope:this

}

,'afterlayout':{

fn: function(container, layout){

console.log('Grid listener fired (afterlayout), arguments:',arguments);

}

,scope:this

}

,'beforeadd':{

fn: function(container, component, index){

console.log('Grid listener fired (beforeadd), arguments:',arguments);

}

,scope:this

}

,'beforeadd':{

fn: function(container, component, index){

console.log('Grid listener fired (beforeadd), arguments:',arguments);

}

,scope:this

}

/**

* beforeclose : ( Ext.Panel p )

* Fires before the Panel is closed. Note that Panels do not

*

繼續閱讀