nested $.ajax in inside $.ajax - very strange issue - javascript

Let's start from here - I have defined base setting for all future AJAX-requests, like this
$.ajaxSetup({
beforeSend : function(){
$("#ajax-loader").dialog({
modal : true
});
},
complete : function(){
$("#ajax-loader").dialog("hide");
}
});
Now, I have a form where my users can upload their bio and pictures. When a form is valid, then I allow to upload their pictures. It works this way:
$("#send").click(function(e){
e.preventDefault();
$.ajax({
data : $("#bio-form").serialize(),
url : "/validate.ajax",
success : function(response) {
// If AJAX-validator returns "1" then a form is valid
if (response == "1"){
// Now I start to upload photos, like
// this
var formData = new FormData(document.getElementById('upload-form'));
$.ajax({
processData : false,
contentType : false,
cache : false,
data : formData,
success : function(response) {
alert(response);
}
});
}
}
});
});
The problem
Once ajax-uploading starts, I expect a $("#ajax-loader") to be showed. On complete this should be closed automatically according to the settings I defined in $.ajaxSetup.
BUT...
It appears and disappears right after 1 sec on file uploading. I know that ajax request isn't completed, because I get successfuly message after 1-2 mins (that photos uploaded).
I tried to change async: false and it started to work as expected === a modal appears when uploading files to server and disappers when done:
data : formData,
async : false,
processData : false,
Question
Is it possble to do the same when async : true is set to its default mode(true)? I don't want a browser to be frozen when uploading in progress!

As you've discovered, Ajax is an asynchronous technology - meaning it runs on its own schedule
Making Ajax synchronous causes all sorts of problems (using async:false actually causes the browser to freeze whilst the request is performed)
There are several issues I'd mention you may want to fix:
Why Nested Ajax?
Nesting Ajax requests, in this current state of the web, is, in my opinion, bad practice. It generally means you've got something wrong with your system. Concurrent requests are fine - but nested I'd avoid like the plague
Ajax Callbacks
If you can get your Ajax requests down to a single entity, I'd look at using Ajax Callbacks to create your desired result
Ajax callbacks work by using Ajax as part of a function, where the success & error callbacks are handled by several differnt parts of your parent function. Here's an example:
function create_modal(o){
fetch_modal(o.ajax, function(data){
//do success stuff here
}, function(data){
//do error stuff here
});
}
function fetch_modal(link, success, error) {
$.ajax({
url: link,
success: function(data) { success(data); },
error: function(data) { error(data); }
});
}

The problem is ajaxSetup. You have to follow enable/disable method on uploading photo. Let me clearup the understanding. You want to show the loader to intimate the user that he/she has to wait until validation process finishes. Once the validation get success you are starting to upload file. While uploading file, you want to allow the user interact with page.
To achieve this you have to do as follows,
function ajaxSetup(mode) {
var options = {};
if (mode === "on") {
options = {
beforeSend : function() {
$("#ajax-loader").dialog({
modal : true
});
},
complete : function() {
$("#ajax-loader").dialog("hide");
}
};
} else {
options = {
beforeSend : function() {
},
complete : function() {
}
};
}
$.ajaxSetup(options);
}
And Make your ajax request as follows,
ajaxSetup("on");
$("#send").click(function(e) {
e.preventDefault();
$.ajax({
data : $("#bio-form").serialize(),
url : "/validate.ajax",
success : function(response) {
// If AJAX-validator returns "1" then a form is valid
if (response == "1") {
// Now I start to upload photos, like
// this
var formData = new FormData(document.getElementById('upload-form'));
ajaxSetup("off"); //Here second-time seem-less displaying of loader will be avoided.
$.ajax({
processData : false,
contentType : false,
cache : false,
data : formData,
success : function(response) {
alert(response);
}
});
ajaxSetup("on");
}
}
});
});
Note: I am not tested code. Please make sure, if any syntax or typo error.

Related

Calculating the Time of an ajax Response and use that time in SetTimeout function

