Execute code after all the promises have been resolved with Jquery - javascript

I seem to struggle to find out how to use the when function in my code. What I wish to achieve is a window.location.reload when all the ajax requests are done.
If I put the window.location.reload after the "each" loop then all my AJAX requests are just aborted.
$('.removeSelected').click(function() {
if (confirm('Are you sure you will continue?')) {
$('.container-fluid :input:checked').each(function () {
var recordId = $(this).data("id");
// Jquery remove it
$.ajax({
method: "GET",
url: "/controllers/lines.asp",
data: { recordId: recordId }
}).done(function() {
console.log('done');
});
});
// window location reload here
}
});

The order of your logic seems to be flawed. What I think you want to do is collect all of your records and then send in one request. When the request is done, then you reload.
$('.removeSelected').click(function() {
if (confirm('Are you sure you will continue?')) {
//prepare an array for records
var records = [];
$('.container-fluid input:checked').each(function () {
//add the id
records.push($(this).data("id"));
});
//make the request
$.ajax({
method: "GET",
url: "/controllers/lines.asp",
data: records
}).done(function() {
//when successful, reload the page
window.location.reload();
});
});
};
});
I'm not sure if you want to reload just on success or on complete (which will reload on fail also).

Get the number of elements to delete with
$('.container-fluid :input:checked').size();
Each time done is called, increment a separate total. When the total matches the count, reload the page.

I agree with fauxserious that sending all at once instead of making a request per record would be more efficient.
However, if for any reason you don't want to change your server side code, what you need to do is identify when all requests have been completed. You can achieve this using promises.
If you don't have much experience with this, code below should give you an idea on how to achieve the expected behavior without using promises:
$('.removeSelected').click(function() {
if (confirm('Are you sure you will continue?')) {
var $containerFluidElements = $('.container-fluid :input:checked');
var numberOfRequestsPending = $containerFluidElements.length;
$containerFluidElements.each(function () {
var recordId = $(this).data("id");
$.ajax({
method: "GET",
url: "/controllers/lines.asp",
data: { recordId: recordId }
}).done(function() {
numberOfRequestsPending -= 1;
if(numberOfRequestsPending == 0)
{
window.location.reload();
}
});
});
}
});

