Question
Action A triggers function B and function C concurrently(?!).
If B throws alert can I
prevent C from executing, perhaps, by using some kind of general listener function?
The solution I seek, does not involve adding specific listener to function C, it must remain generalised.
My Example
I have the following code below that gets triggered when a button within a modal is pressed. This deletes the contents until a GET method is processed and user is returned to the redirected page.
$( ".loadingEnabled" ).click(function( event ) {
$(".loadingSpinner").css("display","block");
$(".myModalContent").hide();
$(".modal-header").html("<b>Processing your request</b>");
$(".modal-footer").hide();
});
Within the modal I have just few simple validation scripts,e.g.,:
} else if ($("#name").val().length < 2){
event.preventDefault();
alert("You cannot proceed without entering your name")
}
So with the current set up, the user will trigger the loadingEnabled click function, however, if the data is not valid the loadingEnabled will trigger hide() and css() indefinitely as the submit dissapears and preventDefault() is triggered.
Question is, can I introduce something in loadingEnabled function that would prevent it from executing, if an alert is triggered?
I can think of workarounds. I am just wondering if there is anything for such a case.
a couple of suggestions.
You could return a boolean from your validation and make the loadingEnabled code conditional on that:
$( ".loadingEnabled" ).click(function( event ) {
if(validation()){
$(".loadingSpinner").css("display","block");
$(".myModalContent").hide();
$(".modal-header").html("<b>Processing your request</b>");
$(".modal-footer").hide();
}
});
then (in validation function) :
return true; // assuming that validation has passed
} else if ($("#name").val().length < 2){
event.preventDefault();
alert("You cannot proceed without entering your name")
return false;
}
or 2. You could invert your flow, calling the validation function on button click, name the other function and only call that if validation passes
I did not find a the solution I had in mind, however, the solution I implemented involved:
instantiating a new global variable validated to true
positioning loadingEnabled function after validation function in order for the system to execute validation first.
Added a line in each failed validation to set validated to false if input was not valid and if all validation passed set validated to true
Set a conditional if statement to only execute loadingEnabled if validation is true.
The above solution ensured that loadingEnabled can be re-used.
I've been stuck at watching a boolean variable. On my program I am setting the value of mutexSelect "true". Whenever the value is true, makeIntoSelect function must be called like below;
(here I want to declare to watch muteSelect value continuously){
if (mutexSelect == true) {
mutexSelect = false;
makeIntoSelect(newSelectedItem);
}
});
How can I check the value of mutexSelect continuously?
Thanks!
Well this solution is like using duct tape to fix something that really needs a replacement piece. The best solution would be to trigger a message when the variable is updated. Not seeing how the varaible gets updated, I can not give you a good solution here. But MDN Docs can show you how to trigger a custom event.
So what is your only other choice? Using an interval and checking to see if it has been flipped.
window.setInterval( function(){
if (mutexSelect == true) {
mutexSelect = false;
makeIntoSelect(newSelectedItem);
}
},10)
You can use jQuery setInterval function. This function will trigger continuously at given interval so that you can place your code inside that function.
jQuery SetInterval
setInterval(function(){
if (mutexSelect == true) {
mutexSelect = false;
makeIntoSelect(newSelectedItem);
}
}, 1000);
Update the call back time as you wish to get this function to trigger more frequently. (i.e) Change the value from 1000 to your desired value.
for iCheck plugin, is there a way to avoid "ifChanged" event handler to fire up when setting the checkbox from Javascript?
Old question, but I found a better method is to check the event.target.checked property and only run your code if it returns true. iCheck fires ifChanged twice - first for the un-checked option, then secondly for the checked option. So if you only run your code if event.target.checked === true, you will get the result you are after.
You could create a variable ignoreChange and when subscribing to the event handler, checking whether that variable is true and if it is, then set it to false and stop the function. If it is not true, then you can execute your normal code.
JS code:
var ignoreChange = false;
$('input').on('ifChanged', function(event){
if (ignoreChange) {
ignoreChange = false;
return;
}
// do stuff
});
// When changing the checkbox
ignoreChange = true;
Basically, whenever you set the variable ignoreChange to true, the next event call is ignored. This is quite a hacky workaround, however necessary, as I did not find a way to solve your problem trough the iCheck library.
Background
I've got asp.net webform with a grid, and when users update textboxes in that grid, the onchange event kicks off a WebMethod call and updates the rest of the changed row. Nothing is saved at that time -- we're just updating the UI.
To commit the changes, you click the save button.
This actually works reliably in almost every scenario. However, there is one very persistant one that it feels like I should be able to solve, but it's time to call in the specialists.
The Problem Scenario
I'm using jQuery to capture the enter key, and unfortunately that event fires first, causing the page to submit before the callback completes. The row is not updated correctly. Stale and bewildering data is saved.
Update
I don't think you can make the enter behavior depend on the callback, because you could save without changing a row. In that case, if you didn't change a row, it would never save.
Now if there was some way to inspect javascript's internal list of things to do, or maybe create my own and then manage it somehow, that would work. But that's some heavy lifting for something that should be easy. So unless an expert tells me otheriwse, I have to assume that's wrong.
Attempts
Right now I'm using the built-in jQuery events and I've got this elaborate setTimeout persisting the fact that a save was attempted, pausing long enough for the WebMethod to at least get called, and relying on the callback to do the submit. But it turns out javascript ansychrony doesn't work the way I hoped, and the onchange event doesn't even fire until that chunk of code completes. That was surprising.
I was thinking I could use my own little object to queue up these events in the right order and find a clever way to trigger that, etc.
This all seems like the wrong direction. Surely this is insane overkill, this is a common problem and I'm overlooking a simple solution because I don't work in javascript 24/7.
Right?
Code
Here's what I've got right this minute. This obviously doesn't work -- I was trying to take advantage of the async nature of jquery, but all of this apparently has to conclude before the row's onchange event event fires:
$(document).bind("keypress", function (e) {
if (e.keyCode == 13) {
handleEnter();
return false; //apparently I should be using e.preventDefault() here.
}
});
function handleEnter() {
setTimeout(function () {
if (recalculatingRow) { //recalculatingRow is a bit managed by the onchange code.
alert('recalculating...');
return true; //recur
}
//$('input[id$="ButtonSave"]').click();
alert('no longer recalculating. click!');
return false;
}, 1000);
}
And then a typical row looks like this. Note that I'm not using jquery to bind this:
<input name="ctl00$MainContent$GridOrderItems$ctl02$TextOrderItemDose" type="text" value="200.00" maxlength="7" id="ctl00_MainContent_GridOrderItems_ctl02_TextOrderItemDose" onchange="recalculateOrderItemRow(this);" style="width:50px;" />
I could post the code for recalculateOrderItemRow, but it's really long and right now the problem is that it doens't fire until the after keypress event concludes.
Update Dos
According to Nick Fitzgerald (and man is that a cool article) the use of setTimeout should cause this to become async. Digging further into interactions between setTimeout and jQuery, as well as interactions between normal javascript events and jQuery events.
Preventing ENTER shouldn't be causing you so much trouble! Make sure you have something like this on your code:
$(document).on('keydown', 'input', function(e) {
if(e.keyCode == 13) {
e.preventDefault();
}
});
UPDATE
It looks like you do want to save on ENTER, but only after the UI is updated on change. That is possible. You could use a flag a Matthew Blancarte suggested above, trigger save from the change callback, and get rid of the setTimeout.
But I wouldn't recommend that. You are better off relying solely on the save button for saving. If you don't, your users will have to wait for two async operations to complete before saving is finished. So you'd have to block the UI, or keep track of all async operations, aborting some as needed. I think it's not worthy, ENTER becomes less intuitive for the users if saving takes too long.
The hideous mass of workarounds below, which effectively took me all day today and half of yesterday to write, seems to solve every permutation.
The amusing thing is that enter itself doesn't trigger onchange, if you call e.preventDefault(). Why would it? The change doesn't actually happen until the default behavior of clicking the save button occurs.
Very little else about this is amusing.
//Used in handleEnter and GridOrderItems.js to handle a deferred an attempt to save by hitting enter (see handleEnter).
var isSaving = false;
var saveOnID = '';
//When one of the fields that trigger WebMethods get focus, we put the value in here
//so we can determine whether the field is dirty in handleEnter.
var originalVal = 0;
//These fields trigger callbacks. On focus, we need to save their state so we can
//determine if they're dirty in handleEnter().
$('[id$=TextOrderItemDose], [id$=TextOrderItemUnits]').live("focus", function() {
originalVal = this.value;
});
$(document).bind("keypress", function (e) {
if (e.keyCode == 13) { //enter pressed.
e.preventDefault();
handleEnter();
}
});
//Problem:
//In the products grid, TextOrderItemDose and TextOrderItemUnits both have js in their onchange events
//that trigger webmethod calls and use the results to update the row. Prsssing enter is supposed to
//save the form, but if you do it right after changing one of those text fields, the row doesn't always
//get updated due to the async nature of js's events. That leads to stale data being saved.
//Solution:
//First we capture Enter and prevent its default behaviors. From there, we check to see if one of our
//special boxes has focus. If so, we do some contortions to figure out if it's dirty, and use isSaving
//and saveOnID to defer the save operation until the callback returns.
//Otherwise, we save as normal.
function handleEnter() {
var focusedElement = $("[id$=TextOrderItemDose]:focus, [id$=TextOrderItemUnits]:focus")
//did we press enter with a field that triggers a callback selected?
if (isCallbackElement(focusedElement) && isElementDirty(focusedElement)) {
//Set details so that the callback can know that we're saving.
isSaving = true;
saveOnID = focusedElement.attr('id');
//Trigger blur to cause the callback, if there was a change. Then bring the focus right back.
focusedElement.trigger("change");
focusedElement.focus();
} else {
forceSave();
}
}
function isCallbackElement(element) {
return (element.length == 1);
}
function isElementDirty(element) {
if (element.length != 1)
return false;
return (element.val() != originalVal);
}
function forceSave() {
isSaving = false;
saveOnID = '';
$('input[id$="ButtonSave"]').click();
}
This gets called in the change event for the textboxes:
function recalculateOrderItemRow(textbox) {
//I'm hiding a lot of code that gathers and validates form data. There is a ton and it's not interesting.
//Call the WebMethod on the server to calculate the row. This will trigger a callback when complete.
PageMethods.RecalculateOrderItemRow($(textbox).attr('id'),
orderItemDose,
ProductItemSize,
orderItemUnits,
orderItemUnitPrice,
onRecalculateOrderItemRowComplete);
}
And then, at the end of the WebMethod callback code we pull the updated form values out, put the caret where it needs to be using jquery.caret, and check to see if we need to force a save:
function onRecalculateOrderItemRowComplete(result) {
var sender, row;
sender = $('input[id="' + result.Sender + '"]');
row = $(sender).closest('tr');
row.find('input[id$="TextOrderItemDose"]').val(result.Dose);
row.find('input[id$="TextOrderItemUnits"]').val(result.Units);
row.find('span[id$="SpanTotalPrice"]').html(formatCurrency(result.TotalPrice));
calculateGrandTotalPrice();
$(document.activeElement).select();
if (isSaving && saveOnID == result.Sender) {
forceSave();
}
}
result.Sender is the ID of the calling control, which I stuffed into the WebMethod call and then returned. saveOnID may not be perfect, and it might actually be even better to maintain a counter of active/uncallback-ed WebMethod calls to be totally sure that everything wraps up before save. Whew.
Can you post your javascript? Sounds like you're on the right track. I would change my OnChange events to increment a variable before making the AJAX call. I'll call the variable inProcess and initialize it to zero. When the AJAX call comes back, I would update the inProcess to the current value minus one. On the Enter key event, I would check to that inProcess equals zero. If not, you could either warn the user or set a timeout to try again in a bit.
You could unbind the Enter key capture while you are in the onChange event, then rebind it at the end of the callback function. If you post some code, I could give a more specific answer.
It sounds like you shouldn't be calling the WebMethod asynchronously. Call it synchronously, and on success, save your data.
I'm using this jQuery plug-in for throttle support.
I have a text input variable called textbox set as follows, which will execute someFunction at most once every second:
textbox.keypress($.throttle(1000, someFunction(e)));
However, I want someFunction to execute immediately if the key pressed is the enter key. I'm not quite sure how to do this. Any suggestions?
textbox.keypress(function(event) {
if(event.which == 13){
// Enter key has been preseed. Do something.
} else {
$.throttle(1000, function(){
someFunction(event);
})
}
});
Keep in mind what you're passing for callbacks. Both 'someFunction' and '$.throttle' are being invoked directly, with their return values being passed as callbacks. To avoid this, wrap the functions in a function (lambda) as I have done above.
You will need to have in your code a handler for the 'ENTER' keypress and abort the $.throttle if that is the case. Can you paste more of your code maybe to give us a better idea of what to work with?