Ext JS: MultiSelect ComboBox beforedeselect behavior - extjs

I have a ComboBox that has multiSelect as true. This ComboBox has an initial value set, which is selected when I open the ComboBox. The problem becomes when I want to use ComboBox.setValue... using this function apparently fires off the beforedeselect event, but not the select event. And the odd thing is, beforedeselect is only fired for the values that I'm setting in setValue.
Please see this example.
To reproduce the issue, you can do the following:
Click the "Set ComboBox Value" button
Click the drop down
You should then see 4 alerts: Maryland, Pennsylvania, Maryland, Pennsylvania
or
Click the drop down
You should then see 2 alerts: Colorado, Colorado
Click the "Set ComboBox Value" button
You should see 1 alert: Colorado
Click the "Set ComboBox Value" button again
You should see 2 alerts: Maryland, Pennsylvania
Maybe I'm misunderstanding the event, but why is this the behavior? Why would using setValue deselect the states (that I'm setting) from the ComboBox, but still have them selected when I open the ComboBox? And why does the first test case show 4 alerts?
Update
Looking at the syncSelection code, it looks like deselectAll is called on the selection model, which would explain why the beforedeselect event is called, but the selection model then calls select... which doesn't explain why the selection event is not called.
Ext.onReady(function() {
var store = Ext.create('Ext.data.Store', {
fields: ['state_id', 'state_name'],
data: [
{state_id: 1, state_name: 'Colorado'},
{state_id: 2, state_name: 'Maryland'},
{state_id: 3, state_name: 'Pennsylvania'}
]
});
var combo = Ext.create('Ext.form.field.ComboBox', {
valueField: 'state_id',
displayField: 'state_name',
store: store,
multiSelect: true,
value: [1],
listeners: {
beforedeselect: function(combo, record, index, eOpts) {
alert('deselected: ' + record.get('state_name'));
},
beforeselect: function() {
alert('selected');
}
},
renderTo: Ext.getBody()
});
var button = Ext.create('Ext.button.Button', {
text: 'Set ComboBox Value',
listeners: {
click: function() {
combo.setValue([2, 3]);
}
},
renderTo: Ext.getBody()
});
});
Cross-post from the Sencha forums.

I've come up with a solution, but I'm unsure of the repercussions... so far, I haven't seen any. I override the comboBox's syncSelection method with the following:
syncSelection: function() {
var me = this,
picker = me.picker,
selection, selModel,
values = me.valueModels || [],
vLen = values.length, v, value;
if (picker) {
// From the value, find the Models that are in the store's current data
selection = [];
for (v = 0; v < vLen; v++) {
value = values[v];
if (value && value.isModel && me.store.indexOf(value) >= 0) {
selection.push(value);
}
}
// Update the selection to match
me.ignoreSelection++;
selModel = picker.getSelectionModel();
selModel.deselectAll(true); // This is the key to it all... suppressing the event listener, just like what's happening in the select right below this
if (selection.length) {
selModel.select(selection, undefined, true);
}
me.ignoreSelection--;
}
}
The only change is the deselectAll call... it's now suppressing the deselect event that's fired, just like the select event that follows it... I'm curious to know why they don't suppress the event like they do in the select. Like I said, I'm not sure of the repercussions, but it's given me great mileage thus far.

Related

ExtJS: Show/hide control on checkbox selectionchange