I am trying to get the response time of an ajax request and use it in a setTimeout() function, this function displays a loader that is suppose to keep loading until we get the response.
Here's my function :
$("#recalculer").click(function(){
ajax_call();
setTimeout(function()
{
$("#divgris").fadeTo(0,1);
$("#loadingdiv2").hide();
}, 5000);
});
And here's my ajax request :
function ajax_call()
{
var resultat;
var duree_souhaitee= $("#duree").val();
var apport_personnel= $("#apport").val().replace(/\s+/g, '');
var prix_achat_bien=$("#prix").val().replace(/\s+/g, '');
$.ajax({
type: "POST",
url: "/iframe/rest-assurance",
data : {
"duree_souhaitee" : duree_souhaitee,
"apport_personnel" : apport_personnel,
"prix_achat_bien" : prix_achat_bien
},
dataType: 'json',
xhrFields: {
withCredentials: true
},
async: true,
beforeSend: function(){
$("#actualiserAssurance").hide();
},
success: callback_assurance
});
}
For now i set a time of 5000 but i need to replace it with the ajax response time, how can I achieve that ?
I use always:
$("#loadingdiv2").show();
$.ajax(
...
).always(function(){ $("#loadingdiv2").hide(); });
If you want to separate it from the Ajax call I would use a custom event.
$("#recalculer").click(function(){
ajax_call();
});
$("body").bind('custom.ajaxStart', function(){ $("#loadingdiv2").show(); });
$("body").bind('custom.ajaxStop', function(){ $("#loadingdiv2").hide(); });
function ajax_call(){
$('body').trigger('custom.ajaxStart');
$.ajax(..).always(function(){ $('body').trigger('custom.ajaxStop'); });
}
The always callback is triggered even on a 404, relying on timing never works well for me.
Using an event gives you the flexibility of calling the loading deal, from anywhere.
Meaby the you can use:
console.time(label);
and
console.timeEnd(label);
more info can be found here.
Goodluck!
use
var afterfnc = ()=>{
$("#divgris").fadeTo(0,1);
$("#loadingdiv2").hide();
}
and then set
callback_assurance = afterfnc
in ajax call

Search on keyup and document ready using Ajax

I am trying to make search function based on Ajax/Jquery.
My web app shows the data of service requests from the database. I want to make searchbar for my app as follows:
show all service request on the table initially.
If something is typed on the searchbar, it searches data and load those data to the table.
Finally if user deletes anyword from searchbar it will show all data as stated on No.1
I managed doing second and third function but I am having issues with the first one.
$(document).ready(function(){
$('#search_text').keyup(function(){
var txt = $(this).val();
if(txt != '') {
$.ajax({
url:"ajax/fetchRequests.php",
method:"post",
data:{search:txt},
dataType:"text",
success:function(data) {
$('#result').html(data);
}
});
}
else if(txt == '') {
$.get("ajax/readRequests.php", {}, function (data, status) {
$("#result").html(data);
});
}
});
});
Here is another script that i have worked on trying:
$(document).ready(function(){
var txt = $('#search_text').val();
if(txt != ''){
$.ajax({
url:"ajax/fetchRequests.php",
method:"post",
data:{search:txt},
dataType:"text",
success:function(data) {
$('#result').html(data);
}
});
}
else if(txt == '') {
$.get("ajax/readRequests.php", {}, function (data, status) {
$("#result").html(data);
});
}
});
All my features are working except for the search functions. Any tips or critics are welcome, thank you very much in advance.
I suggest you do two things, 1) use the suggested .on() and 2) use only one ajax function to simplify things. The idea is to funnel your calls through one function so that you know if something fails, it's not because you messed up the ajax part of the script:
// Create a generic ajax function so you can easily re-use it
function fetchResults($,path,method,data,func)
{
$.ajax({
url: path,
type: method,
data: data,
success:function(response) {
func(response);
}
});
}
// Create a simple function to return your proper path
function getDefaultPath(type)
{
return 'ajax/'+type+'Requests.php';
}
$(document).ready(function(){
// When the document is ready, run the read ajax
fetchResults($, getDefaultPath('read'), 'post', false, function(response) {
$('#result').html(response);
});
// On keyup
$(this).on('keyup','#search_text',function(){
// Get the value either way
var getText = $(this).val();
// If empty, use "read" else use "fetch"
var setPath = (!getText)? 'read' : 'fetch';
// Choose method, though I think post would be better to use in both instances...
var type = (!getText)? 'post' : 'get';
// Run the keyup function, this time with dynamic arguments
fetchResults($, getDefaultPath(setPath), type, { search: getText },function(response) {
$('#result').html(response);
});
});
});
To get initial results hook onto jQuery's document ready event.
var xhr;
var searchTypingTimer;
$(document).ready(function(){
// initial load of results
fetchResults([put your own params here]);
// apply on change event
$('#search_text').on('input', function() {
clearTimeout(typingTimer);
searchTypingTimer = setTimeout(fetchResults, 300);
});
});
function fetchResults($,path,method,data,func)
{
if (xhr && xhr.readyState != 4){
xhr.abort();
}
xhr = $.ajax({
url: path,
type: method,
data: data,
success:function(response) {
func(response);
}
});
}
As Rasclatt mentions you should use jQuery's on method to catch any changes.
Secondly I'd recommend disposing of previous requests when you make new ones, since if you are sending a new one on each character change then for one word many requests will be made. They won't necessarily arrive back in the order you send them. So for example as you type 'search term', the result for 'search ter' may arrive after and replace 'search term'. (welcome to async).
Thirdly since you will send many requests in quick succession I'd only call your fetchResults function after a short time out, so for example if a user types a five character word it doesn't fire until 300ms after the last character is typed. This will prevent 4 unnecessary requests that would just be ignored but put strain on your backend.