You need to pass all the promises to "when" so that it can wait for all of them to be resolved.
To do that you can put all the promises returned by $.ajax in an array and pass it to $.when:
$('.removeSelected').click(function() {
if (confirm('Are you sure you will continue?')) {
var promises = [];
$('.container-fluid :input:checked').each(function () {
var recordId = $(this).data("id");
// Jquery remove it
promises.push($.ajax({
method: "GET",
url: "/controllers/lines.asp",
data: { recordId: recordId }
})
.done(function() {
console.log('done');
}));
});
// use apply to pass an array instead list of parameters
$.when.apply($, promises).then(function() {
// all the promises have been resolved
window.location.reload();
}
});

Related

jquery ajax while loop in odd order

I cant understand what I'm doing wrong on this one. I've got a while loop running an AJAX function to get data from a PHP file for the amount of people selected from a dropdown.
$(document).on('change', '#attendingCount', function() {
$(".person-container").html("");
var amount = $(this).val();
var i = 0;
while (i < amount) {
getPerson(i);
i++;
}
});
getPerson(0);
function getPerson(e) {
$.ajax({
type: 'post',
url: './person.php',
data: {
"amount": e
},
success: function(data) {
$(".person-container").append(data);
},
error: function() {
console.log('error');
}
});
}
When the result gets pumped out though, the order of them is completely random.
What is it exactly I'm doing wrong?!
AJAX works in an asynchronous way, not necessarily the first request you send is going to return data first, that's the problem here. Doing an AJAX call in a while loop is not the best solution.

Is using jQuery clever to download multiple template-files?

I want to download and cache multiple mustache-templates and the only real way I know to do this is by downloading them via the jQuery.ajax()-method.
So my straightforward preload-init code looks a little ... ugly!
function getAllTemplatesUglyAndNotPerformant() {
//this is no longer valid, stays just for reference; look at the bottom for the solution
//easy - preload the template and execute it to the data
$.ajax({
url: 'fragments/employee.mustache',
success: function (employeeTpl) {
//uh-oh async process-handling forces me into digging this deeper
$.ajax({
url: 'fragments/employee_day.mustache',
success: function (dayTpl) {
//third level - now i am puzzled already
$.ajax({
url: 'fragments/employee_day_regular.mustache',
success: function (protodayTplRegular) {
//monologue: am i doing this right?
$.ajax({
url: 'fragments/employee_day_deleted.mustache',
success: function (protodayTplDeleted) {
//most probably not
var cachedTemplates = {
employee: employeeTpl,
day: dayTpl,
protoday: {
regular: protodayTplRegular,
deleted: protodayTplDeleted
}
};
//shoot, i also cannot return cachedTemplates, better bury my init-method in this!
init(cachedTemplates);
}
});
}
});
}
});
}
});
}
//initializes downloading and parsing data to what will be seen
function init(cachedTemplates) {
//get the data
$.ajax(
url: '_get_data.php',
success: function (data) {
if (data.success) {
$.each(data.employees, function (iEmployee, vEmployee) {
//this goes through a custom rendering for an employee and his sub-nodes stored in arrays (all rendered in Mustache)
var employee = parseEmployee(vEmployee);
var html_employee = employee.render(cachedTemplates);
$('#data-position').append(html_employee);
});
}
//ignore what may else happen for now
}
)
}
Is there a better way for downloading multiple files for caching in JS?
EDIT:
my rewrite of getAllTemplates() looks now more like this and is finally "more-understandable" and performant for the next one to touch "Peters Legacy":
function getAllTemplates() {
$.when(
$.get('fragments/employee.mustache'),
$.get('fragments/employee_day.mustache'),
$.get('fragments/employee_day_regular.mustache'),
$.get('fragments/employee_day_deleted.mustache')
)
.done(function (employeeTpl, acquisitionTpl, protodayTplRegular, protodayTplDeleted) {
var cachedTemplates = {
employee: employeeTpl[0],
acquisition: acquisitionTpl[0],
protoday: {
regular: protodayTplRegular[0],
deleted: protodayTplDeleted[0]
}
};
init(cachedTemplates);
});
}
You don't specify which version of jQuery you're using, so here's assuming you're using a somewhat current build;
You can use $.when() which is in jQuery 1.5+.
$.when() allows you to bundle (essentially) a bunch of async methods (ajax in this case) and wait until all of them have completed. In your example you are firign one request, waiting for the response and then firing another. With $.when(); if your connection allows it they can all fire simultaneously, saving a lot of time in your example!
something like:
$.when(
$.ajax( "fragments/employee.mustache" ),
$.ajax( "fragments/employee_day.mustache" ),
$.ajax( "..." )
)
.done(function( employeeRes, dayRes ) {
// the first item in array should be the data
var employeeTpl = employeeRes[0];
var dayTpl = dayRes [0];
// ...
});
There's loads of good examples at the jQuery Website

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.

$.when().then() not working with nested ajax calls

I have been trying to scroll the page to a dynamic div that is created by the an ajax call.
When #divnotifications div clicked (below), I make the first ajax call that adds the Post details, then within this ajax call, another ajax call is made to add the related comments to the div.
The part explained so far works great. Then, I use $.when().then() to scroll to a div item created based on the ajax calls. However, the page does not scroll to the element that was created by LoadCommentsForPost ajax call.
Did I get the logic of $.when().then() wrong?
$(document).on('click', '#divnotifications div', function (event) {
event.preventDefault();
$.ajax({
//other details
success: function (postid) {
$.when(DisplayPostWithFullDetails(postid)).then(function () {
//scroll to the content created by
//LoadCommentsForPost function nested
//inside DisplayPostWithFullDetails
});
}
});
});
function DisplayPostWithFullDetails(postId) {
$.ajax({
//other details
success: function (post) {
//The code to build the div to display the post -- working fine
LoadCommentsForPost(post.PostId);
}
});
}
function LoadCommentsForPost(postid) {
$.ajax({
//other details
success: function (response) {
var comments = JSON.parse(response);
DisplayComments(comments);//builds the div to display the comments - working fine
}
});
}
UPDATED CODE
After receiving some feedback, I ended up with the following code. However, it is still not working. It works only if I add some delay to make sure the div is loaded:
$(document).on('click', '#divnotifications div', function (event) {
event.preventDefault();
$.ajax({
//other ajax stuff
success: function (postid) {
DisplayPostWithFullDetails(postid).done(function () {
setTimeout(function () {
var scrollto = $("div[data-" + type.toLowerCase() + "displayform='" + relateditem + "']").offset().top;
$("html, body").animate({ scrollTop: scrollto }, 600);
}, 500);
});
}
});
});
function DisplayPostWithFullDetails(postId) {
jQuery.support.cors = true;
return $.ajax({
//other ajax stuff
success: function (post) {
post = JSON.parse(post);
//display the post details
LoadCommentsForPost(post.PostId);
}
});
}
function LoadCommentsForPost(postid) {
var promise = new $.Deferred();
jQuery.support.cors = true;
$.ajax({
//other ajax stuff
success: function (response) {
var comments = JSON.parse(response);
DisplayComments(comments);//this is not ajax
promise.resolve('loadedcomments');
}
});
return promise;
}
Did I get the logic of $.when().then() wrong?
Yes, you need to return a promise from the functions if you want to use the function with $.when:
function DisplayPostWithFullDetails(postId) {
return $.ajax({...
// ^^^^^^
That said, wrapping a single promise in $.when is useless.
$.when(DisplayPostWithFullDetails(postid)).then(function () {
should just be:
DisplayPostWithFullDetail(postid).then(function () {
Did I get the logic of $.when().then() wrong?
No, but you are NOT returning the promise so you can't use the promise functions like .then().
UPDATE:
I use $.when().then() to scroll to a div item created based on the ajax calls. However, the page does not scroll to the element that was created by LoadCommentsForPost ajax call.
For me this means that you need to wait that both ajax calls are resolved.
This fiddle show how it should work emulating the ajax call using setTimeout Fiddle.
Your code may look similar to:
function DisplayPostWithFullDetails(postId) {
var promise = new $.Deferred();
$.ajax({
//other details
success: function (post) {
//The code to build the div to display the post -- working fine
LoadCommentsForPost(post.PostId).then(function() {
promise.resolve();
});
}
});
return promise;
}
function LoadCommentsForPost(postid) {
return $.ajax({
//other details
success: function (response) {
var comments = JSON.parse(response);
DisplayComments(comments);//builds the div to display the comments - working fine
}
});
}
Now when you execute the function DisplayPostWithFullDetails it return a promise.
So you can use .then() method;
DisplayPostWithFullDetails(postid)).then(function () {});
or...
var promise = DisplayPostWithFullDetails(postid);
promise.then(function(data){});
Also the major advantage of use $.when() is that you can execute the .then() method when all the promises that you pass to it are resolved.
There are not need to use it when you are waiting for a single promise.

values won't get updated in a function because of a ajax call

I'm writing a javascript function that should change the view of a postcard depending on which case is selected.
My problem is that the old values of the object "m_case" is getting used. If I press the button with class "case-btn" twice I get the right case selected in my postcard view. But I want it to be triggered when I press the button once.
I guess I have to do something like a callback function in the m_case.setCase() function call, but I can't get it working,
$('.case-btn').on('click', function() {
remove_active_state_from_cases();
m_case.setCase($(this).data('case'));
// Changes the view of the postcard
m_postcard.changeBackground(m_case.getPhoto());
m_postcard.changeMessage(m_case.getMessageText());
m_postcard.changeFullName(m_case.getFullName());
m_postcard.changeCountry(m_case.getCountry());
$(this).toggleClass('active');
});
The setCase() function
this.setCase = function(id) {
// Set ID
this.setID(id);
var that = this;
$.ajax({
url: 'get_case_by_id.php',
type: 'get',
dataType: 'json',
data: {id: that.getID() },
success: function(data) {
if(data.success) {
that.setFirstName(data.first_name);
that.setFullName(data.full_name);
that.setAdress(data.adress);
that.setCountry(data.country);
that.setStamp(data.stamp);
that.setPhoto(data.photo);
that.setSummary(data.summary);
that.setStory(data.story);
that.setMessageText(data.message_text);
that.setDefaultMessageText(data.default_message_text);
that.setMessageImg(data.message_img);
} else {
console.log('failure');
}
}
EDIT The problem might be in my AJAX call, I have to wait till the ajax have been called. but how do I continue the first flow when my Ajax is done and not before?
SOLUTION
I made it working by adding a callback parameter and calling that function in the ajaxs complete function.
$('.case-btn').on('click', function() {
remove_active_state_from_cases();
var that = this;
m_case.setCase($(this).data('case'), function() {
m_postcard.changeBackground(m_case.getPhoto());
m_postcard.changeMessage(m_case.getMessageText());
m_postcard.changeFullName(m_case.getFullName());
m_postcard.changeCountry(m_case.getCountry());
$(that).toggleClass('active');
});
});
// Sets the whole case from an id.
this.setCase = function(id, callback) {
// Set ID
this.setID(id);
var that = this;
$.ajax({
url: 'get_case_by_id.php',
type: 'get',
dataType: 'json',
data: {id: that.getID() },
success: function(data) {
if(data.success) {
that.setFirstName(data.first_name);
that.setFullName(data.full_name);
that.setAdress(data.adress);
that.setCountry(data.country);
that.setStamp(data.stamp);
that.setPhoto(data.photo);
that.setSummary(data.summary);
that.setStory(data.story);
that.setMessageText(data.message_text);
that.setDefaultMessageText(data.default_message_text);
that.setMessageImg(data.message_img);
} else {
console.log('fail big time');
}
},
complete: function() {
callback();
}
});
}
Your m_postcard.changeBackground and other calls should be in the success callback, or in another function that is called from the success callback, as those values aren't set till the async ajax call is done.
With the way your code is now the m_postcard.changeBackground and other calls are called immediately after your setCase function is done executing. This means your methods are executed before the data has arrived from the server
While the ajax is processing you probably should show a loading message on the screen to let the user know they have to wait till the processing is done. Show the message before calling the .ajax call and hide it in the success/error callbacks.
Any changes to the site content should be done from the success callback, ie changing active states, changing content, etc.

Categories

Resources