Binding an event to an add dialog select input in jqgrid - javascript

I have a jqgrid with the add dialog enabled for adding new rows. The way I would like it to work is that the user will select from a list of drop down items and the item chosen will cause a second drop-down to be populated with data based on the first item.
For example, if my grid had two columns, one for country and one for state, when the user clicked the add button, the country input would be a drop-down, dynamically populated with countries by an ajax call. Then, when the user selects a country, the state drop-down is populated based on the country selected.
Currently I am doing something like the following:
beforeProcessing: function () {
var allcountries = ajaxcall();
$('#clientReportsGrid').setColProp('Countries', { editoptions: { value: allcountries, class: 'edit-select' }, editrules: { required: true, edithidden: true} });
},
loadComplete: function () {
$('#Countries').change(function () {
// States will be populated here
alert("changed");
});
}
The first part in beforeProcessing works fine and the countries drop-down is populated as expected. However, the event in loadComplete does not get attached to the select input with id the 'Countries' and the alert never occurs. It seems that the select object has not yet been created with loadComplete fires, but if that is the case I'm not sure where to place the logic where the states will be populated.
Any ideas?

jqGrid has no direct support of depended selects, but in the answer you will find the implementation of the scenario. The most problem is that the code is not small, but it's quickly to analyse a working code as to write your own one.

I ended up doing something like the following, its a bit redundant but it works and isn't too code heavy:
First, in the beforeProcessing callback, I populate both the countries and states drop-downs with their initial values:
beforeProcessing: function () {
var allcountries = ajaxCallToFetchCounties();
$('#clientReportsGrid').setColProp('Countries', { editoptions: { value: allcountries, class: 'edit-select' }, editrules: { required: true, edithidden: true} });
var states = ajaxCallToFetchStates();
$('#clientReportsGrid').setColProp('States', { editoptions: { value: states , class: 'edit-select' }, editrules: { required: true, edithidden: true} });
}
Then in the pager's add option, I used the beforeShowForm callback to attach a method to the change event of the countries select input, and within that method I fetch the states based on the current country and repopulate the select control:
beforeShowForm: function (form) {
$("#Countries").unbind("change").bind("change", function () {
var states = ajaxCallToFetchStates();
//Manually clear and re-populate the states select box here with the new list of states.
});
$('#tr_AccountCode', form).show();
}

Related

I want to update options of another select2 multiselect component based on selected value of first select2 component

I want to update options of another select2 multiselect component based on selected value of first select2 component
as you can see approach that I tried below, but its not accurate, how can I get to know whats the value that got selected instead of retrieving all selected values of multiselect
$('#id_old_states').on("change", function (){
let name = $('#id_old_states').select2('data')[0]["text"]
let id = $('#id_old_states').select2('data')[0]["id"]
$('#id_new_states').select2({
theme: "classic",
multiple: true,
placeholder: "Select States",
}).prepend(new Option(name, id, false, false))
});
$('#mySelect2').on('select2:select', function (e) {
var data = e.params.data;
console.log(data);
});

Dynamically populating ACF field works with select - but not with select2

I have a select field $("#country") in a ACF form on Wordpress backend which triggers the values for a second select field $("#city"). It's a typical Country/City relationship with custom values.
The script is enqueued, the change event is triggered and the second select gets populated with success using this code:
var cities = {
"Spain": [
"Madrid",
"Barcelona"
],
"Portugal": [
"Lisboa",
"Oporto"
]
}
jQuery(document).ready(function($) {
$('#country').change(function () {
loadCities($(this).find("option:selected").val());
}).change();
});
function loadCities(country) {
$('#city').empty();
cities[country].forEach(function(city) {
$('#city').append("<option value='"+city+"'>"+city+"</option>");
});
}
As the list of cities is quite long, I want to use select2 instead of select. Here the changed loadCities function:
function loadCities(country) {
$('#city').empty();
var data = [{id: "", text: ""}];
cities[country].forEach(function(city) {
data.push({id: city, text: city});
});
$('#city').select2({
data: data,
placeholder: "Select a city"
}).trigger('change');
}
The problem is that the options (=cities) don't show up as expected in the select2 list. But they seem to be appended, checking their existence with jquery shows them.
First thing you can dynamically append options using the data attribute even for a multi dimensional json array of objects. But do not initialize the select box twice!
$("#country").select2({ data: data.keys });
Instead destroy it and init with your values, you can also try with change event.
$("#country").select2('destroy').empty().select2({ data: data.keys });
Let me know if this helps.

