I have a form of which I catch the submit event. I have to wait for a $.post call in order to decide whether to submit it. I've tried this:
$('#my_form').submit(function(event){
var data = null;
$.post("/post/call").then(function(_data){
data = _data;
});
if ( $.isPlainObject(data) && data.status === 'ko' ) {
return false; // Do not submit the original form
} else {
// Submit the original form
}
});
But data results to be always null, also if _data is valorized, because $.post is an asynchrounous call. How can I wait for the $.post call to be completed?
EDIT: As wrote in the comments, I would like to avoid $.ajax({async: true}), because it freezes the page; is there any way to get the same effect without freezing the page?
You should have some sort of flag that says the form is to be submitted or not as well as that the ajax is processing. Then when the ajax call passes, call $('#my_form').submit() to send the form.
Loosely,
var validated = false;
$('#my_form').submit(function(event){
if (validated) {
return true;
}
$.post("/post/call").then(function(_data){
var data = _data;
if ( $.isPlainObject(data) && data.status === 'ko' ) {
// Do not submit the original form
} else {
validated = true;
$('#my_form').submit();
}
});
return false;
});
If you are using MVC / C#, and can use a Json request to access your web service, know the following.
$.getJSON
Is asynchronous
SOLUTION: $.ajax
Can be set to synchronous
Related
I'm stuck in a really bizarre situation here. It's complicated to explain but I'll try my best.
I have a UI with 4 navigation <a> buttons on top - in the center there's always a form - and at the bottom I have Previous & Next buttons.
Forms are constructed in MVC using Ajax.BeginForm
For each Nav link <a> element on top, I have a JavaScript function
var LoadTabs = function (e, arg) {
// This is to validate a form if one of the top links is clicked and form has incomplete fields...
if (arg !== "prev" && arg !== "next") {
if (!window.ValidateForm(false)) return false;
}
var url = $(this).attr('data'); // this contains link to a GET action method
if (typeof url != "undefined") {
$.ajax(url, { context: { param: arg } }).done(function (data) {
$('#partialViewContainer').html(data);
});
}
}
This function above binds to each top link on page load.
$('.navLinks').on('click', LoadTabs);
My Next & Previous buttons basically trigger the click event i.e. LoadTabs function.
$('button').on('click', function () {
if (this.id === "btnMoveToNextTab") {
if (!window.ValidateForm(true)) return false;
$.ajax({
url: url,
context: { param: 'next' },
method: "GET",
data: data,
success: function(response) {
if (typeof response == 'object') {
if (response.moveAhead) {
MoveNext();
}
} else {
$('#mainView').html(response);
}
ScrollUp(0);
}
});
}
if (this.id === "btnMoveToPreviousTab") {
MoveBack();
}
return false;
});
MoveNext() Implementation is as below:
function MoveNext() {
var listItem = $('#progressbarInd > .active').next('li');
listItem.find('.navLink').trigger('click', ['next']);
ScrollUp(0);
}
The problem is, for some reasons, when Nav Link 3 is active and I hit the NEXT button - Instead of posting the form first via form.submit() - the nav 4 gets triggered - hence GET for nav 4 runs before form POST of nav 3.
My ValidateForm method is basically just checking if the form exists and is valid then Submit, else returns false. It's as below:
function ValidateForm(submit) {
var form = $('form');
// if form doesn't exist on the page - return true and continue
if (typeof form[0] === "undefined") return true;
// now check for any validation errors
if (submit) {
if (!$(form).valid()) {
return false;
} else {
$(form).submit();
}
}
else {
return true;
}
return true;
}
My speculation is that form.submit does get triggered as it should be but since submit takes a little longer to finish it continues with the next code block in the button onclick event.
I first thought that this is C# issue as in the POST I'm saving a big chunk of data with a few loops, and any code block that's process heavy I have that part in
var saveTask = Task.Factory.StartNew(() => ControllerHelper.SomeMethod(db, model));
Task.WaitAll(saveTask);
WaitAll will wait and pause the execution until SomeMethod finishes executing. I'm not sure how can I lock a process in javascript and wait for it to finish execution. Because I think If i can somehow lock the form.submit() in ValidateForm until its finished processing .. via a callback method perhaps...
Please if anyone can put me in right direction, I'd greatly appreciate the help. If you need more information please let me know I'd be happy to provide!
I'm posting this is a note to Alan since it's multi-line code and I can't make it readable in a comment. To avoid the promise anti-pattern when you sometimes run an async operation and sometimes don't, one can use this:
function ValidateForm(){
// do your logic
if (pass) {
return $.post("urlToPost");
else {
return $.Deferred().reject(xxx).promise();
}
}
This way, you're always returning a promise. In one case, the promise comes from $.ajax(). In the other case, you're just returning a rejected promise since the operation has already failed.
Edit regard to return promise anti pattern
as jfriend00 commented the way original answer is returning the promise is anti pattern, better way would be:
function ValidateForm(){
// do your logic
if (pass) {
return $.post("urlToPost");
else {
return $.Deferred().reject(xxx).promise();
}
}
more info on this, checkout https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns
original answer
I will try picture your problem:
what you want is run ValidateForm first, when it passess (such that when form post is done), then do MoveNext
your problem is, MoveNext is called before ValidateForm has finished.
If I am correct, then the reason is because Javascript by nature is async programing, hence use callback to fire when something has done.
So in your situation, you need to use promise to acheive it.
essentialy what you need to do is:
let ValiateForm returns promise
only execute MoveNext once the ValidateForm has finish
So code would be like:
function ValiateForm(){
var dfd = jQuery.Deferred();
// do your logic
if(pass){
$.post("urlToPost"
} else {
// also failed
dfd.reject();
}
// return the promise
return dfd.promise();
}
then your move next button would be like
$('button').on('click', function () {
if (this.id === "btnMoveToNextTab") {
$.when(window.ValidateForm(true)).then(function(){
// after success post form
$.ajax({
url: url,
context: { param: 'next' },
method: "GET",
data: data,
success: function(response) {
if (typeof response == 'object') {
if (response.moveAhead) {
MoveNext();
}
} else {
$('#mainView').html(response);
}
ScrollUp(0);
}
});
if (this.id === "btnMoveToPreviousTab") {
MoveBack();
}
}
return false;
});
});
more info on promise checkout
https://api.jquery.com/deferred.promise/
and for run when finish
https://api.jquery.com/deferred.then/
This is the block that checks if returned value = 1:
} else if (validateUsername(username.val()) == 1) {
errors.html("That username already exists! Please use another.<span id='close'>X</span>");
errors.fadeIn("slow");
username.css("border", "solid 2px red");
}
If yes, username exists..
Now let's do the ajax work.
function validateUsername(username) {
$.post("js/ajax/ajax.php", { validateUsername : username }, function(data) {
return data;
});
}
This will send a request to ajax, and fill var data with the response.
if (isset($_POST['validateUsername']))
{
echo 1;
}
this is the ajax that will send a response, basically it's int 1 for now, for checking.
But, I always get undefined.
I mean, fireBug does say "request=1", but it looks like the if statement won't work.
If I just do return 1; manually without ajax, it will work.
Why is it doing that?
Ajax is asynchronous, so when you return data here :
function validateUsername(username) {
$.post("js/ajax/ajax.php", { validateUsername : username }, function(data) {
return data;
});
}
and try getting it here :
validateUsername(username.val()) == 1
it won't work, as the ajax call hasn't completed yet (and the return returns from the inner function only).
Instead you should do:
function validateUsername(username) {
return $.post("js/ajax/ajax.php", { validateUsername : username });
}
validateUsername(username.val()).done(function(data) {
if ($.trim(data) === 1) {
errors.html("That username already exists! Please use another.<span id='close'>X</span>");
errors.fadeIn("slow");
username.css("border", "solid 2px red");
}
});
Ajax functions are asynchronous. validateUsername() returns before the ajax call is complete (without a return value, which is why you see undefined).
Probably the most elegant way to handle this would be to refactor to return $.ajax... and work with the Deferred object that is returned.
AJAX stands for Asynchronous JavaScript And XML. Ignore the XML part, but the Asynchronous is what's biting you in the backside here.
Anything that depends on the result of an ajax call must be in the success handler, called by the success handler, or defined in such a way that it will only be run after the success handler.
In my Javascript, I have the following scenario:
call_A(success_A)
call_B(success_B)
function call_A(success){
// make ajax request
success(result)
}
function call_B(success){
//make ajax request
success(result)
}
function success_A(){
//set data to be used by success_B
}
function success_B(){
..do some stuff
}
I want to make both call_A and call_B one after the other so that ajax calls are made (to save time).
I do not have the option for changing call_A and call_B function headers, so the call to these function has to be made specifying the success callback function.
What I want is that regardless of which of the 2 ajax call finishes first, I want success_A get called before success_B, because B depends on A's data.
What is the best way of getting this done?
a_success= false;
b_success= false;
call_A(generalSuccess);
call_B(generalSuccess);
function call_A(success) {
// make ajax request
a_success = true;
success(result)
}
function call_B(success) {
//make ajax request
b_success = true;
success(result)
}
function generalSuccess() {
//assuming you are passing which a or b is called. set b_success or a_success is true
if (a_success == true && b_success == true) {
// use all variables you need and get the final result
} else if (a_success == true) {
// set your desired value into a global variable from a request
} else if (b_success == true) {
// set results to use from b request.
}
}
jQuery(document).ready(function(jQuery) {
function checkEmail(email){
jQuery.post('someroute.php',
{email:eamil},
function(data){
if(data==error){
return false;
}
);
return true;
}
jQuery('#myform').submit(function(){
// como code and at the momment this
var result true;
//then I check email
var email = ('#myemail').val();
result = checkEmail(email);
return result;
});
The problem is this, a checkEmail function, first return true, and then return value jQuery.post function. Why?
I checked and in submit, first return true, and if you stop submit then you release that post return the value. Post works fine, but I don't understand why function checkEmail does not wait until post returns the value and goes on until the end to return true.
Because the jQuery.post() function is an AJAX request, and the first A in AJAX stands for asynchronous. That's exactly what asynchronous is, it doesn't stop code execution while it waits for a response. It fires the request and then immediately moves on to any code after the line making the request, in this case your return true statement.
Like Anthony Grist said. .post is an asynchronous call which means it doesn't wait to finish.
I've looked up the .post method (http://api.jquery.com/jQuery.post/).
It's basicly a shortcut for .ajax. You're missing the fail method in here so I would say use the .ajax call.
var value = 1;
var handlerUrl = "someroute.php";
//Do the Ajax Call
jQuery.ajax(
{
url: handlerUrl,
data: { email:eamil },
type: 'POST',
success: function (data)
{
return true;
},
error: function (jxhr, msg, err)
{
return false;
}
});
#user1727336 : Hi! You might want to edit your code and add the missing } and });. Here's a fix:
// enter code here
jQuery(document).ready(function(jQuery) {
function checkEmail(email){
jQuery.post('someroute.php', {email:eamil},
function(data){
if(data==error){
return false;
}
});
return true;
}
});
Not sure if my question is subjective/objective but as a JavaScript newbie i'm encountering this problem quite a lot. So here I go.
I'm used to write C#, so my JavaScript structure looks like C#. And just that, that gives problems I think ;-)
Let's give a simple example where I met my problem again today:
MyLibrary.fn.InitAddEntityForm = function () {
$('a#btnAddEntity').click(function () {
//post data and receive object with guid and isPersisted boolean
var persistedObject = MyLibrary.fn.CheckAndSendAddEntityForm("name", "avatarurl.png");
console.log("test");
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject.gdEntityId);
} else {
alert("Oops, something went wrong. Please call 911");
}
});
};
//////*****/////
//SOME FUNCTION THAT SENDS MY FORM AND RETURNS AN OBJECT WITH TRUE VALUE AND POSTED ENTITY ID
/////*****//////
MyLibrary.fn.CheckAndSendAddForm = function (txtName, ImageUrl) {
var postUrl = "/admin/add";
var persistedObject = new Object();
$.post(
postUrl,
{ Name: txtName, ImageUrl: txtImageUrl},
function (data) {
if (data.Status == 200) {
console.log("Post status:" + data.Message);
persistedObject.isPersisted = true;
persistedObject.gdEntityId = data.Data;
} else if (data.Status == 500) {
console.log("Failed to post entitiy");
} else {
console.log("Fault with Javascript");
}
}, "json"
);
return persistedObject;
};
Okay, thats it. Everything looks okay right? Browser says no.
I tried to debug it using firebug, looping over my code line by line, and that way the browser does what I want: Execute a new function to show the next panel in my wizard.
After placing a lot of Console.logs() in my code I figured out that this must be something about timing in JavaScript. In C# the code executes line by line, but apparently JavaScript doesn't.
By placing that Console.log("test") I noticed that "test" appeared in my console before "Post status: Success!".
So here's my question, how should I write my JavaScript code so I have control over the way the browser executes my code?
Should I really replace the code below to the end of my CheckAndSendAddEntityForm()?
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject.gdEntityId);
} else {
alert("fout");
}
Is this how I have to write JavaScript: One big domino effect or am I just doing something wrong?
$.post is a shortcut for an AJAX call, AJAX is by definition asynchronous, which means it won't wait on a response before continuing processing. If you switch it to a regular AJAX() method, there is an async option you can set to false, which will make it behave as you are expecting.
Alternatively you can also define a function to execute on successful return of the AJAX request, in which you can call the next step in your process chain.
The AJAX call is asychronous; that means that the callback method exposes by $.post will be executed when the request completes, but your javascript will continue executing as soon as the invoke to $.post finishes. If you want to do something after the ajax call is done, you need to provide a callback method and do something else, ex:
MyLibrary.fn.CheckAndSendAddForm = function (txtName, ImageUrl, callback) {
var postUrl = "/admin/add";
var persistedObject = new Object();
$.post(
postUrl,
{ Name: txtName, ImageUrl: txtImageUrl},
function (data) {
if (data.Status == 200) {
console.log("Post status:" + data.Message);
persistedObject.isPersisted = true;
persistedObject.gdEntityId = data.Data;
} else if (data.Status == 500) {
console.log("Failed to post entitiy");
} else {
console.log("Fault with Javascript");
}
callback(); // This is where you return flow to your caller
}, "json"
);
};
Then you invoke like so:
var persistedObject = MyLibrary.fn.CheckAndSendAddEntityForm("name", "avatarurl.png", function()
{
console.log("test");
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject .gdPronoId);
} else {
alert("Oops, something went wrong. Please call 911");
}
});
JavaScript is single-threaded. If you have asynchronous functionality, a simple boolean semaphore variable will help not to allow invocations of a function while some processes are running.
If you want to execute asynchronous tasks one by one (like a domino line), you will need to use callback functions.
What you're encountering is the "asynchronous" bit of AJAX. If you want to physically (as in the line line by line in the Javascript file) you can use the .success,.pipe or .done jQuery methods to add a callback to process the data further. Don't embed your callbacks if you can help it, or you will get a "domino effect" as you call it.