I have an ExtJS check-tree ExtJS Check Tree that I am trying to add some control to based on items checked/unchecked. It doesn't seem to fire correctly though.
Here is a Fiddle Example
When checkbox 'A' is selected, I want to hide the textfield, 'testValue', which works, but then if I unselect checkbox 'A', I want to show the textfield, 'testValue', which does not work.
For this test I am merely looking to see if the selections.selected.length === 0. However, when I unselect any of the checkboxes, the listener does not seem to be firing, since the alert message is not getting triggered - plus, if I then try to reselect the check box again it still does not fire.
I would use a selection Model (as outlined below) to achieve this (since I know it works), but then this places checkboxes on all my tree items when I just want to have the leaf nodes with checkboxes.
selModel: {
type: 'checkboxmodel',
listeners: {
selectionchange: 'onCheckedNodesChange'
}
}
Any suggestions would be most welcome!
EDIT
Adding allowDeselect: true and a listener for select and deselect sort of worked (I updated the Fiddle to exhibit the behavior):
selModel: {
allowDeselect: true,
listeners: {
deselect: function(model, record, index) {
text = record.get('text');
alert(text);
},
select: function(model, record, index) {
text = record.get('text');
alert(text);
}
}
},
I want to make sure that when 'A' is selected, the textfield remains hidden, but if you select another item in the list and then deselect it, the textfield returns.
I am trying to use the getChecked() method alone with when selectionchange event occurs. However, this only seems to return data when I do a submit (for example, on the Get checked nodes control). Any suggestions would be most welcome. This should not be so difficult.
For tree panel we have checkchange event it is similar to the selectionchange event.
http://docs.sencha.com/extjs/4.2.5/#!/api/Ext.tree.Panel-event-checkchange
checkchange( node, checked, eOpts )
Fires when a node with a checkbox's checked property changes
Parameters
node : Ext.data.TreeModel
The node who's checked property was changed.
checked : Boolean
The node's new checked state
eOpts : Object
The options object passed to Ext.util.Observable.addListener.
var fields = [
{
name: 'column'
},
{
name: 'leaf',
type: 'boolean'
},
{
name: 'checked',
type: 'boolean'
},
{
name: 'cls',
type: 'string',
defaultValue: 'x-tree-noicon'
},
];
this.dataModel = Ext.define('Filter-' + this.getId(), {
extend: 'Ext.data.Model',
fields: fields,
});
columns: [
{
xtype: 'treecolumn',
width: 200,
itemId: "filter",
dataIndex: 'column' ,
renderer: function (val, metaData, r) {
},
scope: this,
},
],
listeners: {
'checkchange': Ext.bind(function (node, checked,eOpts) {
},
scope: this
The checkboxes you are seeing are not part of the selection behaviour. Instead, they come from the checked configuration on the NodeInterface class.
Your tree panel is using the default selModel, which is row-based selection, with no deselect option. If you want the in-tree checks to control the selection, you'll need to configure that manually, probably by listening to change events from the store.
OTH, if all you care about is finding out which items are checked or not, you can use the getChecked() method on the TreePanel

Extjs : Selecting same value in multiselect combo not removing value

I am using ext5 multiselect combo. If we select a value from the dropdown, say 'Other' and click outside to cancel the dropdown.
Now the value 'Other' will be shown in combo.
Again click the dropdown and select tha same value 'Other', it should remove 'Other' from its values. But rather it adds same value once again.
My code is given below:
xtype: 'combo',
emptyText: 'Retail BI',
name: 'chainRefId',
store: Ext.create('shared.store.filter.ChainRefs'),
displayField: 'name',
multiSelect: true,
valueField: 'chain_ref_id',
listeners: {
expand: function(){
this.getStore().load();
},
change: function(combo, newVal) {
if (!combo.eventsSuspended) {
var storeCombo = Ext.ComponentQuery.query('combo[name=storeId]')[0];
storeCombo.getStore().removeAll();
storeCombo.setValue(null);
var chainCombo = Ext.ComponentQuery.query('combo[name=chainId]')[0];
chainCombo.getStore().removeAll();
chainCombo.setValue(null);
}
}
}
Is there a solution for this?
Thanks in advance.
Your combo's store gets reloaded on each expand. Technically the record corresponding to the value you selected the first time disappears on the second store load, so the removing logic does not "see" it and therefore leaves it in the field.
Remove this:
expand: function(){
this.getStore().load();
}
and just use autoLoad: true on the store.
I have faced the same problem. I have provided a workaround for this. This fix holds good for tagfield too.
//on combo store load event
load: function () {
// I am assuming reference to combo
var rawVal = combo.getValue();
// If combo is multiselct, getValue returns an array of selected items.
// When combo is configured as remote, everytime it loads with new records and therefore removes the old reference.
// Hence, do setValue to set the value again based on old reference.
combo.setValue(rawVal);
// Here you can see, based on old reference of selected items, the drop down will be highlighted.
}

Restrict RowEditor when CheckBox Column is clicked

I have a grid with a CheckBoxColumn and RowEditor plugin that is configured with clicksToEdit: 1
How can I prevent the RowEditor to open when the CheckBoxColumn is clicked? Cause I am not able to select more than one row at a time.
RowEditor have the 'beforeedit' event. The second parameter of this event is a edit event object - e.
Edit event object have a property 'cancel' - set it to true to cancel the edit or return false from your handler.
So, we can just set it in 'true' or 'false' to disable or enable the RowEditor:
{
xtype: 'checkbox',
fieldLabel: 'Disable row Editor',
listeners: {
change: function(cb) {
var editor = cb.up('grid').editingPlugin;
editor.on({
beforeedit: function(plugin, e) {
e.cancel = cb.checked;
}
});
}
}
}
Please, see live example on jsfiddle: http://jsfiddle.net/p7Vzu/

Ext.js Combox keydown triggers select event with no selection

I am trying to make a live search combo box and everything is working great except for one small detail. I want to call a search method as the user presses the down and up keys through the combo box. This does trigger a select event but the piker has no selection. When I select a combobox item with my mouse or by pressing enter the select event does get a selection. I want to launch queries using the value selected with the down and up keys while navigating the box.
Combo code
searchField = new Ext.form.ComboBox({
id:'searchField',
store: queryCacheStore,
pageSize:0,
width: 780,
triggerAction:'all',
typeAhead:false,
mode:'remote',
minChars:2,
forceSelection:false,
hideTrigger:true,
enableKeyEvents:true,
queryDelay:200,
queryMode:'remote',
queryParam:'query',
queryCaching:false,
displayField:'query',
autoSelect:false,
listConfig:{
loadingText: 'Searching...',
// Custom rendering template for each item
getInnerTpl: function() {
return '<div class="search-item">' +
'{query}' +
'</div>';
}
},
listeners: {
specialkey:function (field, e) {
if (e.getKey() == e.UP || e.getKey() == e.DOWN) {
}
},
select:function (combo, selection) {
var post = selection[0];
searchField.setValue(post.get('query'));
requestAccessList.runSearch();
},
focus:function (combo, event, opts) {
combo.store.proxy.extraParams = {
'lcm':true,
'type':RequestAccess.OBJECT_TYPE
}
}
}
});
So when
select:function (combo, selection) {
gets called with down arrow key or up arrow key then selection is null. When it gets called with enter key or mouse click it has the highlighted combobox selection. So the question is how can I get the value of the combo box from arrow key events?
OK I figured this out myself. You have to override the highlight event of the BoundListKeyNav
Ext.view.BoundListKeyNav.override({
highlightAt:function (index) {
// this.callOverridden(index); For some reason this does not work
var boundList = this.boundList,
item = boundList.all.item(index);
if (item) {
item = item.dom;
boundList.highlightItem(item);
boundList.getTargetEl().scrollChildIntoView(item, true);
searchField.setValue(boundList.getNode(index).textContent);
searchService.runSearch();
}
}
});
I added the following listConfig to a combobox to accomplish something similar:
listConfig: {
listeners: {
highlightitem: function(view, node, eOpts) {
combo.setValue(node.innerText);
}
}
}
It updates the combo box text field value on mouse over and key up/down.

Using PagingToolbar and CheckboxSelectionModel in single GridPanel

I've posted this over on the Sencha forums, wanted to also post it here just in case:
I have a GridPanel that utilizes a PagingToolbar and a CheckboxSelectionModel. I want to keep track of selections across pages. I'm nearly there, but I'm running into issues with the PagingToolbar controls (such as next page) firing a 'selectionchange' event on the my selection model.
Here's a simplified sample of my code:
Code:
var sm = Ext.create('Ext.selection.CheckboxModel', {
listeners:{
selectionchange: function(selectionModel, selectedRecords, options){
console.log("Selection Change!!");
// CODE HERE TO KEEP TRACK OF SELECTIONS/DESELECTIONS
}
}
});
var grid = Ext.create('Ext.grid.Panel', {
autoScroll:true,
store: store,
defaults: {
sortable:true
},
selModel: sm,
dockedItems: [{
xtype: 'pagingtoolbar',
store: store,
dock: 'bottom',
displayInfo: true
}],
listeners: {'beforerender' : {fn:function(){
store.load({params:params});
}}}
});
store.on('load', function() {
console.log('loading');
console.log(params);
console.log('selecting...');
var records = this.getNewRecords();
var recordsToSelect = getRecordsToSelect(records);
sm.select(recordsToSelect, true, true);
});
I assumed that I could select the records on the load event and not trigger any events.
What's happening here is that the selectionchange event is being triggered on changing the page of data and I don't want that to occur. Ideally, only user clicking would be tracked as 'selectionchange' events, not any other component's events bubbling up and triggering the event on my selection model. Looking at the source code, the only event I could see that fires on the PagingToolbar is 'change'. I was trying to follow how that is handled by the GridPanel, TablePanel, Gridview, etc, but I'm just not seeing the path of the event. Even then, I'm not sure how to suppress events from the PagingToolbar to the SelectionModel.
Thanks in advance,
Tom
I've managed to handle that. The key is to detect where page changes. Easiest solution is to set buffer for selection listener and check for Store.loading property.
Here is my implementation of selection model:
var selModel = Ext.create('Ext.selection.CheckboxModel', {
multipageSelection: {},
listeners:{
selectionchange: function(selectionModel, selectedRecords, options){
// do not change selection on page change
if (selectedRecords.length == 0 && this.store.loading == true && this.store.currentPage != this.page) {
return;
}
// remove selection on refresh
if (this.store.loading == true) {
this.multipageSelection = {};
return;
}
// remove old selection from this page
this.store.data.each(function(i) {
delete this.multipageSelection[i.id];
}, this);
// select records
Ext.each(selectedRecords, function(i) {
this.multipageSelection[i.id] = true;
}, this);
},
buffer: 5
},
restoreSelection: function() {
this.store.data.each(function(i) {
if (this.multipageSelection[i.id] == true) {
this.select(i, true, true);
}
}, this);
this.page = this.store.currentPage;
}
And additional binding to store is required:
store.on('load', grid.getSelectionModel().restoreSelection, grid.getSelectionModel());
Working sample: http://jsfiddle.net/pqVmb/
Lolo's solution is great but it seems that it doesn't work anymore with ExtJS 4.2.1.
Instead of 'selectionchange' use this:
deselect: function( selectionModel, record, index, eOpts ) {
delete this.multipageSelection[i.id];
},
select: function( selectionModel, record, index, eOpts ) {
this.multipageSelection[i.id] = true;
},
This is a solution for ExtJs5, utilizing MVVC create a local store named 'selectedObjects' in the View Model with the same model as the paged grid.
Add select and deselect listeners on the checkboxmodel. In these functions add or remove the selected or deselected record from this local store.
onCheckboxModelSelect: function(rowmodel, record, index, eOpts) {
// Add selected record to the view model store
this.getViewModel().getStore('selectedObjects').add(record);
},
onCheckboxModelDeselect: function(rowmodel, record, index, eOpts) {
// Remove selected record from the view model store
this.getViewModel().getStore('selectedObjects').remove(record);
},
On the pagingtoolbar, add a change listener to reselect previously seleted records when appear in the page.
onPagingtoolbarChange: function(pagingtoolbar, pageData, eOpts) {
// Select any records on the page that have been previously selected
var checkboxSelectionModel = this.lookupReference('grid').getSelectionModel(),
selectedObjects = this.getViewModel().getStore('selectedObjects').getRange();
// true, true params. keepselections if any and suppresses select event. Don't want infinite loop listeners.
checkboxSelectionModel.select(selectedObjects, true, true);
},
After whatever action is complete where these selections are no longer needed. Call deselectAll on the checkboxmodel and removeAll from the local store if it will not be a destroyed view. (Windows being closed, they default set to call destroy and will take care of local store data cleanup, if that is your case)

Resources