ExtJS : Re-selecting the same value does not fire the select event

Normally, when you select an item in a combobox, you would expect it to fire the select event. However, if you try to select an item that was already selected, the select event is not fired. That is the "normal" behavior of an ExtJs combobox.
I have a specific need for an ExtJS combobox: I need it to fire the select event even if I re-select the same value. But I cannot get it to work. Any help would be much appreciated!
Example here: https://fiddle.sencha.com/#view/editor&fiddle/2n11
Open the dev tools to see when the select event is fired.
I'm using ExtJS Classic 6.6.0.
Edit: I answered my own question and updated the Fiddle with working solution.
try to look at this:
ExtJS 4 Combobox event for selecting selected value
Its for earlier ExtJS version, but catching click event for itemlist may help you out too..
I found the culprit: it all happens in the SelectionModel of the combobox BoundList, in the method doSingleSelect.
So if we extend Ext.Selection.DataViewModel and Ext.form.field.ComboBox, we can force the select event to be fired every time.
Ext.define( "MyApp.selection.DataViewModelExt", {
"extend": "Ext.selection.DataViewModel",
"alias": "selection.dataviewmodelext",
"doSingleSelect": function(record, suppressEvent) {
var me = this,
changed = false,
selected = me.selected,
commit;
if (me.locked) {
return;
}
// already selected.
// should we also check beforeselect?
/*
if (me.isSelected(record)) {
return;
}
*/
commit = function() {
// Deselect previous selection.
if (selected.getCount()) {
me.suspendChanges();
var result = me.deselectDuringSelect([record], suppressEvent);
if (me.destroyed) {
return;
}
me.resumeChanges();
if (result[0]) {
// Means deselection failed, so abort
return false;
}
}
me.lastSelected = record;
if (!selected.getCount()) {
me.selectionStart = record;
}
selected.add(record);
changed = true;
};
me.onSelectChange(record, true, suppressEvent, commit);
if (changed && !me.destroyed) {
me.maybeFireSelectionChange(!suppressEvent);
}
}
});
We also must extend the combobox to force using our extended DataViewModel. The only thing to change is the onBindStore method where it instancies the DataViewModel:
Ext.define( "MyApp.form.field.ComboBoxEx", {
"extend": "Ext.form.field.ComboBox",
"alias": "widget.comboboxex",
"onBindStore": function(store, initial) {
var me = this,
picker = me.picker,
extraKeySpec,
valueCollectionConfig;
// We're being bound, not unbound...
if (store) {
// If store was created from a 2 dimensional array with generated field names 'field1' and 'field2'
if (store.autoCreated) {
me.queryMode = 'local';
me.valueField = me.displayField = 'field1';
if (!store.expanded) {
me.displayField = 'field2';
}
// displayTpl config will need regenerating with the autogenerated displayField name 'field1'
if (me.getDisplayTpl().auto) {
me.setDisplayTpl(null);
}
}
if (!Ext.isDefined(me.valueField)) {
me.valueField = me.displayField;
}
// Add a byValue index to the store so that we can efficiently look up records by the value field
// when setValue passes string value(s).
// The two indices (Ext.util.CollectionKeys) are configured unique: false, so that if duplicate keys
// are found, they are all returned by the get call.
// This is so that findByText and findByValue are able to return the *FIRST* matching value. By default,
// if unique is true, CollectionKey keeps the *last* matching value.
extraKeySpec = {
byValue: {
rootProperty: 'data',
unique: false
}
};
extraKeySpec.byValue.property = me.valueField;
store.setExtraKeys(extraKeySpec);
if (me.displayField === me.valueField) {
store.byText = store.byValue;
} else {
extraKeySpec.byText = {
rootProperty: 'data',
unique: false
};
extraKeySpec.byText.property = me.displayField;
store.setExtraKeys(extraKeySpec);
}
// We hold a collection of the values which have been selected, keyed by this field's valueField.
// This collection also functions as the selected items collection for the BoundList's selection model
valueCollectionConfig = {
rootProperty: 'data',
extraKeys: {
byInternalId: {
property: 'internalId'
},
byValue: {
property: me.valueField,
rootProperty: 'data'
}
},
// Whenever this collection is changed by anyone, whether by this field adding to it,
// or the BoundList operating, we must refresh our value.
listeners: {
beginupdate: me.onValueCollectionBeginUpdate,
endupdate: me.onValueCollectionEndUpdate,
scope: me
}
};
// This becomes our collection of selected records for the Field.
me.valueCollection = new Ext.util.Collection(valueCollectionConfig);
// This is the selection model we configure into the dropdown BoundList.
// We use the selected Collection as our value collection and the basis
// for rendering the tag list.
//me.pickerSelectionModel = new Ext.selection.DataViewModel({
me.pickerSelectionModel = new MyApp.selection.DataViewModelExt({
mode: me.multiSelect ? 'SIMPLE' : 'SINGLE',
// There are situations when a row is selected on mousedown but then the mouse is dragged to another row
// and released. In these situations, the event target for the click event won't be the row where the mouse
// was released but the boundview. The view will then determine that it should fire a container click, and
// the DataViewModel will then deselect all prior selections. Setting `deselectOnContainerClick` here will
// prevent the model from deselecting.
ordered: true,
deselectOnContainerClick: false,
enableInitialSelection: false,
pruneRemoved: false,
selected: me.valueCollection,
store: store,
listeners: {
scope: me,
lastselectedchanged: me.updateBindSelection
}
});
if (!initial) {
me.resetToDefault();
}
if (picker) {
me.pickerSelectionModel.on({
scope: me,
beforeselect: me.onBeforeSelect,
beforedeselect: me.onBeforeDeselect
});
picker.setSelectionModel(me.pickerSelectionModel);
if (picker.getStore() !== store) {
picker.bindStore(store);
}
}
}
}
});
Then just use the extended combobox in your app. By doing that, the select event will be fired every time.

