I'm having an issue with a data-bind updating through a custom event that's called when a select element is changed. The idea is that the select element changes which updates the datatype parameter. Then the change event kicks in which updates sliderType which is a boolean and changes to true if a specific value is selected. That all works fine, however the div -data-bind="if: sliderType- does not update on a change, but it works on the page load.
Is there some way to force the page to recheck bindings?
Here is a fragment of the code to give you an idea of my structure.
HTML
<select data-bind="options: Items, optionsText: 'type', value: dataType, optionsCaption: 'Choose Data Item...', event: { change: dataTypeChange }"></select>
<div data-bind="if: sliderType">Here is a message. Astonishing.</div>
JavaScript
dataTypeChange: function() {this.sliderType = (this.dataType._latestValue.id == 6) ? true : false;
ko.observable being used:
{
dataItemId: -1,
name: ko.observable(res.Text),
dataType: ko.observable(res.DataType),
dataTypeChange: function() {
this.sliderType = (this.dataType._latestValue.id == 6) ? true : false;
},
sliderType: sliderActive
}
Stupid mistake on my part, it would help if the data-bind object was an observable
{
dataItemId: -1,
name: ko.observable(res.Text),
dataType: ko.observable(res.DataType),
dataTypeChange: function() {
this.sliderType = (this.dataType._latestValue.id == 6) ? true : false;
},
sliderType: ko.observable(sliderActive)
}
Related
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.
I'm trying to implement a select that calls a function even if the same option is selected twice. Following one of the answers on this thread, I've set the selectedIndex to -1 on focus. However, I'm still not getting the function call when the same option is selected twice.
var scaleSelect = new ComboBox({
id: "scale_select",
style: {width: "150px"},
name: "scale_select",
placeHolder: "Choisir une échelle",
store: scaleStore,
disabled: true,
onFocus: function() {
this.selectedIndex = -1;
console.log(this.selectedIndex); //-1
},
onChange: function(value){
mapScale = value;
window.myMap.setScale(mapScale);
}
}, "scale_select");
scaleSelect.startup();
UPDATE Attempting to set the selected index within onChange still doesn't call the function--wondering if this has to do with the fact that selected index is undefined onChange..
var scaleSelect = new ComboBox({
id: "scale_select",
style: {width: "150px"},
name: "scale_select",
placeHolder: "Choisir une échelle",
store: scaleStore,
disabled: true,
onChange: function(value){
mapScale = value;
window.myMap.setScale(mapScale);
var mySelect = document.getElementById("scale_select");
console.log(this.selectedIndex) //undefined
this.selectedIndex = -1;
mySelect.selectedIndex = -1;
console.log(this.selectedIndex); //-1
console.log(mySelect.selectedIndex); //-1
}
}, "scale_select");
scaleSelect.startup();
I tested every things that i knew with no success. I wondering about the thread that you linked and answers with upvotes! I the reason is that a selected option is not an option any more. But i have a suggestion:
Create your own custom Select
It is only a textbox and a div under that with some links line by line.
So I think the problem is that after you've picked a value in the select, it doesn't lose focus. So your onFocus handler won't get called unless the user actually clicks somewhere else on the page. If she doesn't do that, then the select's value won't fire the onChange if she selects the same value again.
So instead of setting the selected index on focus, why not do it in onChange, after you've handled the change? That way your select will be primed for another selection as soon as you're done treating the current one.
UPDATE
Ok, so looking at the Dojo code, this is the best I could come up with:
var scaleSelect = new ComboBox({
id: "scale_select",
style: {width: "150px"},
name: "scale_select",
placeHolder: "Choisir une échelle",
store: scaleStore,
disabled: true,
onChange: function(value){
if(value) { // the .reset() call below will fire the onChange handler again, but this time with a null value
mapScale = value;
window.myMap.setScale(mapScale);
console.log("Changed to", value, this);
this.reset(); // This is the magic here: reset so you are guaranteed to isseu an "onChange" the next time the user picks something
}
}
}, "scale_select");
scaleSelect.startup();
Note that you need to start the whole thing with no value - or else that initial value won't issue an "onChange" event, and every time you reset the widget, it'll go back to the initial value...
Hope this helps!
how to set isDirty() value dynamically in Ext Js
I have form panel which contains textbox, radio buttons and save button. In afterrender function i am setting a value to textbox . after loading the page the isDirty() is returning true, but my requirement is when I click on update button only it should return true.
how I can achieve this.
I tried with trackResetOnLoad= true but its not working.
Fiddle example is
https://fiddle.sencha.com/#fiddle/1d74
Update:-
I need track Change or update after Afterrender function.
You can achieve the same functionality by giving value config to textfield rather than setting anything in afterrender. When you run the below code , you will not get true in isDirty button when you first click it.
Ext.onReady(function() {
var form = new Ext.form.Panel({
itemId:'panelFormID',
trackResetOnLoad: true,
renderTo: Ext.getBody(),
items: [{
xtype: 'textfield'
,name: 'username'
,itemId : 'username'
,fieldLabel: 'username'
, value:'stackoverflow'
},{
xtype: 'textfield'
,name: 'password'
,itemId : 'password'
,fieldLabel: 'password'
}, {
xtype: 'button',
text : 'isDirty' ,handler: function() {
alert(Ext.ComponentQuery.query('#panelFormID')[0].isDirty());
}},
{
xtype: 'button',
text : 'Update' ,
handler: function() {
Ext.ComponentQuery.query('#username')[0].setValue ('resetvalue');
Ext.ComponentQuery.query('#password')[0].setValue ('restpassword');
}
}]
}
})
;
});
You can use form component's setValues method for this purpose. This couldn't change your dirty status while your form's trackResetOnLoad property is true.
In your case you can do it like that;
Ext.ComponentQuery.query('#panelFormID')[0].up('form').getForm().setValues({
username : 'reset value',
password : 'resetpassword'
});
These object properties related with your component's name property. When you miss some field's value it gonna set as empty. so you have to get all of it's value and change your wishes and set again.
Use the below code in your afterrender listener:
var items = Ext.ComponentQuery.query('#panelFormID') [0].getForm().getFields().items,
i = 0,
len = items.length;
for(; i < len; i++) {
var c = items[i];
if(c.mixins && c.mixins.field && typeof c.mixins.field['initValue'] == 'function') {
c.mixins.field.initValue.apply(c);
c.wasDirty = false;
}
}
isDirty() depends upon originalValue of respective component.In afterrender as you set value to field, you also need to set originalValue field.I have added this line in afterrender code:
afterrender :function () {
Ext.ComponentQuery.query('#username')[0].setValue ('stackoverflow');
//new line
Ext.ComponentQuery.query('#username')[0].originalValue='stackoverflow'
Ext.ComponentQuery.query('#panelFormID')[0].trackResetOnLoad = true;
}
Please try using this after setting values (in afterrender function):
Ext.ComponentQuery.query('#panelFormID')[0].form.setValues(Ext.ComponentQuery.query('#panelFormID')[0].form.getValues())
I have a select control bound to a Knockout observable array:
<select data-bind="event: { change: selectedProductOfferingChange }, options: $parent.productTypes, optionsText: 'text', optionsCaption: '-- Select --', value: selectedProductType, enable: !isReadOnly()"></select>
When the selection is changed, I want to run some code, perhaps make an AJAX call. If the change is not allowed, I want to cancel the change and display a modal dialog. I can't subscribe to the property as that will fire after the change has taken place. I would need the new value to determine if the change should be cancelled or not.
I tried the following in the viewmodel but the change is not cancelled though the property (selectedProductOffering) on the viewmodel is not updated:
self.selectedProductOfferingChange = function (data, event) {
event.stopImmediatePropagation();
return false;
};
Could I use the "beforeChange" option with subscribe?
self.selectedProductType.subscribe(function (previous) {
}, self, "beforeChange");
Can the change be cancelled here?
After some thought, here's what I came up with:
self.selectedProductOfferingChange = function (data, e) {
// Do any checking here
if (confirm("OK to make this change ?")) { return; }
// This stops the viewmodel property from being updated
e.stopImmediatePropagation();
// Since the viewmodel property hasn't changed, force the view to update
self.selectedProductType.valueHasMutated();
};
The problem with this code is that it doesn't give you access to the new value. A computed will solve this issue:
<select data-bind="options: $parent.productTypes, optionsText: 'text', optionsCaption: '-- Select --', value: computedSelectedProductType, enable: !isReadOnly()"></select>
self.computedSelectedProductType = ko.computed({
read: function () {
return self.selectedProductType();
},
write: function (value) {
// Do any checks here. If you want to revert to the previous
// value, don't call the following but do call:
// self.selectedProductType.valueHasMutated()
self.selectedProductType(value);
},
owner: self
});
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},
...