Here is the deal: I have a form, that takes quite some time to submit, because I'm waiting for some third party web services. So what I'm trying to achieve is, that after the submit button is clicked, it gets disabled, is assigned a special class and if it takes more than 5 seconds to submit the form, a notice is displayed.
I've came up with this:
$('#register_index_button').click(function(){
$(this).addClass('bt_button_loader');
$(this).val('Please wait');
$(this).attr('disabled', 'disabled');
$('#register_index_form').submit();
//it takes more than 5 seconds, display notice
setTimeout(function() {
$('#notice').html('Still waiting ...');
}, 5000);
});
Everything works fine, except the timeout function. I guess after I submit the form with jQuery, everything else after that is ignored?
Thank you for your help!
Try attaching an event handler on the form for the "submit" event. Put the timeout event handler function.
(https://developer.mozilla.org/en-US/docs/Web/Events/submit)
$('#register_index_form').on('submit', function(){
setTimeout(function() {
$('#notice').html('Still waiting ...');
}, 5000);
});
You should submit after your services get returned, I really don't see any code there that does that, and after you receive your service response, you submit your form.
$('#register_index_button').click(function(){
$(this).addClass('bt_button_loader');
$(this).val('Please wait');
$(this).attr('disabled', 'disabled');
//it takes more than 5 seconds, display notice
setTimeout(function() {
$('#notice').html('Still waiting ...');
}, 5000);
});
Place it when the service is retrieved, an ajax call, in complete method.
$('#register_index_form').submit();
As #gaemaf stated.
$('#register_index_button').click(function(){
$(this).addClass('bt_button_loader');
$(this).val('Please wait');
$(this).attr('disabled', 'disabled');
$('#register_index_form').submit(
//Nested inside of the submit to be executed when submit
setTimeout(function() {
$('#notice').html('Still waiting ...');
}, 5000);
);
});
Another method would be to gather up all of the fields from the form and submit them using an Ajax submit.
That way you can create the 'Please wait' when the ajax is fired and have a confirmation that the form has been received and is being processed. So something like....
$('#register_index_form').submit(function(){
var url = "path/to/your/script.php";
// the script where you handle the form input.
$.ajax({
type: "POST",
url: url,
data: $("#register_index_form").serialize(),
// Gets the forms elements and submits them
timeout: 10000
// How long the Ajax will wait before considering the call to have failed
})
.done(function( data ) {
//Do something ...
});
return false; // avoid to execute the actual submit of the form.
});
Related
I'm stuck in a really bizarre situation here. It's complicated to explain but I'll try my best.
Detailed explanation of the issue:
On every top Nav click (Green donuts/circles), or next button, I must submit the form, if it exists and is valid. If not valid, form.valid() triggers validation errors and return false would stop any further propagation. This setup was working flawlessly until I noticed a strange behavior which isn't very persistence. Form on my 3rd tab, specifically, is quite data heavy. When I hit next button it should practically go thru the same process: check for an existing form, if valid, then submit. Submit calls the POST action method and when post completes it GETs the view for next tab. It works like this 5/10 times but at other times GET executes before the POST, which causes next page to load with incomplete data. When I put breakpoints to debug, I see GET for the next tab executing before POST of the current tab.
UI Explained:
I have a UI with 4 navigation <a> buttons on top - in the center there's a 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 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. Its 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 a server side 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!
Ajax is async, and your forms submit which is using Ajax.BeginForm() is using ajax. What is happening is that when you click your 'Next' button, which triggers the $('button').on('click', function () { code:
You call the ValidateForm() function (and assuming its valid),
your $(form).submit(); line of code starts making a ajax POST
The code progresses to the final return true; line while the ajax
call is executing.
Because the ValidateForm() function returned true, the $.ajax
GET call now starts, but at that point the ajax POST in the
ValidateForm() function may not have finished executing causing
your GET method to return invalid data
You need to change your code so that the GET call is made once the POST method call has completed. And since your using the $.ajax() methods throughout your code, and $.ajax() gives you more flexibility, it seems unnecessary to use Ajax.BeginForm() (and the extra overhead of including the jquery.unbtrusive-ajax.js script). You should also be handling the forms .submit() function (if you do not want the 'Next' button to be a submit button in the form, you could just trigger the .submit() event in the buttons .click() handler)
$(document).on('submit', 'form', function(e) {
e.preventDefault(); // cancel default submit
var form = $(this);
if (!form.valid()) {
return; // will display the validation errors
}
.... // get the relevant urls to the GET and POST methods etc
$.post(postUrl, form.serialize(), function(data) {
.... // not clear if your [HttpPost] method returns anything
}).done(function() {
$.get(getUrl, someData, function(response) {
.... // Update the DOM with the next form?
.... // Re-parse the validator for client side validation
}
}).fail(function() {
.... // code that you might run if the code in the [HttpPost] method fails
});
});
You should also consider returning the appropriate 'next' view in the [HttpPost] method so that you don't then needs to make a second call back to the server to get it.
It is also worth reading the Deferred Object documentation and the use of $.when(), $.then() etc.
Workflow:
With the macro goal of not having the user wait for anything, this is the user-flow of what I'm trying to do:
User fills out form
User clicks button to submit form
Form submits async via AJAX (FWIW, processing takes ~5 seconds)
Immediately after the form is async submitted, jQuery pops up a survey modal
User can either fill out survey and click button to submit it, or click button to skip it entirely
What I'm having trouble accomplishing now is the effect after user clicks submit-survey or skip-survey. It's easiest to talk about skipping first. If the user chooses to skip the survey, then this happens:
A "working" overlay appears over the page for as long as the async needs to still finish (if at all needed), because once the async finishes, it returns the right next action that needs to happen in the jQuery code (for example purposes, let's say this is a simple console.log)
If the user fills out the survey and clicks submit-survey, then what happens is this:
Survey submits async via AJAX
A "working" overlay appears over the page for as long as the async needs to still finish (exactly same as if skip-survey were clicked)
The additional complexity is that, the survey submits to a different route depending on if the first form is completed by the time the user clicks the submit-survey
Code
I'm taking this opportunity to learn more about async flows in general, which is why the code is a mess. Right now the implementation I'm thinking is that the form submission AJAX, once it's succeeded or failed, generates the 1) appropriate URL for survey submission and the 2) next action that submit or survey will ultimately trigger)
So something like...
$.ajax({
type : "POST",
url : '/orders/create_or_update',
dataType: 'json',
contentType: 'application/json',
data : JSON.stringify(params)
})
.done(function(){
// submit-survey post route
submit-url = '/orders/success_ajax_update'
// next action triggered by skip-survey and submit-survey
function nextAction() { console.log("fail") }
})
.fail(function(){
// submit-survey post route
submit-url = '/almost_orders/fail_ajax_update'
// next action triggered by skip-survey and submit-survey
function nextAction() { console.log("fail") }
})
$(document).on("click", "#submit-survey", function() {
if (isFunction(nextAction)) {
$.ajax({url: submit-url, ... })
nextAction()
} else {
addWorkingOverlay();
}
})
$(document).on("click", "#skip-survey", function() {
if (isFunction(nextAction)) {
nextAction()
} else {
addWorkingOverlay();
}
})
The problem with the above though, is if user clicks either submit-survey or skip-survey before the AJAX has finished, then the working overlay gets added, but nothing then says "oh now AJAX has finished, nextAction is defined, so loop again". And obviously I can add a counter to check back on isFunction(nextAction) but this seems to completely defeat the purpose of AJAX in the first place.
Ideas on how to fix this?
Have new idea, need more help to flesh out. Per documentation, you can use jQuery's .when like so:
$.when( $.ajax( "/page1.php" ), $.ajax( "/page2.php" ) )
.then( myFunc, myFailure );
This can work in my case, only instead of a second .ajax I have a button click (either submit or skip). The question with this idea becomes, how do I turn the user's click of either submit or skip button into a resolved deferred that the .when can work with?
I have a form with one tricky field that needs to pass validation on the server side. So i need to capture for submit, send ajax request with this field, get result, if result is valid the continue form submission, other wise stop submission and show message.
How would i do that?
I got lost a little bit in "big picture" with events and callbacks.
Here is what i have so far.
$("form").submit(function( event ) {
event.preventDefault();
return check();
});
function check() {
var field = $('#myFiled').val();
if (!field ) {
show.error('Please enter VIN number and try again.');
return false;
}
$.getJSON('MyURL', function(data){
if (!data.valid) show.error('Invalid entry! Please try again.');
})
}
UPDATE
Please read the question.
In case of successful validation i need to continue regular form submission. Not via AJAX.
$("form").submit(function( event ) {
event.preventDefault();
checkAndSubmit();
});
function checkAndSubmit() {
var field = $('#myFiled').val();
if (!field ) {
show.error('Please enter VIN number and try again.');
}
else {
$.getJSON('MyURL', function(data){
if (!data.valid) {
show.error('Invalid entry! Please try again.');
}
else {
$.post('yourFromPostURL', $('form').serialize(), function(data){
//show something of success or failure.
});
}
})
}
}
Judging by your check() function, you already understand basic JavaScript validation. If you want a form's validation to fail, have its .submit() method return false, otherwise return true.
Just treat the data returned by the AJAX request the same way you did the field variable.
Perhaps the one thing your missing is how to make check() return false from inside the getJSON function. The answer to that is a simple matter of variable scope.
var data;
$.getJSON('MyURL', function(data_){
data = data_;
});
if (!data.result) return false;
Since JavaScript has function scope, data will still be accessible inside of .getJSON().
There is one more thing though, .getJSON() is an asynchronous AJAX request, meaning your validation function will finish without waiting for the result of the request. You want to use a syncrhonous ajax request so your check() function won't finish until your validation is done. However there is no way to make .getJSON() synchronous so you will have to use the .ajax() method. See this post.
EDIT:
Your issue is if there are no validation errors, you do not return a value. You must return true;.
The alternative to the AJAX form submit is to prevent form submission and execute validation at the button level.
$('#submitButtonID').click(function (e) {
e.preventDefault();
//start your AJAX validation
checkAndSubmit();
}
Then validate and upon success, you can still submit the form regularly.
function checkAndSubmit() {
var field = $('#myField').val();
if (!field ) {
show.error('Please enter VIN number and try again.');
}
else {
$.getJSON('MyURL', function(data){
if (!data.valid) {
show.error('Invalid entry! Please try again.');
}
else {
$('form').submit();
}
});
}
}
Server side validations will run upon post, but you won't interrupt the form submission with your AJAX validation because it's only captured at the button level. (If you need to capture the Enter button, see this answer: Capturing Enter button to submit form
I have a script that uses ajax to send mail. I have tested it by checking the email that will receive the mail and true enough, the ajax request is successful. I also checked the console window of my Firefox browser and it also shows me a successful message. But my problem here is that instead of the done callback function, the error callback function is fired. You may all wonder why I'm still using the error function instead of fail. The reason for this is because when I tried using the fail function, it doesn't trigger the alertbox that I have set inside it. So what I did is go back and use error function again since at least it triggers the alertbox I made.
Here is the script:
<script type="text/javascript">
var submitButton = $('#submit'); // Variable to cache button element
var alertBox1 = $('.success'); // Variable to cache meter element
var alertBox2 = $('.alert');
var closeButton1 = $('.close1'); // Variable to cache close button element
var closeButton2 = $('.close2'); // Variable to cache close button element
$( function(){
$( '#contactform' ).submit( function(e){
e.preventDefault();
console.log( 'hello' );
var formData = $( this ).serialize();
console.log( formData );
$.ajax({
type: 'POST',
url: 'send.php',
data: formData,
dataType: 'json',
done: function(){
$(submitButton).fadeOut(500); // Fades out submit button when it's clicked
setTimeout(function() { // Delays the next effect
$(alertBox1).fadeIn(500); // Fades in success alert
}, 500);
},
error: function(){
$(submitButton).fadeOut(500); // Fades out submit button when it's clicked
setTimeout(function() { // Delays the next effect
$(alertBox2).fadeIn(500); // Fades in fail alert
}, 500);
}
});
});
$(closeButton1).click(function() { // Initiates the reset function
$(alertBox1).fadeOut(500); // Fades out success message
setTimeout(function() { // Delays the next effect
$('input, textarea').not('input[type=submit]').val(''); // Resets the input fields
$(submitButton).fadeIn(500); // Fades back in the submit button
}, 500);
return false; // This stops the success alert from being removed as we just want to hide it
});
$(closeButton2).click(function() { // Initiates the reset function
$(alertBox2).fadeOut(500); // Fades out success message
setTimeout(function() { // Delays the next effect
$('input, textarea').not('input[type=submit]').val(''); // Resets the input fields
$(submitButton).fadeIn(500); // Fades back in the submit button
}, 500);
return false; // This stops the fail alert from being removed as we just want to hide it
});
});
</script>
What seems to be the one causing this? Just to reiterate, I've tried using fail instead of error callback function since that is one of the answers I found in the Internet and also because I know for a fact that the error function is already deprecated. But because of the reason I mentioned above, I've no choice but to use it.
if you refer the documentation, you cannot use done inside the ajax function as a callback. Either use success or add done at the end of ajax call.
$.ajax({
// url, data etc
success: function() {
//success handler
},
error:function(){
//Error handler
}
});
(OR)
$.ajax({
// ajax related codes
}).done(function(){
//callback
});
Also if you aren't really returning JSON from the server, remove the dataType: 'json', from the ajax call.
I am using bootstrap and JQuery. I want to know if its possible to invoke a bootstrap modal dialog before making an ajax call in '$.ajax beforeSend'? I want to collect user comments before submitting the form. I have several buttons on my page that require this behavior. So, I want to make it more generic.
Thanks
I'd suggest the use of jQuery's Deferred object (see http://api.jquery.com/category/deferred-object/).
The following is pseudo code for your buttons' event handler:
$('#theButton').on('click', function() {
var dfd = $.Deferred();
// Code to show modal dialog here.
// Pass dfd to the dialog, and have it call dfd.resolve()
// when the user has finished, or dfd.reject() in case the
// user does not complete the form.
dfd.done(function() {
// Ajax call here
});
});
The function that gets passed to dfd.done() as an argument gets only called if and when somebody calls the resolve() method on the Deferred object.
Due to the asynchronous event model in javascript, you cannot postpone the ajax request from being sent off from within beforeSend. Once beforeSend is executed, the only chance you have to "delay" the ajax request is to outright cancel it entirely by returning false from the callback.
So while you could keep track of a status variable that knows whether the form is ready to submit (return false from beforeSend whenever the form is not ready), you're much better off doing these validation checks before ever creating the ajax request to begin with.
// why do this
$.ajax('/path', {
beforeSend: function () {
if (formIsNotReady()) {
showModal();
return false;
}
}
});
// when you can do this
if (formIsNotReady()) {
showModal();
} else {
$.ajax('/path');
}