Waiting for all AJAX call than show dialog box

I am working on a dynamic online form website. In the main form, I have multiple sub-forms which can be added and deleted dynamically.
<div class='subform'>
//form fields
<input ...>
...
<button class='subform_submit'>
</div>
For each subform, I bind an AJAX call on the subform's submit button like this:
$('#main').on('click', '.subform_submit', function(){
// Get this subform's user input
...
$.ajax({
url: ..,
type: ..,
data: /* this subform's data */
});
});
So in that page, I may have 0 to 10 subforms depending on the user's selection.
I also have a main submit button on the bottom of the page, which can submit those subforms and the main form's data together.
$('#main').on('click', '#submit', function(e){
$('.subform_submit').click(); // Submit each subform
bootbox.confirm({ });
})
Once main submit button is clicked, I want to show a loading picture and then show a dialog box (I use bootbox.confirm() here) until all AJAX calls have completed.
This dialog box is telling user that whole form including sub-forms has been submitted.
But the problem is that each AJAX call may take 2 seconds to complete and I don't know how may calls may be pending completion. How can I write this main submit button so that it will:
Show the loading image immediately, and
Hide the loading image and show the dialog box after all AJAX calls have completed?
Keep track of how many sub-forms there are;
$subFormsCount = $('.subform').length;
Keep track of how many forms have been submitted;
$submittedForms = 0;
Each time a form finishes submitting, add to the $submittedForms;
$.ajax({
..
..
done: function(){
$submittedForms++;
}
})
Create a global timer to see if the number of submitted forms matches the total number of subforms. If true, hide the dialog box;
setInterval(function(){
if($submittedForms == $subFormsCount){
$('.dialog').show();
}
}, 50ms)
Edit
You could skip the global timer (as this will probably be a few milliseconds out) - include the check in your ajax.done instead;
$.ajax({
..
..
done: function(){
$submittedForms++;
if($submittedForms == $subFormsCount){
$('.dialog').show();
}
}
})
You want to use .done() in order to specify code that should wait until the AJAX asynchronous function completes.
$.ajax({
url:..,
type: ..,
data: /* this subform's data*/ })
.done(function() {
//Put code here
});
Have you tried .ajaxStop() event handler ?
$(document).ajaxStop(function() {
// place code to be executed on completion of last outstanding ajax call here
});
also, check this answer
I assume you have 9 subform and 1 main form.
Code for 8 subform will be same.
I use here async:false : Means next ajax will not be call until 1st one is not completed.
Sample Code Format :
var id = 5;
$.ajax({
url: ,
type: 'POST',
data: {'id':id},
dataType: 'JSON',
async: false,
error : function(xhr, textStatus, errorThrown) {
alert('An error occurred!');
},
success : function(response){
}
});
Just set variable in your last sub form that is 9th subform.
success : function(response){
var counter = true;
}
if(counter){
/* Code to show dialog.*/
}
You can use $.when to wait for each request to complete. Something like this should get you close. You'd basically want to store all the ajax requests in an array and pass that to when as the arguments.
$('#main').on('click', '.subform_submit', function () {
var formRequests = $('.subform').map(function () {
var $form = $(this);
return $.ajax({
url: '',
data: $form.serialzeArray()
});
}).get();
$.when.apply(undefined, formRequests).done(function () {
console.log('All done!');
});
});
Here goes a very similar little demo I just made up: https://jsfiddle.net/g9a06y4t/

How to work with JSONP to call back the function?

