.preventDefault() is not working in conditionals - javascript

I have a bit of a problem. When a form is submitted, I wan't to check some things about it, and if something is wrong, I want to prevent it from submitting and then show an error on the client side. Everything seems to work fine except the form keeps submitting. It even shows the error on client side for a split second before it submits.
$('#register').on('submit', function (e) {
e.preventDefault();
var username = $("#register-username"),
name = $("#register-name"),
email = $("#register-email"),
password = $("#register-password"),
confirmPassword = $("#register-confirmPassword");
checkUsername(function (res) {
if (res) {
checkEmail(function (res) {
if (res) {
this.submit();
} else {
clearErrors();
email.toggleClass('input-error');
}
});
} else {
clearErrors();
username.toggleClass('input-error');
}
});
});
function checkEmail(callback) {
$.get("/checkEmail/" + $('#register-email').val(), function (data) {
if ( data == undefined ) {
callback(true);
} else {
callback(false);
}
});
}
function checkUsername (callback) {
$.get("/checkUsername/" + $('#register-username').val(), function (data) {
if ( data == undefined ) {
callback(true);
} else {
callback(false);
}
});
}
function clearErrors () {
var arr = [
$("#register-username"),
$("#register-name"),
$("#register-email"),
$("#register-password"),
$("#register-confirmPassword")
];
arr.forEach(function(el) {
el.removeClass('input-error');
});
}
Update:
Now I am just confusing myself. checkUsername() returns undefined from my server, I know for a fact, but somehow it is reaching the 'else' statement where checkUsername() is called. I've added the rest of my code. Should clear some confusion.

The call to preventDefault is made from the anonymous callback function you're passing to checkUsername. If the anonymous function is called asynchronously, then it's too late to cancel the event.

Assuming the problem is due to asynchronous code not shown, an effective way is to use preventDefault for the jQuery submit handler and use native submit when all validation passes
Something like:
$('#register').on('submit', function (e) {
e.preventDefault();// prevent jQuery submit
// after all validation passes
this.submit();// submit native method won't trigger jQuery handler again
})

You need to return false from validation to stop the current submitting event, and then manually send the post request on success from when the callbacks have successfully returned.
Since the callback is running after the validation has returned from the server then you can actually affect whether a request is made. Where as the other solutions involve trying to change how the original submit event occurs which is no longer in scope since you've already sent requests to the server at this point.
$('#register').on('submit', function (e) {
var username = $("#register-username"),
name = $("#register-name"),
email = $("#register-email"),
password = $("#register-password"),
confirmPassword = $("#register-confirmPassword");
checkUsername(function (res) {
if (res) {
checkEmail(function (res) {
if (res) {
$.post('{insert form action here}', $(this).serialize());
} else {
clearErrors();
email.toggleClass('input-error');
}
});
} else {
clearErrors();
username.toggleClass('input-error');
}
});
return false;
});
You'll have to replace the {insert form action here}, and $(this).action might work in it's place, but I'm not sure.

So I built off of charlietfl's solution and assigned the native form to a variable, and then submitted it within an anon function.
$('#register').on('submit', function (e) {
e.preventDefault()
var username = $("#register-username"),
name = $("#register-name"),
email = $("#register-email"),
password = $("#register-password"),
confirmPassword = $("#register-confirmPassword"),
form = document.getElementById('register');
$.get("/checkUsername/" + username.val(), function (data) {
if (data) {
clearErrors();
username.toggleClass('input-error');
} else {
$.get("/checkEmail/" + email.val(), function (data) {
if ( data ) {
clearErrors();
email.toggleClass('input-error');
} else {
form.submit();
}
});
}
});
});
This works.

Related

Javascript not executing after .then

