I have a form with 3 elements (Qty, UnitCost, and TotalPrice) that are calculated based on the results of other data earlier in the form.
Qty and UnitCost are filling in properly based on a jquery Get, however the Total Price, which I am just using plain old javascript doesn't update unless I make a change in earlier fields (after which it does update correctly).
I am still VERY new to jquery and am teaching myself as I go, so I am likely missing something.
The Form looks like this
A (Text), B (Dropdown), C(dropdown), Qty, UnitCost, TotalPrice
//Get the unit cost
$.get("calcCost.php", {item:item}, function(data) {
document.getElementById(unitCostField).value = data;
});
unitCost = document.getElementById(unitCostFiled).value;
The code for Qty is essentially the same - just the fields and php script are alerted. Both are working correctly.
However, when I try to calculate the TotalPrice (which is just Qty*UnitCost) it's not updating right away. It starts out as 0 - which is expected when either Qty or Unit Cost is not filled in yet.
//Total Cost
cost = unitCost * qty
document.getElementById(costField).value = cost;
(The variables inside document.getElementById are already defined elsewhere);
Any ideas?
You need to remember that A in AJAX stands for asynchronous which means that the result of an AJAX request will not be available immediately for you to use. Instead, the browser will execute the request, finish everything else that you have on your call stack and only then, providing your request returned a response by that time, will your ajax success execute.
The example you have provided is even more interesting because your cost calculation routine needs to run after both Qty and UnitCost requests finish, and only if both of them were successful. I believe the best solution to a problem like this, especially that you are already using jquery, are Deferred objects.
The jquery docs for $.when (which is the method you might want to consider using) show a solution to an almost exactly the same problem as yours.
However, for the sake of convenience (error handlers omitted for brevity):
var calcCostReq = $.get("calcCost.php", {item:item}).done(function(data) {
document.getElementById(unitCostField).value = data;
unitCost = document.getElementById(unitCostFiled).value;
});
var qtyReq = $.get("qty.php").done(function(data) {
//whatever you need to do when qty was successful
});
$.when(calcCostReq, qtyReq).done(function() {
cost = unitCost * qty
document.getElementById(costField).value = cost;
}
You are retrieving the unitCostField value before it has been populated by the AJAX request. Even though the unitCost assignment is below the $.get method, it will still execute before the AJAX request has finished.
Code that relies on a AJAX request must be handled inside the callback function.
var qty;
function calcCost() {
var unitCost = document.getElementById(unitCostField).value,
cost = unitCost * qty;
document.getElementById(costField).value = cost;
}
$.get("calcCost.php", {item:item}, function(data) {
document.getElementById(unitCostField).value = data;
calcCost();
});
Related
I have a large number of inputs on a page, each input is disabled (and hidden) by default unless a checkbox is checked. Checking a related checkbox enables the input for a user to type an amount - that works fine.
After I've typed an amount into a given box and I shift my blur focus to something else (indicating I'm done with this input), I'm looping through every visible input to check if it has an amount in it and then sending an ajax request to update the back-end (this also works but maybe approach is wrong?).
What doesn't work is when I loop through more than 5-10 checkboxes, it seems to be extremely slow or simply doesn't send the ajax requests.
Code the listens for an enabled/visible amount box to change:
$(document).on("blur", ".dollar-amount", function(){
MainPage.amountInputListener('add');
});
Here is the foreach loop, which updates each associated user's backend data with the amount in the visible field:
var MainPage = {
amountInputListener: function (type) {
$(".dollar-amount:visible").each(function () {
//Get the employee being updated
var empID = $(this).data('empid');
//get the amount
var amount = $(this).val();
//Send update request to backend
$.ajax({
type: "POST",
url: "update/amount?empid=" + empID + "&amt=" + amount + '&type=' + type,
dataType: "html"
});
});
},
}
The HTML for the input:
<input type="text" name="dollar_hour_amountX" value="0" class="form-control dollar-amount disabled" data-empid="1" tabindex="-1" disabled>
Note: dollar_hour_amountX, X is a dynamic number related to the row's employee ID. data-empid is the same dynamic number, also used in the for loop.
Things I've tried to ensure the loop works properly:
Adding async: false. This allows it to work 100% of the time, but it's extremely slow the more inputs that are added.
Adding a timeout of 100-1000ms around the entire function, this simply delays the round-trip time of the Ajax call.
I'm open to Vanilla JS suggestions if it aids in making the calls to my back-end much faster and consistent.
// capture the passed in event
$(document).on("blur", ".dollar-amount", function(e){
// give the element to the method
MainPage.amountInputListener(e.target, 'add');
});
var MainPage = {
// accept the element on the arguments
amountInputListener: function (element, type) {
// use the element in place of `this`
//Get the employee being updated
var empID = $(element).data('empid');
//get the amount
var amount = $(element).val();
//Send update request to backend
$.ajax({
type: "POST",
url: "update/amount?empid=" + empID + "&amt=" + amount + '&type=' + type,
dataType: "html"
});
},
}
Does not make sense to update everything, just update what changes.
$('.dollar-amount').on("change", function () {
console.log(this.value, $(this).data('empid'))
// make the one Ajax request
})
Or change your backend to be able to handle multiple things being sent up at once so you are not hammering the backend with a bunch of calls.
"I'm looping through every visible input to check if it has an amount in it and then sending an ajax request to update the back-end (this also works but maybe approach is wrong?)."
I would strongly recommend you change this approach. I suspect it will fix your issues. Don't loop through all of these each time. There is no need. Simply, on blur, just check if this specific input has changed, and then send an ajax call ONLY if that specific one was edited.
Just pass "this" into the amountInputListener as an argument, and then get rid of the above "each" function. The rest would be the same. Instead of $(this), just pass the argument value that represents "this" from the event.
The first and foremost thing is using a get http verb request for update should be avoided.
This is not per standard, usually get requests are used to retrieve data.
And the next thing is instead of making an ajax call for each element with callname .dollar-amount and visible, it is better to declare a global variable above the foreach block of type array of objects and then add each item in the block to that global variable and then finally make an ajax request after the for block execution is done
amountInputListener: function (type) {
var objList = [];
$(".dollar-amount:visible").each(function () {
//Get the employee being updated
var empID = $(this).data('empid');
//get the amount
var amount = $(this).val();
//Send update request to backend
objList.push({
'empId':empId,
'amt':amount,
'type': type
});
});
$.ajax({
type: "post",
url: "update/amount"
dataType: "application/json",
data:{'data':objList}
});
},
}
This way you can send all data in one shot to server and it really helps in the performance.
Note: code is just to give you an idea.
I have problem loading partial views in mvc. Im trying to load the view with jquery and it kind of works. It either displays two divs filled with the right information or four filled with the same. But it loops through the array so the in paramater changes and I get a warning.
The process or thread has changed since last step
I have a array in jquery that is filled with four values and I need the value in that list as a paramater in the ActionResult.
The code I have
public ActionResult TvShow(string channel)
{
var model = un.SortAllPrograms(channel);
return PartialView("TvShow", model);
}
and jquery
$(document).ready(function () {
var nameOfChannel = ["TV3", "TV4", "TV6", "TV8"];
debugger
$.each(nameOfChannel, function (index, value) {
$('.showContainer').append('<div class="number">' + value + '</div>');
$('.number').load('Home/TvShow', { channel: value });
});
});
I would really appreciate some help or advice on how to make this work.
The issue is, For each ajax call, you are loading the response of the ajx call to the div with class number. So the ajax call which gets the last response will be used to update all the div's with that css class. This is the reason you are seeing more than one divs with same response at time, but this will be also random because every time your server takes different time to respond for each calls. Sometimes, the call for TV6 will be the one which takes more time. Since ajax is asynchronous, when the response comes back, it updates all the available divs with class number at that point. It is possible that your loop is executing it's second iteration at that, so you have only 2 divs present with that css class, hence it will update only those 2 divs.
Ideally you want to load to the div which was created for the specific in the array you are looping.
This should do it.
$(function() {
var nameOfChannel = ["TV3", "TV4", "TV6", "TV8"];
$.each(nameOfChannel, function (index, value) {
var $d = $("<div class='number'>")
.load('#Url.Action("TvShow")',{ channel: value });
$('.showContainer').append($d);
});
});
Keep in mind that, your current approach is making n number of calls to server (where n is the size of the array). You might consider sending the array as the parameter and making a single call which can return the markup for all the items, resulting in one single call. Use the approach which makes more sense to your use case.
I have a function that queries a database for info, when a button is clicked. This info gets written to innerHTML of a label. When this function returns, I read the innerHTML of this label. Problem is, it always returns the old value, not the new value that was pulled from the database. The label on the scree is displaying the correct value, though. When I click the button again, the value that I was expecting on the previous click, is now given. Seems like a timing issue but can't seem to figure it out.
example:
SQL Data - cost = 10
I expect to see 10 alerted to me when I click the button. I get a blank alerted to me, even though 10 is now in the label. When I click the button again, 10 is alerted, but 20 is now in the label.
function getInfo() {
var ctlMonthly = document.getElementById("cellMonthlyCost")
getSQLData(ctlMonthly);
alert(ctlMonthly.innerHTML);
}
function getSQLData(ctlCell){
...
var my_ctlCell = document.getElementById(ctlCell);
$.each(objData.items, function() {
my_ctlCell.innerHTML = this.Param1
});
...
}
Thanks.
you need to add the alert after the data is received from the database. I am assuming that you're sending an ajax request to fetch data. You will be able to get the new value in the callback of you're ajax request function.
Currently what is happening in your code is that
1. getSQLData(ctlMonthly);
// This sends a request to the data base to fetch data
2. alert(ctlMonthly.innerHTML);
// This shows the current value in an alert
3. data is received and shown in the label
This process happens so fast that you don't notice the difference between step 2 and 3.
Is this what you want?
I used a callback function
function getInfo() {
var ctlMonthly = document.getElementById("cellMonthlyCost")
getSQLData(ctlMonthly,alertInfo);
}
function alertInfo(info){
alert(info);
}
function getSQLDate(ctlCell,callbackFn){
...
var my_ctlCell = document.getElementById(ctlCell);
$.each(objData.items, function() {
my_ctlCell.innerHTML = this.Param1;
callbackFn(this.Param1);
});
...
}
to piggyback on Himanshu's answer, your request to your server is async. Meaning javascript will execute the GET request and continue on with the script, when the requests comes back from the server it will run whatever callback you give it. ( i.e. update label tag )
assuming getSQLData is a ajax call or something promised based, something like:
function getSQLData(ctlCell){
return $.get('/sql/data').done(function () {
var my_ctlCell = document.getElementById(ctlCell);
$.each(objData.items, function() {
my_ctlCell.innerHTML = this.Param1
});
});
}
you can change your code to:
function getInfo() {
var ctlMonthly = document.getElementById("cellMonthlyCost")
getSQLData(ctlMonthly)
.done(function () {
alert(ctlMonthly.innerHTML);
});
}
Basically the difference is your telling javascript to alert the innerHTML after the requests comes back from the server.
The more correct answer would be to alert the data straight from the response instead of reading from the DOM.
I may be doing something very silly here. Basically a user can have multiple "sites" and for each site we have to run some calculations to come up with a total. We have a php page which does these calculations which we can call calculate.php. It returns a string ex: 50 which we then parse into a float in JS. So here's what I'm trying to do: get the total of all of the numbers outputted by calculate.php.
My idea was to loop through the sites, $.post() to calculate.php within every iteration (other things are being done too in the loop, less important) and add to a variable in the callback function. I believe my problem is that $.post() is async... Here is my sample code:
function systems(siteList){
var runningTotal = 0;
for (var ii=0,i<siteList.length,i++){
$.post("calculate.php",{foo:siteList[ii]},function(data){
// important part
runningTotal = runningTotal + data
})
}
//outside loop
alert(runningTotal)
}
This function may be imperfect, but my real question is how can I achieve the result I'm looking for here? Just so you know, runningTotal always alerts as 0 with the above code.
Thanks
EDIT: Thanks to all for your help. from what I can see, it is not wise of me to be using so many AJAX calls so instead of directly fixing this problem, I believe I will take a step back and take the advice of sending only one ajax callback to perform the task of summing for me. Thanks again
I'd suggest to make use of $.when and run a callback once all Ajax calls are done. At that moment you have access to the response of each Ajax call and you can perform any necessary computation.
For example:
function systems(siteList){
var promises = [];
for (var ii=0; i<siteList.length; i++){
// `$.post` returns a promise. We keep track of each promise.
promises.push($.post("calculate.php",{foo:siteList[ii]}));
}
$.when.apply($, promises).then(function() {
// this is called once the responses of all Ajax calls
// have been received
var sum = 0;
for (var i = 0, l = arguments.length; i < l; i++) {
sum += +arguments[i][0]; // value must be cast to number first
}
alert(sum)
});
}
Have a look at the jQuery documentation to learn more about promises: http://learn.jquery.com/code-organization/deferreds/.
In general though, it's better to make as few Ajax requests as possible. If you can change the server side code, I would change it to accept a list of values, not only one value, and let the server return the final result.
I recommend you package up your data on the client side (in JavaScript) and send it to the server in one AJAX request. Multiple requests waste bandwidth and time.
Or, if the calculations are simple and don't need a database or some other external dependency, just move your calculations to a library of javascript functions and do it all client side.
Leaving aside that there should probably be one request doing this sort of summing up the calculations on the server side (IMHO), use a counter and when it reaches 0 you know, you have all results collected:
function systems(siteList){
var runningTotal = 0;
var counter = 0;
for (var ii=0,i<siteList.length,i++){
counter++;
$.post("calculate.php",{foo:siteList[ii]},function(data){
// important part
runningTotal = runningTotal + data
counter--;
if(counter==0)
alert(runningTotal)
});
}
}
Not very fancy, but you could use a file on the server to store your running total.
As each ajax request is received, read/update total/write to a file on the server.
Then send that value back either after receiving a specific request (&request=send_total), or somehow appended to the output for each request ( 50|3753 ).
AJAX is asynchronous.
Do the alert inside of the AJAX callback.
I have a javascript function which needs to do a numerical calculation. Some of the numbers used in this calculation are stored in a database, and they will differ depending on how a user fills out an online form. Once the user fills out the form, they will click the CALCULATE button. At this time, in the JS function, I would like to use ajax to get values from a database that correspond to some other value chosen by the user.
For a simple example: there are 3 sizes of t-shirts, with different prices based on each size (stored in database). The user chooses the size, and when they click CALCULATE, I use ajax to get the price associated with the size they chose.
The question is, i want to use ajax to update some variables that I will use later on in the script. The way I am trying to do it now doesn't work, the variable in the script doesn't get updated from ajax, I can only access the value from the database inside the success function of the ajax call. I understand this is because ajax in asynchronous by nature, and it takes some time, waiting for the data to be returned from the server, while the function still continues to run
In the following example, the ajax call returns JSON data, and I have a function called isjson() that tests if the returned string is in fact JSON data.
Example code:
function calculate_cost(){
var price = 0;
var size = $('form#tshirt_form [name="size"] option:selected').val();
$.ajax({
url:'my_script.php',
type:'post',
data:'select=price&table=tshirts.prices&where=size = "' + size + '"',
success:function(data){
if(isjson(data)){
data = $.parseJSON(data);
data = data[0];
price = data['price'];
}else{
//display error getting data
}
}
});
// continue code for calculation
// this alert will display "0", but I want the price from the database in there
alert(price);
//perhaps do other ajax calls for other bits of data
//...
return final_price;
}
Does anyone know how I can accomplish this, updating variables with ajax in real-time??
Thanks a lot!
** EDIT **
Thanks everyone for the help, I understand about ajax being asynchronous. I would really like an answer where I don't have to continue the calculation inside the success function, because my actual problem involves many values from quite a few different tables. I would also like to be able to expand on the calculation in the future without it getting too convoluted. If this is not possible, ever, than i will have to live with that.
;-)
** EDIT 2 **
OK, we got the answer: of course it is right near the top on the docs page, :-/ sorry about that. The async property in jQuery ajax call. http://api.jquery.com/jQuery.ajax/
That is because ajax executes the request asynchronously by default and before the control reaches alert(price); the request has not yet executed and price still holds the old value.
If you want to execute it synchronously then you can set async to false.
$.ajax({
async: false,
.....
.....
});
you need to calculate inside the success function
function calculate_cost(){
var price = 0;
var size = $('form#tshirt_form [name="size"] option:selected').val();
$.ajax({
url:'my_script.php',
type:'post',
data:'query=select price from tshirts.prices where size = "' + size + '"',
success:function(data){
if(isjson(data)){
data = $.parseJSON(data);
data = data[0];
price = data['price'];
// continue code for calculation
// this alert will display "0", but I want the price from the database in there
alert(price);
//perhaps do other ajax calls for other bits of data
//...
}else{
//display error getting data
}
}
});
// return final_price; this function wont be able to return a value
}
ajax is asynchronous and for this reason you should refactor your code so that you do what you need to do in the callback
$.ajax({
url:'my_script.php',
type:'post',
data:'query=select price from tshirts.prices where size = "' + size + '"',
success:function(data){
if(isjson(data)){
data = $.parseJSON(data);
data = data[0];
price = data['price'];
//call another function (maybe make another ajax call) from here
dosomethingwithprice(price);
}else{
//display error getting data
}
}
});
Your ajax code takes time to execute (albeit not much); however the code after the ajax call is executed asynchronously, and most likely before the results of the ajax call come in.
Instead, why don't you try moving alert(price) into the body of the if(isjson(data)) region, and then execute a callback function which returns the price to whatever other utility you need it to be used at?
you have to do your calculation inside callback stack. Ajax work async, that means that, codes outsides callback function run before callback function start. So you should do your calculation in callback.
function calculate_cost(){
var price = 0;
var size = $('form#tshirt_form [name="size"] option:selected').val();
$.ajax({
url:'my_script.php',
type:'post',
data:'query=select price from tshirts.prices where size = "' + size + '"',
success:function(data){
if(isjson(data)){
data = $.parseJSON(data);
data = data[0];
price = data['price'];
}else{
//display error getting data
}
// continue code for calculation
// this alert will display "0", but I want the price from the database in there
alert(price);
//perhaps do other ajax calls for other bits of data
//...
return final_price;
}
});
}
I suggest having the ajax call return a deferred object. Then, when the final price is completely calculated, resolve the deferred object with that value.
function calculate_cost() {
var price = 0,
size = $('#tshirt_form [name="size"] option:selected').val(),
def = $.Deferred();
$.ajax({
data: {size:size},
url: 'my_script.php',
type: 'post',
dataType: 'json',
}).done(function(data){
data = data[0];
price = data['price'];
def.resolve(price);
}).fail(function(){
// do on error stuff
});
return def.promise();
}
// ...
calculate_cost("large").done(function(price){
alert(price);
}).fail(function(){
alert("Failed to retrieve price");
});