I am trying to communicate with a server using JSONP call back.
Here is my code
$('.icwsDownloadRecording').click(function(){
var id = $(this).attr('data-recordingid');
$.ajax({
type: 'GET',
url: 'http://example.com/Default2.aspx',
data: {'ID': id},
dataType: 'jsonp',
cache: false,
timeout: 40000,
crossDomain:true,
jsonp: "MyCallbackFunction",
});
});
function MyCallbackFunction(data)
{
//process data further
console.log(data);
if(!data || data.url.length < 5){
return;
}
var $preparingFileModal = $("#preparing-file-modal");
$preparingFileModal.dialog({ modal: true });
$.fileDownload( data.url, {
successCallback: function (url) {
$preparingFileModal.dialog('close');
},
failCallback: function (responseHtml, url) {
$preparingFileModal.dialog('close');
$("#error-modal").dialog({ modal: true });
}
});
return false; //this is critical to stop the click event which will trigger a normal file download!
}
The issue here is that I keep getting this message in the console
ReferenceError: MyCallbackFunction is not defined
I do have this defined as you can see in my code above
The server respond looks like this
MyCallbackFunction("{'URL': 'http:\/\/example.com:8106\/ghjgj3835396265336634646562363030303122226D616C686179656B22535353557DBE0C305645E2DE110AA1D7F8792E96A3'}");
how can I correct this issue?
EDITED
This is my code after Quentin Answer , this is my new code
$(function(){
$('.icwsDownloadRecording').click(function(){
var id = $(this).attr('data-recordingid');
$.ajax({
type: 'GET',
url: 'http://example.com/Default2.aspx',
data: {'ID': id},
dataType: 'jsonp',
timeout: 40000,
success: function(data){
//process data further
console.log(data);
if(!data || data.url.length < 5){
return;
}
var $preparingFileModal = $("#preparing-file-modal");
$preparingFileModal.dialog({ modal: true });
$.fileDownload( data.url, {
successCallback: function (url) {
$preparingFileModal.dialog('close');
},
failCallback: function (responseHtml, url) {
$preparingFileModal.dialog('close');
$("#error-modal").dialog({ modal: true });
}
});
return false; //this is critical to stop the click event which will trigger a normal file download!
}
});
});
});
Unless you have all of that code wrapped in another function, that should work.
Using a hardcoded function name is bad practise though.
Update:
$(function(){
You do have all that code wrapped in another function.
Remove this:
jsonp: "MyCallbackFunction",
Replace it with:
success: MyCallbackFunction
Or you could put an anonymous function expression there instead (as you have done in your edit)
Let jQuery generate a unique function name (which protects you from race conditions) and allow the server to use the callback query string argument to determine what function name to use.
MyCallbackFunction is in the same scope as the ajax call, so it will be available to the function (which can copy it to a suitably named global).
After you fix that, you have an additional problem:
MyCallbackFunction("{'URL':
Your response is JSON encoded in a JavaScript string, but you are trying to treat it as a JavaScript object.
Either:
Fix the server so it doesn't stringify the JSON or
Run the first argument through JSON.parse
crossDomain:true,
Remove that. It doesn't do anything here. (All it does is, when using XHR (which you aren't using) to the same origin (which you aren't targeting), suppress the custom headers that aren't typically allowed on a cross-origin request so that you can perform an HTTP redirect to a different origin).
cache: false,
That's the default for JSONP requests. Including it is pointless.
return false; //this is critical to stop the click event which will trigger a normal file download!
If you want to stop the click event, then you need to return false from the click event handler function (not the Ajax success handler).
You can't wait until the Ajax function has run and got a response before doing that. Ajax is asynchronous.

help with jquery ajax success event

I'm having an issue with my update button and jquery ajax. Right now when I click on my update button, it saves whatever updated data to the database. My goal is I want to slide up a message if the update is successful. I was looking at ajax post and using the success event seems like it would work but I dont know how to incorporte it. How would I do this? Would it be something like this?
$(document).ready(function(){
$('#divSuccess').hide();
$('#btnUpdate').click( function() {
alert('button click');
$.ajax({
url: "test.aspx",
context: document.body,
success: function(){
$('#divSuccess').show("slide", { direction: "down" }, 3000);
$('#divSuccess').hide("slide", { direction: "down"}, 5000);
}
});
});
});
check out this question for an example on how to handle the success event. Hope this helps!
$("#targetDiv").load("page.php",$("#form").serializeArray(),function (response)
{
if (response == '0' && response != '')
alert('Request not sent to server !\n');
else if(response == '-1')
alert('Please write some more !\n');
else
{
alert("success! ");
}
}
);
i've echo ed 0 and -1 for failure and other for success
In the jquery post function, you can execute some callback function.
function (data, textStatus) {
// data could be xmlDoc, jsonObj, html, text, etc...
this; // the options for this ajax request
// textStatus can be one of:
// "timeout"
// "error"
// "notmodified"
// "success"
// "parsererror"
// NOTE: Apparently, only "success" is returned when you make
// an Ajax call in this way. Other errors silently fail.
// See above note about using $.ajax.
}
http://docs.jquery.com/Post
With at least jQuery 1.5, you've got deferred objects and new syntax for AJAX events (including success).
var $ajaxcall = $.ajax({
url : 'myurl.svc/somemethod',
data : '{ somedata : "sometext" }'
});
$ajaxcall.success(function() {
// do something on successful AJAX completion
});
Of course you can chain that as well, and call something along the lines of $.ajax().success() or something.
Just wrote a blog post on it myself, if you're interested in reading more.

Categories

Resources