I'm trying to put recaptcha v3 on a form but inserting the token into a hidden input field doesn't work - on the first submission.
Here is the code that I doctored a little. I added an alert and stopped the sumbit to see what's happening. This code is in a separate bundle.js file.
var form = document.querySelector('#contact-form');
var inputs = form.querySelectorAll('input');
$(form).submit(function(e) {
e.preventDefault();
grecaptcha.ready(function() {
grecaptcha.execute('secret_key', {
action: 'my_action'
}).then(function(token) {
document.getElementById('tokenField').value = token;
alert('token is; ' + token); // inserted for troubleshooting
});
});
removeMessageBox();
validation();
var alerts = document.querySelectorAll('.alert-text');
if (alerts.length == 0) {
// document.forms['contact-form'].submit();
}
});
With this code as written, the alert shows a valid token but tokenField has no value. tokenField gets the token AFTER you press OK on the alert. What is the problem??? I have tried everything.
If you take out the alert and uncomment submit, tokenField is empty on submission.
Note that this script also calls validation() and removeMessageBox(), which removes validation error messages.
If validation() stops the submission for some reason, you can fix the problem, submit again and tokenField gets it value and everything works great - the second time.
As far as I can tell, the field is being set inside of a callback, while the other functions are running in the main function. You can either put all of the code into the callback function for the .then function, or use an async function.
Callback
$(form).submit(function(e) {
e.preventDefault();
grecaptcha.ready(function() {
grecaptcha.execute('secret_key', {
action: 'my_action'
}).then(function(token) {
document.getElementById('tokenField').value = token;
removeMessageBox();
validation();
var alerts = document.querySelectorAll('.alert-text');
if (alerts.length == 0) {
// document.forms['contact-form'].submit();
}
});
});
});
Async
$(form).submit(function(e) {
e.preventDefault();
grecaptcha.ready(async function() {
const token = await grecaptcha.execute('secret_key', {
action: 'my_action'
});
document.getElementById('tokenField').value = token;
removeMessageBox();
validation();
var alerts = document.querySelectorAll('.alert-text');
if (alerts.length == 0) {
// document.forms['contact-form'].submit();
}
});
});

How to respect client side validation when making an ajax call

