Callback when AJAX calls are done using jQuery when - javascript

I am having trouble making the callback work correctly. Here is what I am trying to achieve: I have 2 items that I want to add to cart so I make 2 asynchronous POST requests. Once those two POST requests are complete, then I want to update the partial view of the cart. The issue is that it seems like only 1 item gets added to cart. When I debug it, then 2 of the items gets added. Any suggestion or help would be great. Thanks in advance!
here is my code:
var cart = []
function AddToCart(input) {
return $.ajax({
method: 'POST',
url: '/cart/add.js',
data: input,
dataType: 'json'
})
.done(function(data) {
return data;
});
}
$("#addToCart").click(function() {
$('#capsContainer input:checked').each(function() {
var cartItem = {
id: $(this).val(),
quantity: 1
}
cart.push(cartItem);
});
var test1 = AddToCart(cart[0]);
var test2 = AddToCart(cart[1]);
$.when.apply(test1, test2).done(function() {
$.getJSON('/cart.js', function(data) {
$('.cart-item-count').text(data.item_count);
$('.cart-item-price').html(formatMoney(data.total_price));
ShowCart();
});
})
});

Part of the issue is that your using ajax requests which could occur either before or after the code happens to handle those executions. Since they are async, its possible that they could fire/return before any other code runs on the page depending on how your browser's Javascript parser decides to execute the code. Rather than attempting to use Callbacks on asynchronous ajax requests, you can control the entire interaction by using a small 600 byte library called ajaxq.js
ajaxq.js essentially works just like jQuery's $.post() method, only you can also specify multiple queues by name to execute asyncronously, and attach a callback to them or whatever.
Here's a quick example of how you could setup your Ajax Cart Preview using this library.
/* queue up an AJAX request */
$.postq("set_data", "add_items.php", {"itemID": someItemID, "customerID": customerID },
function(result1) {
/* perform new request at the end of first request */
$.postq("get_data", "get_items.php", { "id": customerID },
function(result2) {
/* add in code to perform at the end of the second ajax POST request */
}); // end of request 2
}); // end of request 1
Here's your example using the ajaxq.js library:
function AddToCart(input) {
$.postq("add_item", "/cart/add/js", input, function(result) {
preview_items();
}, "json");
}
function preview_items() {
$.getJSON('/cart.js', function(data) {
$('.cart-item-count').text(data.item_count);
$('.cart-item-price').html(formatMoney(data.total_price));
ShowCart();
}
$(document).ready(function() {
$("#addToCart").on("click" function() {
$('#capsContainer input:checked').each(function(elem) {
var cartItem = {
id: $(this).val(),
quantity: 1
}
cart.push(cartItem);
});
var test1 = AddToCart(cart[0]);
var test2 = AddToCart(cart[1]);
});

Your AddToCart method already returns a deferred object. You are calling .done method twice.
I guess your code should look like this.
var cart = []
function AddToCart(input) {
return $.ajax({
method: 'POST',
url: '/cart/add.js',
data: input,
dataType: 'json'
});
}
$("#addToCart").click(function() {
$('#capsContainer input:checked').each(function() {
var cartItem = {
id: $(this).val(),
quantity: 1
}
cart.push(cartItem);
});
var test1 = AddToCart(cart[0]);
var test2 = AddToCart(cart[1]);
$.when(test1, test2).done(function() {
$.getJSON('/cart.js', function(data) {
$('.cart-item-count').text(data.item_count);
$('.cart-item-price').html(formatMoney(data.total_price));
ShowCart();
});
})
});

Related

How to insert data in function parameters in javascript?

I initialize Datatable on the page:
$("#datatable-buttons").DataTable()
The problem is that I need to form a parameters (300 lines of code) for this Datatable, so I make an object:
var myfunctions = {
parametrs: {
ajax: {
type: "POST",
data: data,
.....
},
inittable: function() {
a = $("#datatable-buttons").DataTable(this.parametrs);
}
The problem is that data has to change depending on user input. So I make a function:
var requestdata = function() {
return document.getElementById("daterange").value;
};
I change a data in parameters to this function
ajax: {
type: "POST",
data: requestdata()...
However, when page is first time initialized the function is being called and everything work excellent however when the code calls this function after user's input, it uses the parameters from initial page initialization, not asking it current (changed!!!) value.
It is using initial data because when myfunctions object is created, the parameters object is created with initial data.
var requestdata = function() {
return document.getElementById("daterange").value;
};
var myfunctions = {
parametrs: {
ajax: {
type: "POST",
data: requestdata(), // Pass this data as a function parameter
.....
},
inittable: function() {
a = $("#datatable-buttons").DataTable(this.parametrs);
}
}
Instead of saving the data, pass the data to the function call.

How to make a progress loading in Ajax function while the function is inserting the data in the database?

I have problem regarding inserting the data in the database. I have a function which inside the function has a 4 ajax, so to make story short.
I want to make a progress loading inside of on click function to determine that my 4 ajax inserting data already done or finish. after already insert the data in the database. It will automatically redirect my page to the other page. Is it possible to make it on the on click function? if so can you help me guys to make that kind of progress bar.
Goal: If I click the submit button, the progress bar will load automatically until the storing the data in the database finish, and after loading the progress bar it will automatically redirect to the other page.
I have here my on click function for submitting all the ajax function.
var orderNumber;
var orders = [];
var menu;
var customer_id = $('#hidden_customer_id').val();
$('#add_to_cart').on('click', function() {
orders = [];
menu = undefined;
$('.tbody_noun_chaining_order').children('tr').each(function() {
$row = $(this);
if ($row.hasClass('condimentParent')) {
// store a previous menu to the orders array if exists.
if (menu) {
orders.push(menu);
}
menu = {
'total': $row.find('.total').text(),
'name': $row.find('.parent_item').text(),
'customer_id': customer_id,
'condiments': {
'Item': [],
'Qty': [],
'Total': []
}
};
} else if ($row.hasClass('editCondiments')) {
menu.condiments.Item.push($row.find('.child_item').text());
menu.condiments.Qty.push($row.find('.condiments_order_quantity').text());
menu.condiments.Total.push($row.find('.total').text());
}
});
if (menu) {
orders.push(menu);
}
storeOrder(orders);
$('table#noun_chaining_order').find('.tbody_noun_chaining_order').empty();
$('.append_customer_noun_order_price').text('0.00');
$('.total_amount').text('0.00');
$('.total_amount').text('0.00');
$('.rate_computation').text('0.00');
});
This is my function inside of this are the ajax inserting data in the database.
function storeOrder(data) {
var customer_id = $('#hidden_customer_id').val();
var place_customer = $('#place_customer').text();
// reset
$id = "";
$total_amount = $('.total_amount').text();
$append_customer_noun_order_price = $('.append_customer_noun_order_price').text();
$tax_rate = $('.rate_computation').text();
$delivery_rate = $('.del_rate').text();
var sessionTransactionNumber_insert = localStorage.getItem('sessionTransactionNumber');
$.ajax({
url:'/insert_customer_order_properties',
type:'POST',
data:{
'hidden_customer_id': customer_id,
'hidden_customer_address': place_customer,
'sessionTransactionNumber': sessionTransactionNumber_insert
},
success:function(data) {
$id = data[0].id;
$.ajax({
url:'/insert_customer_payment_details',
type:'POST',
data:{
'hidden_customer_id': customer_id,
'total_amount': $total_amount,
'customer_sub_total': $append_customer_noun_order_price,
'tax_rate': $tax_rate,
'id_last_inserted': $id
},
success:function(data) {
localStorage.removeItem('sessionTransactionNumber');
}
})
}
})
for (var num in orders) {
// simulate storing the order
$.ajax('/insert_wish_list_menu_order', {
type: 'POST',
// as the call is asynchronous, make sure to provide all required reference data along with the call.
context: orders[num].condiments,
data: {
// 'json': '{"orderNumber":' + (orderNumber++) + '}',
'append_customer_noun_order_price': orders[num].total,
'append_customer_noun_order': orders[num].name,
'customer_id': customer_id
},
success: function(orderNumber) {
$order_number = orderNumber[0].id; // meron na tayong kuha ng main item..
$.ajax({
url:'/insert_customer_order_details_properties',
type:'POST',
data:{
'order_number': $order_number,
'data_attribute_wish_order_id': $id,
},
success:function(data) {
console.log(data);
}
})
if (orderNumber !== undefined) {
$.ajax('/insert_wish_list_menu_belong_condiments', {
context: orderNumber,
type: 'POST',
data: {
'ParentId': orderNumber,
'Item': this.Item,
'Qty': this.Qty,
'Total': this.Total
},
success: function(result) {
console.log(result);
// setTimeout(function () {
// swal({
// title: "Wow!",
// text: "Message!",
// type: "success",
// confirmButtonText: "OK"
// },
// function(isConfirm){
// if (isConfirm) {
// window.location.href = "//stackoverflow.com";
// }
// }); }, 1000);
},
})
}
}
})
}
}
There's functions in javascript namely .ajaxStart and .ajaxStop which you can work around. Typical example:
$(document).ready(function(){
$( document ).ajaxStart(function() {
//start progress bar
});
$( document ).ajaxStop(function() {
//stop progress bar and redirect
});
//other code here with you ajax calls
...
})
in this case $(document).ajaxStart() and $(document).ajaxStop() listen for any ajax call in the entire document
Read more about ajaxStart and ajaxStop
You could declare a variable to store the loading progress and in every ajax success calback you can increment this variable:
// declare inside click handler
var loadingPercentage = 0; // percentage
// in every success handler
loadingPercentage += 25;
When all 4 ajax calls were successful, you would have the variable set to 100%.
Depending on loadingPercentage you could display a progressbar.

Reusing a JavaScript AJAX call before another AJAX call

I have two buttons that both performs AJAX call:
$("#save").click(function() {
$.ajax({
type: "POST",
url: saveEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
})
$("#tag-as-final").click(function() {
$.ajax({
type: "POST",
url: finalizeEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
})
The requirement is that when the user click the finalize button, the system will first perform a save before actually tagging it as final. To reuse the code attached to the save button, I call the onclick listener of the save button before the actual AJAX call like this:
$("#tag-as-final").click(function() {
$("#save").click()
^^^^^^^^^^^^^^^^^^
$.ajax({
type: "POST",
url: finalizeEntryURL,
But it will not do "save-and-finalize-after" behavior since both AJAX calls are asynchronous. I need to run one after another, but cannot afford to make the AJAX call of the save button synchronous (I'm doing also a lot of other things while the tagging occurs occurs). I know this would be silly but I'm thinking something similar to...
$("#tag-as-final").click(function() {
$("#save").click().peformAsyc()
^^^^^^^^^^^^
$.ajax({
type: "POST",
url: finalizeEntryURL,
...that will force it to finish performing first the chained function before continuing, but I know that is not available. Is there any way to do this? My current work-around is placing the same save AJAX function inside the finalize AJAX function, though it doesn't allow me to code DRY (Don't Repeat Yourself):
$("#tag-as-final").click(function() {
$.ajax({
type: "POST",
url: saveEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
$.ajax({
type: "POST",
url: finalizeEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
},
error: function(r) {
...
}
})
})
It's pretty simple, you are better using jquery "promises". Like so:
var generalSettings = { }; //Settings for AJAX call.
var jqXHR = $.ajax(generalSettings); //Do AJAX call.
generalSettings.data = 'newdata'; //update generalSettings
jqXHR.done(function(data){
$.ajax(generalSettings); //New Petition with updated settings.
});
This is using ES6 promises and jQuery promises:
function doAjaxAsPromise(settings){
return new Promise(function(resolve){
var jqXHR = $.ajax(settings);
jqXHR.done(function(data){
resolve(data);
});
});
}
var settings = { };
var petition = doAjaxAsPromise(settings);
var secondpetition = petition.then(function(data){
//work with data
//new settings
var settings = { };
return doAjaxAsPromise(settings);
});
var thirdpetition = secondpetition.then(function(data){
//work with data
//new settings
var settings = { };
return doAjaxAsPromise(settings);
});
//If needed to reuse settings object outside promise scope:
//var settings = Object.create(settings);
Some other nice thing you can do for code reuse:
function save(settings) {
var prom = doAjaxAsPromise(settings);
return prom.then(function(data){
//do something with your data.
});
}
function tagAsFinal(savedPromise, settings){
return savedPromised.then(function(){
var prom = doAjaxAsPromise(settings);
return prom.then(function(data){
//work with data;
});
});
}
$('save').on('click', function(){
save(settings); //settings = $.ajax settings.
});
$('tagAsFinal').on('click', function(){
var generalSettings = { };
var settingsone = Object.create(generalSettings);
var settingstwo = Object.create(generalSettings);
var saved = save(settingsone); //$.ajax settings.
tagAsFinal(saved, settingstwo);
});
//Can still be reduced.

how to delay select-2, so that it waits for some time after user types in data

I know that the above can be achieved by using quietMillis in the AJAX call, but I am using query to cache the data. And it is here I am not able to delay the AJAX call. Below is the code
$('#AssetType').select2({
cacheDataSource: [],
placeholder: ' ',
quietMillis: 3000,
query: function q(query) {
self = this;
var key = query.term;
var cacheData = self.cacheDataSource[key];
if (cacheData) {
query.callback({
results: $.map(cacheData, function (item) {
return {
text: item.LongDescription,
id: item.AssetTypeID
}
})
});
return;
}
else {
$.ajax({
url: 'http://localhost:52377/api/reference/asset/types/' + key,
dataType: 'json',
type: 'GET',
quietMillis: 3000,
//data: function (query) {
// return { assetType: query.term, };
//},
success: function (data) {
self.cacheDataSource[key] = data;
query.callback({
results: $.map(data, function (item) {
return {
text: item.LongDescription,
id: item.AssetTypeID
}
})
});
},
cache: true
})
}
}
});
Is there any work around to delay the AJAX call so that the AJAX call is not fired for every keystroke?? The reason for using "query" is for caching, which is not achieved just by setting cache to true in the AJAX call.
According to the select2 documentation, you can do this easily.
A request is being triggered on every key stroke, can I delay this?
By default, Select2 will trigger a new AJAX request whenever the user changes their search term. You can set a time limit for debouncing requests using the ajax.delay option.
This will tell Select2 to wait 250 milliseconds before sending the request out to your API.
$('select').select2({
ajax: {
url: '/example/api',
delay: 250
}
});
I found a way to delay the triggering. I've used an implementation of debounce function in underscore.js . The code would now look like this
query: debounce(function q(query) {..
.....
}, 350),
Hope it helps someone.
Select2 (4.0.3) has an undocumented option: minimumInputLength
This option will prompt the user to fill in the minimum number of characters and then fire the selection

How to reset the data after init , Select2

I use Select2-4.0.0 I have been struggling with this problem for a whole day that to update the data.
I search every posts I can like Update select2 data without rebuilding the control, but none have them work.
What I need is really simple (a setter to data):
I have a diaglog, which had a select. Every time I open the diaglog, I will ajax for an data to keep it in a local array like:
when dialog open :
var select2List=syncToLoadTheData();//data for select2
$('#search-user-select').select2({ //here when secondly executed, the select2's data on UI does not refreshed
data:select2List,
matcher: function(params, data){
var key = params.term;
if ($.trim(key) === '') {
return data;
}
if( (matchKeyAndPinyin(key,data.text))){
return data;
}
return null;
}
}
But the problem is even though the list is changing, the select options does not change at all.Please note in my test case, every time i open the dialog, the data from server is changed:
What I had tried:
1.when init:
data: function() { return {results: select2List}; }// not work to show any data at all
2.when secondly open dialog:
$( "#search-user-select").select2('data',newdata,true);//not work to have the new data
3.when secondly open:
$("#search-user-select").select2("updateResults");//Error, does not have this method
And some other method like directly change the array's data(only one copy of the data), but none of them work.
I had the same problem before, my problem was i need to update the select2 after every ajax request with new data.
and this how i fixed my code.
EventId = $(this).attr('id');
$.ajax({
url: 'AjaxGetAllEventPerons',
type: 'POST',
dataType: 'json',
data: {id: EventId},
})
.done(function(data) {
$("#select2_job").select2({
minimumInputLength: 2,
multiple:true,
initSelection : function (element, callback) {
//var data = data;
callback(data);
},
ajax: {
url: "/AjaxGetAllPersonForEvent",
dataType: 'json',
quietMillis: 100,
data: function (term) {
return {
term: term
};
},
results: function (data) {
var myResults = [];
$.each(data, function (index, item) {
myResults.push({
'id': item.id,
'text': item.fullname
});
});
return {
results: myResults
};
}
}
});
I hope this example will help you to solve your problem

Categories