ExtJS: How to customise the combobox picker's keynav?

A combobox has a picker (a boundlist instance) which itself has a keynav (BoundListKeyNav).
How can I modify / customise this keynav instance?
Basically, by default it contains bindings for home / end. While this would be useful under normal circumstances, it is not when using a customised combobox. I want my home / end keys to function correctly, as they do before ext decides to hijack them (go to start / end of input contents).
Ideally, I want to do this in the configuration object of the combobx, like so:
{
xtype: 'combobox',
itemId: 'search',
emptyText: 'Search',
editable: true,
typeAhead: false,
hideTrigger: true,
queryMode: 'local',
minChars: 3,
displayField: 'name',
valueField: 'search'
}
It is made to behave in such a way that you can type anything in (to search) but can also choose auto completed searches.
The keynav lives at combo.listKeyNav, but the chunk of code which sets this up in ext fires no events to let us jump in and change it. It appears the combo has no configuration for such a thing either (seeing as the function setting listKeyNav doesn't take any config from our combo object).
FYI
It is the BoundListKeyNav which has these bindings hard coded. The combobox's onExpand creates the instance (taking no config anywhere, allowing for no customisation).
The only way is to override onExpand method of combo.
As Saki wrote customizing of the key navigation is only possible with overriding the onExpand method of the combobox - basically duplicating the original implementation (in case of ExtJS4).
For example:
onExpand: function() {
var me = this,
keyNav = me.listKeyNav,
selectOnTab = me.selectOnTab,
picker = me.getPicker();
// Handle BoundList navigation from the input field. Insert a tab listener specially to enable selectOnTab.
if (keyNav) {
keyNav.enable();
} else {
keyNav = me.listKeyNav = new Ext.view.BoundListKeyNav(me.inputEl, {
boundList: picker,
forceKeyDown: true,
tab: function(e) {
if (selectOnTab) {
this.selectHighlighted(e);
me.triggerBlur();
}
// Tab key event is allowed to propagate to field
return true;
},
enter: function(e) {
var selModel = picker.getSelectionModel(),
count = selModel.getCount();
this.selectHighlighted(e);
// Handle the case where the highlighted item is already selected
// In this case, the change event won't fire, so just collapse
if (!me.multiSelect && count === selModel.getCount()) {
me.collapse();
}
},
home: {
fn: Ext.emptyFn,
defaultEventAction: false
},
end: {
fn: Ext.emptyFn,
defaultEventAction: false
}
});
}
// While list is expanded, stop tab monitoring from Ext.form.field.Trigger so it doesn't short-circuit selectOnTab
if (selectOnTab) {
me.ignoreMonitorTab = true;
}
Ext.defer(keyNav.enable, 1, keyNav); //wait a bit so it doesn't react to the down arrow opening the picker
me.inputEl.focus();
}

jqGrid with an editable checkbox column

When using jqGrid how do you force a cell to load in its editable view on page load as well as when it is clicked?
If you set up 'cell editing' like below, the check box only appears when you click on the cell.
{ name: 'MyCol', index: 'MyCol', editable:true, edittype:'checkbox', editoptions: { value:"True:False" },
cellEdit:true,
Also on clicking checkbox, is there a way of sending a AJAX post to server instantly rather than having to rely on the user pressing enter?
To allow the checkboxes to always be click-able, use the checkbox formatter's disabled property:
{ name: 'MyCol', index: 'MyCol',
editable:true, edittype:'checkbox', editoptions: { value:"True:False"},
formatter: "checkbox", formatoptions: {disabled : false} , ...
To answer your second question, you will have to setup an event handler for the checkboxes, such that when one is clicked a function is called to, for example, send an AJAX POST to the server. Here is some example code to get you started. You can add this to the loadComplete event:
// Assuming check box is your only input field:
jQuery(".jqgrow td input").each(function(){
jQuery(this).click(function(){
// POST your data here...
});
});
This is an old one but has a lot of view so I decided to add my solution here too.
I'm making use of the .delegate function of JQuery to create a late binding implementation that will free you from the obligation of using the loadComplete event.
Just add the following:
$(document).delegate('#myGrid .jqgrow td input', 'click', function () { alert('aaa'); });
This will late bind that handler to every checkbox that's on the grid rows.
You may have a problem here if you have more than one checkbox column.
I had the same problem and I suppose that I found a good solution to handle checkbox click immediately. The main idea is to trigger editCell method when user clicks on the non-editable checkbox. Here is the code:
jQuery(".jqgrow td").find("input:checkbox").live('click', function(){
var iRow = $("#grid").getInd($(this).parent('td').parent('tr').attr('id'));
var iCol = $(this).parent('td').parent('tr').find('td').index($(this).parent('td'));
//I use edit-cell class to differ editable and non-editable checkbox
if(!$(this).parent('td').hasClass('edit-cell')){
//remove "checked" from non-editable checkbox
$(this).attr('checked',!($(this).attr('checked')));
jQuery("#grid").editCell(iRow,iCol,true);
}
});
Except this, you should define events for your grid:
afterEditCell: function(rowid, cellname, value, iRow, iCol){
//I use cellname, but possibly you need to apply it for each checkbox
if(cellname == 'locked'){
//add "checked" to editable checkbox
$("#grid").find('tr:eq('+iRow+') td:eq('+iCol+') input:checkbox').attr('checked',!($("#regions").find('tr:eq('+iRow+') td:eq('+iCol+') input:checkbox').attr('checked')));
//trigger request
jQuery("#grid").saveCell(iRow,iCol);
}
},
afterSaveCell: function(rowid, cellname, value, iRow, iCol){
if(cellname == 'locked'){
$("#grid").find('tr:eq('+iRow+') td:eq('+iCol+')').removeClass('edit-cell');
}
},
Then your checkbox will send edit requests every time when user clicks on it.
I have one submit function that sends all grid rows to webserver.
I resolved this problem using this code:
var checkboxFix = [];
$("#jqTable td[aria-describedby='columnId'] input").each(function () {
checkboxFix.push($(this).attr('checked'));
});
Then I mixed with values got from the code below.
$("#jqTable").jqGrid('getGridParam', 'data');
I hope it helps someone.
I had shared a full code at the link below, you can take a look if you need it.
http://www.trirand.com/blog/?page_id=393/bugs/celledit-checkbox-needs-an-enter-pressed-for-saving-state/#p23968
Better solution:
<script type="text/javascript">
var boxUnformat = function ( cellvalue, options, cell ) { return '-1'; },
checkboxTemplate = {width:40, editable:true,
edittype: "checkbox", align: "center", unformat: boxUnformat,
formatter: "checkbox", editoptions: {"value": "Yes:No"},
formatoptions: { disabled: false }};
jQuery(document).ready(function($) {
$(document).on('change', 'input[type="checkbox"]', function(e){
var td = $(this).parent(), tr = $(td).parent(),
checked = $(this).attr('checked'),
ids = td.attr('aria-describedby').split('_'),
grid = $('#'+ids[0]),
iRow = grid.getInd(tr.attr('id'));
iCol = tr.find('td').index(td);
grid.editCell(iRow,iCol,true);
$('input[type="checkbox"]',td).attr('checked',!checked);
grid.saveCell(iRow,iCol);
});
});
</script>
In your colModel:
...
{name:'allowAccess', template: checkboxTemplate},
...

Categories