I wanted to make an ajax call, and then wait for the response from ajax call (Stop form submission till then) and then post the form based on the ajax response that I get. This question is based on Make ajax call and then submit form. So, after putting below script, everything works fine, but I lose the client side validation. Can I make an ajax call and still respect the Client Side validation?
#section scripts {
<script>
var canSubmit = false;
$('form').submit(function (e) {
if (!canSubmit) {
e.preventDefault();
var data = $('form').serialize();
var url = $(this).data('url');
$.ajax({
url: url,
type: 'POST',
data: data,
success: function (response) {
if (response.hasPreviousRequest) {
if (confirm("You've already applied for this job. Apply again?")) {
canSubmit = true;
$('form').submit();
}
}
else {
canSubmit = true;
$('form').submit();
}
},
error: function () {
alert("error");
}
});
}
});
</script>
}
P.S: Kindly note that as soon I as remove the ajax call Client side validation starts working fine. Can some one please explain me this behaviour?
You need to call the .valid() method on the form element (and if not, then cancel the ajax call)
$('form').submit(function (e) {
if (!$(this).valid()) {
return // exit
}
if (!canSubmit) {
....

issue with an asynchronous while in WinJS

I have an app which invokes a WebService (callPathsToMultiTiffWS) which have two possibilities:
complete = true
complete = false
in the case complete = false I want to show a dialog which notifies to user than webService failed and two buttons:
retry action (reinvoke WS)
Exit
this is my code so far:
callPathsToMultiTiffWS(UID_KEY[9], stringCapturePaths, UID_KEY[1], UID_KEY[2], UID_KEY[3], UID_KEY[4], UID_KEY[5], UID_KEY[6]).then(
function (complete) {
if (complete == true) {//if true, it stores the id of the picture to delete
Windows.UI.Popups.MessageDialog("WS executed successfully", "Info").showAsync().then(function (complete) {window.close();});
} else {
var messageDialogPopup = new Windows.UI.Popups.MessageDialog("An error occur while calling WS, retry??", "Info");
messageDialogPopup.commands.append(new Windows.UI.Popups.UICommand('Retry', function () { /*code for recall element*/ }));
messageDialogPopup.commands.append(new Windows.UI.Popups.UICommand('Exit', function () { /*code for exit*/ }));
messageDialogPopup.showAsync();
_divInput.innerHTML = "";
}
},
function (error) { console.log("function error"); });
This works good so far, but I want the recall feature working
so I thought to embedd my code inside a loop like this
var ban = true;
while (true) {
callPathsToMultiTiffWS(UID_KEY[9], stringCapturePaths, UID_KEY[1], UID_KEY[2], UID_KEY[3], UID_KEY[4], UID_KEY[5], UID_KEY[6]).then(
function (complete) {
if (complete == true) {//if true, it stores the id of the picture to delete
Windows.UI.Popups.MessageDialog("WS executed successfully", "Info").showAsync().then(function (complete) { window.close(); });
} else {
var messageDialogPopup = new Windows.UI.Popups.MessageDialog("An error occur while calling WS, retry??", "Info");
messageDialogPopup.commands.append(new Windows.UI.Popups.UICommand('Retry', function () { ban == true; }));
messageDialogPopup.commands.append(new Windows.UI.Popups.UICommand('Exit', function () { ban == false; }));
messageDialogPopup.showAsync().then(function (complete) {
console.log("no ps no");
});
}
},
function (error) { console.log("function error"); });
if (ban == false) break;
}
this loop executes the webService, but it doesn't wait for user interaction to trigger the webservice by touching one of the buttons, it is an endless loop with calls to my webService, how to fix this??
thanks in advance for the support
If I'm not missing something, it looks like the error is caused because your code isn't designed to run the next set of tasks after the asynchronous call to showAsync returns. Because the call to showAsync is non-blocking, the while loop will start over again and make another call to the Web service. And because THAT call (callPathsToMultiTiffWS) is also non-blocking, the loop will start over again, triggering another call to callPathsToMultiTiffWS. And over again, and again.
My recommendation is to break out the next call to the Web service so that it will only be triggered when the user makes a selection. If you separate your concerns (move the calls to the Web service into different function or module than the UI that informs the user of an issue), then you can probably fix this.
Kraig BrockSchmidt has a great blog post about the finer details of Promises:
http://blogs.msdn.com/b/windowsappdev/archive/2013/06/11/all-about-promises-for-windows-store-apps-written-in-javascript.aspx
-edit-
Here's some code that I wrote to try to demonstrate how you might accomplish what you're trying:
function tryWebServiceCall(/* args */) {
var url = "some Web service URL";
return new WinJS.xhr({ url: url }).then(
function (complete) {
if (complete) {
return new Windows.UI.Popups.MessageDialog("WS executed successfully", "Info").showAsync().then(
function () { /*do something */ });
} else {
var messageDialogPopup = new Windows.UI.Popups.MessageDialog("An error occur while calling WS, retry??", "Info");
messageDialogPopup.commands.append(new Windows.UI.Popups.UICommand('Retry', function () {
return tryWebServiceCall( /* args */);
}));
messageDialogPopup.commands.append(new Windows.UI.Popups.UICommand('Exit', function () { return; }));
return messageDialogPopup.showAsync();
}
});
}

jQuery $.post() request doesn't work unless ran twice

Here are two functions.
function getUserInformation(UserID) {
$.post("assets/scripts/chat/get_user_info.php", {
UserID: UserID
}, function (data) {
if (data == "error") {
alertBar('negative', 'There was an error sending the message');
}
window.username = data;
})
}
function CreateChatBox(UserID) {
if ($('#' + UserID).length == 0) {
getUserInformation(UserID);
alert(username);
}
My issue is, that when the CreateChatBox() function is executed, it has to be clicked twice in order for it to actually work. How ever if I remove the getUserInformation() function from the CreateChatBox() function the CreateChatBox() function executes successfully.
Could anybody help me with this issue? Thanks.
---Edit (extra Detail)----
When I click a link <a onclick = "CreateChatBox()">Some link</a> nothing happens. But when I click it the second time it does work. If I remove the function getUserInformation() from the CreateChatBox() function the CreateChatBox() function works first time when the link is clicked.
It is because the you are not waiting for ajax response to complete. When you click first time the ajax call is made through post and by second click the response is most likely available so you get it. You can see this putting alert inside the success handler.
function getUserInformation(UserID) {
$.post("assets/scripts/chat/get_user_info.php",
{
UserID: UserID
},
function(data){
if (data == "error") {
alertBar('negative','There was an error sending the message');
}
window.username = data;
alert(window.username);
});
}
function CreateChatBox(UserID) {
if ($('#'+UserID).length==0) {
getUserInformation(UserID);
}
//alert(username);
}
This is AJAX which means request is asynchronous. What you should do is to pass callback function as the second argument of your getUserInformation which will be invoked when data is available:
function getUserInformation(UserID, callback) {
$.post("assets/scripts/chat/get_user_info.php", {UserID: UserID}, function(data) {
if (data == "error") {
alertBar('negative', 'There was an error sending the message');
}
callback(data);
})
}
function CreateChatBox(UserID) {
if ($('#'+UserID).length == 0) {
getUserInformation(UserID, function(username) {
alert(username);
});
}
}

weird problem - change event fires only under debug in IE

I have form autocomplete code that executes when value changes in one textbox. It looks like this:
$('#myTextBoxId)').change(function () {
var caller = $(this);
var ajaxurl = '#Url.Action("Autocomplete", "Ajax")';
var postData = { myvalue: $(caller).val() }
executeAfterCurrentAjax(function () {
//alert("executing after ajax");
if ($(caller).valid()) {
//alert("field is valid");
$.ajax({ type: 'POST',
url: ajaxurl,
data: postData,
success: function (data) {
//some code that handles ajax call result to update form
}
});
}
});
});
As this form field (myTextBoxId) has remote validator, I have made this function:
function executeAfterCurrentAjax(callback) {
if (ajaxCounter > 0) {
setTimeout(function () { executeAfterCurrentAjax(callback); }, 100);
}
else {
callback();
}
}
This function enables me to execute this autocomplete call after remote validation has ended, resulting in autocomplete only when textbox has valid value. ajaxCounter variable is global, and its value is set in global ajax events:
$(document).ajaxStart(function () {
ajaxCounter++;
});
$(document).ajaxComplete(function () {
ajaxCounter--;
if (ajaxCounter <= 0) {
ajaxCounter = 0;
}
});
My problem is in IE (9), and it occurs only when I normally use my form. Problem is that function body inside executeAfterCurrentAjax(function () {...}); sometimes does not execute for some reason. If I uncomment any of two alerts, everything works every time, but if not, ajax call is most of the time not made (I checked this by debugging on server). If I open developer tools and try to capture network or debug javascript everything works as it should.
It seems that problem occurs when field loses focus in the same moment when remote validation request is complete. What I think it happens then is callback function in executeAfterCurrentAjaxCall is executed immediately, and in that moment jquery validation response is not finished yet, so $(caller).valid() returns false. I still do not know how alert("field is valid") helps in that scenario, and that could be sign that I'm wrong and something else is happening. However, changing executeAfterCurrentAjaxCall so it looks like this seems to solve my problem:
function executeAfterCurrentAjax(callback) {
if (ajaxCounter > 0) {
setTimeout(function () { executeAfterCurrentAjax(callback); }, 100);
}
else {
setTimeout(callback, 10);
}
}

Categories

Resources