I have an Ext.grid.Panel which shows several columns. One is a combo box with a static predefined set of selectable values.
On some data entries (columns) I want to disable the combo box. The disabling depends on another column value.
How do I do that?
More background:
After rendering column's cell data in a combo box I am not able to back reference the column's data model within the afterrender-listener.
If I am able to re-reference the data object then I can make a decision on allowing to make a selection from the combo box. Some columns (data tuples) allow selecting a different value from the combo box, others will not.
Background:
Panel's cell editing is turned on. I have one column which is a dropdown with determined values taken from the data received.
The store I am able to reference withing the afterrender method is not the store holding all the table data. It just holds the static Big-Medium-Low data of the column. But I need the store of the row; or better the correct row of data which ist stored in that more global store.
Ext.define( 'MyTable',{
extend: 'Ext.grid.Panel',
xtype:'myXtypeName',
mixins: {
field: 'Ext.form.field.Field'
},
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', { clicksToEdit: 1} )
],
store: Ext.create( 'Ext.data.Store', {
data: [],
autoLoad: false,
} ),
columns: [
{
header: '#',
dataIndex: 'id',
},
{
header: 'Severity',
dataIndex:'correctionSeverity',
sortable: false,
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
var myTable = Ext.ComponentQuery.query('myXtypeName');
myTable[0].severityChangeable[ record.id ] = record.data.correctionType == 'Changed';
return value;
},
editor: Ext.create( 'Ext.form.field.ComboBox', {
queryMode: 'remote',
store: Ext.create( 'Ext.data.Store', {
fields: ['id', 'text'],
data: [ { id:'entry0', text:'Big' },
{ id:'entry1', text:'Medium' },
{ id:'entry2', text:'Low' } ],
} ),
valueField: 'id',
displayField: 'text',
editable: false,
forceSelection: true,
listeners: {
afterrender: function(combo, eOpts) {
// how to access the underlaying entry (row data) instead of just the column?
// by another value of the
var rowData = ???;
if ( rowData['preventColumnDropdown'] == true ) {
combo.disable();
}
},
change: function(combo, newValue, oldValue, eOpts) {
// do something
},
}
}),
}
],
initComponent: function() {
this.callParent(arguments);
this.initField();
},
setValue: function(value) {
this.store.loadData( value );
},
});
I hope my point and problem is understandable. Please let me know if not so...
I am running ExtJS 6.0.0 classic.
The afterrender event is the wrong event to use, because the same combobox instance is reused for all rows. Therefore, at combobox instantiation time, the row data is not available.
You may want to use the beforeedit event on your CellEditing plugin to modify the editor before the start of a cell edit:
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1,
listeners: {
beforeedit: function(editor, context, eOpts) {
// Only if the combo column is being edited:
if(context.column.dataIndex == "myComboDataIndex") {
// Disable combobox depending on:
editor.setDisabled(!context.record.get("AllowComboDataEdit"));
// or just return false to stop edit altogether:
// return context.record.get("AllowComboDataEdit");
}
}
}
});
Related
I'm trying to edit a combobox value. But what happens is when I click a grid cell the old value disappears I want to keep a the and also avoid the user to select the values form the combo box itself and not values other than those in the drop down
How can i achieve these two things.
{
text: 'Food Items',
xtype: 'gridcolumn',
dataIndex: 'food_lister',
flex: 1,
renderer: function (value) {
console.log()
if (Ext.isNumber(value)) {
var store = this.getEditor().getStore();
return store.findRecord('foodid', value).get('fooddescp');
}
return value;
},
editor: {
xtype: 'combobox',
allowBlank: true,
displayField: "fooddescp",
valueField: "foodid",
queryMode: 'local',
mapperId: 'getfooddescrip',
lastQuery: '',
forceSelection: true,
listeners: {
expand: function () {
var call = this.up('foodgrid[itemId=fooditemgrid]').getSelectionModel().selection.record.data.foodname.trim();
this.store.clearFilter();
//this.store.filter('call',call);
this.store.filter({
property: 'call',
value: call,
exactMatch: true
})
}
}
}
}
Use editable : false in combo check the updated example
click here
Edit: As per your comment if you still want to use editable true and restric the value other than the store you have to handle it in cell editing event validateedit
plugins: {
ptype: 'cellediting',
clicksToEdit: 1,
listeners: {
validateedit: function( editor, context, eOpts ){
var store = context.column.getEditor().store,
record = context.record;
if(store.find('id',context.value) === -1){
context.cancel = true;
}
}
}
}
Above code is based on my example. Please check running example in my fiddle
I have rowediting grid that gird have two combos and two text field.
when type some character on combo box that combo box filter that type word from drop down list
select that filter value and form combo and do save record ok and gird view record correctly
NEXT---
after that select one of the gird record and start edit that record.type some character on combo box but that combo don't filter that type word form drop down list.
note: that happen clearFilter(true); after save/update record. If i remove clearFilter(true); gird view combo filtered result only that why I clear filter data before load store
This is my combo box grid column
{
xtype: 'gridcolumn',
itemId: 'colId',
width: 140,
dataIndex: 'ID',
menuDisabled: true,
text: 'Name',
editor: {
xtype: 'combobox',
id: 'cbold',
itemId: 'cbold',
name: 'CBO_ID',
allowBlank: false,
displayField: 'NAME',
queryMode: 'local',
store: 'Store',
valueField: 'FIELD_ID'
}
},
This gird RowRditing
plugins: [
Ext.create('Ext.grid.plugin.RowEditing', {
saveBtnText: 'Save',
pluginId: 'grdEditor',
autoCancel: false,
clicksToMoveEditor: 1,
listeners: {
edit: {
fn: me.onRowEditingEdit,
scope: me
}
}
})
],
onRowEditingEdit function
Ext.Ajax.request({
url: 'url',
method: 'POST',
scope:this,
success : function(options, eOpts) {
var store = Ext.getStore('GridStore');
var grid = Ext.getCmp('gridFileLyt');
cbo1Store = Ext.getStore('cbo1Store');
cbo1Store.clearFilter(true);
cbo1Store.load();
cbo2Store = Ext.getStore(cbo2Store);
cbo2Store..clearFilter(true);
fldStore.proxy.extraParams = {
'_ID': ''
};
cbo2Store.load();
if(response.success){
Ext.Msg.alert('Success', response.msg);
} else {
Ext.Msg.alert('Failed', response.msg);
}
}
});
I feel i did some basic mistake please help to me
Same story here, bro.
I actively use ExtJS 4 and RowEditing since 2011, it always worked, until today when I found this bug.
I could not even Google it until I debugged and found out a workaround with clearFilter():
rowEditingPlugin.on('beforeedit', function(editor, e) {
editor.editor.form.getFields().each(function(field){
if (field instanceof Ext.form.field.ComboBox) {
field.store.clearFilter(true);
}
});
});
I have a grid in my ExtJS 4.2.1 application that has an editable column with combo editor.
I need to render the column with the value from the DisplayField of the combo but the comboStore.getCount() = 0
Here is my grid:
Ext.define('App.employee.Grid', {
extend: 'Ext.grid.Panel',
requires: ['Ext.grid.plugin.CellEditing'],
alias: 'widget.employee.grid',
config: {
LocationId: 0
},
initComponent: function() {
var me = this,
store = me.buildStore(),
comboStore = Ext.create('App.store.catalog.Location', { autoLoad: true });
me.rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {
clicksToMoveEditor: 1,
autoCancel: false,
listeners: {
edit: function(editor, e) {
}
}
});
me.cellEditing = new Ext.grid.plugin.CellEditing({
clicksToEdit: 1
});
Ext.applyIf(me, {
plugins: [me.rowEditing],
columns: [{
xtype: 'rownumberer',
text: '#',
width: 50,
sortable: false,
align: 'center'
//locked: true
},{
text: 'Number',
dataIndex: 'EmployeeNumber',
align: 'center',
width: 90
}, {
text: 'Name',
dataIndex: 'EmployeeName',
flex: 1
}, {
text: 'Location',
dataIndex: 'LocationId',
width: 140,
renderer: function(value) {
// HERE!!!
// me.comboStore.getCount() = 0 so I never get a record
var record = me.comboStore.findRecord('LocationId', value);
return record.get('Description');
},
editor: {
xtype: 'combobox',
typeAhead: true,
triggerAction: 'all',
store: comboStore,
displayField: 'Description',
valueField: 'LocationId',
queryMode: 'local',
listConfig: {
width: 250,
// Custom rendering template for each item
getInnerTpl: function() {
return '<b>{Code}</b><br/>(<span style="font-size:0.8em;">{Description}</span>)';
}
}
}
}],
store: store,
});
me.callParent(arguments);
}
});
The problem is in the renderer function because the comboStore is always empty. The strange thing is that in my view If I click to edit the row and open the combo the combo has values.
[UPDATE]
What I think is that my comboStore has a delay when loading so the renderer is fired before the comboStore gets loaded. I figure this out because if I debug in chrome and I wait a few seconds, then it works... but don't know how to force to wait until comboStore is loaded.
Any clue on how to solve this? Appreciate any help.
A couple of solutions:
Ensure that the combo store is loaded before the grid store. This can be done by loading the combo first and from load event of its store trigger the grid store load. The disadvantage is that it adds unnecessary delay so this method impairs the user experience.
Load all needed stores in one request. It requires a bit of coding but it saves server roundtrip so it's a very valuable approach. Store loadData method is used to actually load store with the received data. You would, of course, first call it on combo, then on the grid.
The best method that I use almost exclusively is to turn the whole thing upside down and link display field to the store, not value field. Server must return both display and value fields in the grid store and a little piece of code that updates both fields in the grid store after editing is complete is required. This method is demonstrated here: Remote Combo in ExtJS Grid
I'm using ExtJS 4.1.2, and I have an Ext.grid.Panel with a check-box selection model and a text-box in the header for filtering the items in the grid:
var nameStore = new Ext.data.Store({
...
proxy: { type: 'ajax' ... },
fields: [ { name: 'name', type: 'string' }, ... ],
});
var headerBar = new Ext.toolbar.Toolbar({
xtype: 'toolbar',
dock: 'top',
layout: 'fit',
items: [{
xtype: 'textfield',
...,
listeners: {
change: function(fld, newVal, oldVal, opts) {
var grid = ...;
if (newVal.length > 0)
grid.store.filterBy(function(record, id) { return record.get('name').indexOf(newVal) !== -1; });
else
grid.store.clearFilter()
}
}
}]
});
var nameGrid = new Ext.grid.Panel({
...
selType: 'checkboxmodel',
selModel: { mode: 'SIMPLE' },
store: nameStore,
columns: [ { dataIndex: 'name', flex: true }, ... ],
dockedItems: [ headerBar ],
bbar: [ { xtype: 'tbtext', text: '0 items selected' ... },
listeners: {
selectionchange: function(selModel, selected, opts) {
var txt = ...;
txt.setText(Ext.String.format("{0} items selected", selected.length));
}
}
});
As shown nameStore's filter is applied (or removed) based on the value in headerBar's textbox. Also, a text string in the bottom bar is updated as items are selected/deselected.
Each of these features is working smoothly on its own. But the interaction is giving me trouble: if an item is selected and then gets filtered out, it is deselected and then remains so when the filter is cleared.
How can I maintain the selections (or at least appear to do so to the user) of "hidden" items, either for use elsewhere in my application or to reselect when the filter is cleared? Thanks!
you need not to add the selected grid separately. This can be done in one grid only. Simple method is to have an array variable in page scope and capture the grid select event or itemclick event whatever you want.
e.g if you use select event it will give you your record.
select( this, record, index, eOpts )
you can get your record id and push it to the array variable that you declared.
Once you filtered out the grid. you can loop through the filtered records and call select method by getting selection model.
e.g
grid.getSelectionModel().select(record);
Hope this will help.
How to send AJAX request for combo box by having condition that current store size is 0.
Basically I have two remote combo box which are cascaded and both are lazily loaded ( This behaviour cannot be changed)
I found even if the second combo box is cascaded based on events in first combobox upoun expansion it makes AJAX call to server ( Only first expansion though)
querymode local and autoload option will not work as it load the combo box on page load when store is created.
Given below is code snippet.
xtype: 'combo',id='firstcombo', name: 'DEFAULT_VALUE' ,minChars:2, value: decodeHtmlContent('') ,width:200 ,listWidth:500, resizable: true,
valueField : 'id', displayField: 'id', pageSize: 15,forceSelection:true, enableKeyEvents: true,
store: new Ext.data.Store({reader: new Ext.data.CFQueryReader({id: 'NAME',
fields:[{name:'id', mapping:'id'}]}),
fields: [{name:'id', mapping:'id'}],
url:'server/ajax/Params'
}
}),
listeners : {
select:function(combo, record, index) {
this.setRawValue(decodeHtmlContent(record.get('id')));
cascadeFields();
}
xtype: 'combo',id='secondcombo', name: 'DEFAULT_VALUE' ,minChars:2, value: decodeHtmlContent('') ,width:200 ,listWidth:500, resizable: true,
valueField : 'id', displayField: 'id', pageSize: 15,forceSelection:true, enableKeyEvents: true,
store: new Ext.data.Store({reader: new Ext.data.CFQueryReader({id: 'NAME',
fields:[{name:'id', mapping:'id'}]}),
fields: [{name:'id', mapping:'id'}],
url:'server/ajax/Params',
baseParam:{valuefirst:''}
},
listeners: {
beforeload: function(store, options) {
var value = Ext.getCmp('fistcombo').value;
Ext.apply(options.params, {
valuefirst:value
});
},
}),
listeners : {
select:function(combo, record, index) {
this.setRawValue(decodeHtmlContent(record.get('id')));
}
function cascadeFields()
{
var combo = Ext.getCmp('secondcombo');
if(combo.store)
{
var store = combo.store;
combo.setDisabled(true);
combo.clearValue('');
combo.store.removeAll();
combo.store.load();
combo.setDisabled(false);
}
}
To just extend your code you can do it Like so
listeners: {
beforeload: function(store, options) {
if (store.getCount() > 0) {
return false; // will abort the load operation.
}
var value = Ext.getCmp('fistcombo').value;
Ext.apply(options.params, {
valuefirst:value
});
}
}
This should work all the same in ExtJS 3.x and ExtJS4.x cause you didn't specified this. But I guess you are using 3.x
If this hangs the combo give me feedback cause there is still another but a bit more complex way.