Jquery $.get execution order - javascript

I have doubt regarding the execution order for a jquery function i created for my project. The function is given below.
$('#s_cust').change(function(event) {
var custId = $("select#s_cust").val();
$.get('ContactAjax', {
custId: custId
}, function(jsonResponse) {
alert("jsonresp: " + jsonResponse);
cconjson = jsonResponse;
var select = $('#s_ccon');
$(select).find('option').remove();
$('<option>').text("Select").appendTo(select);
$.each(jsonResponse, function(key, value) {
$('<option>').val(key).text(value).appendTo(select);
});
});
if (cconjson != null) {
for (var j = 1; j <= i; j++) {
var select1 = $('#s_ccon' + j);
$(select1).find('option').remove();
alert("test");
$('<option>').text("Select").appendTo(select1);
$.each(cconjson, function(key, value) {
alert("key: " + key + " value:" + value);
$('<option>').val(key).text(value).appendTo(select1);
});
}
}
});
"'#s_ccon' + j" is given because I'm dynamically generating a text box based on the click of a button.
The problem that I have got while using this function is that, after refresh of the form page I change the value in my select list, s_cust, it goes into the ajax call retrieves my data and populate the s_ccon correctly. When I change value of s_cust again it executes the if loop first, then goes back and does the ajax function get, I understood this beacause the first alert that comes after the second change is alert("test"), then the alert("key: " + key + " value:" + value), after this alert("jsonresp: " + jsonResponse).
I don't know why this happens, please tell me the mistake I have made here.

Since Ajax is asynchronous java script does not wait for the ajax request to come back to solve this problem as mention in the question above I had to put the condition evaluation regarding ajax inside the json response function itself, so finally my code looks like this.
$('#s_cust').change(function(event) {
var custId = $("select#s_cust").val();
$.get('ContactAjax', {
custId: custId
}, function(jsonResponse) {
alert("jsonresp: " + jsonResponse);
cconjson = jsonResponse;
var select = $('#s_ccon');
$(select).find('option').remove();
$('<option>').text("Select").appendTo(select);
$.each(jsonResponse, function(key, value) {
$('<option>').val(key).text(value).appendTo(select);
});
if (cconjson != null) {
for (var j = 1; j <= i; j++) {
var select1 = $('#s_ccon' + j);
$(select1).find('option').remove();
alert("test");
$('<option>').text("Select").appendTo(select1);
$.each(cconjson, function(key, value) {
alert("key: " + key + " value:" + value);
$('<option>').val(key).text(value).appendTo(select1);
});
}
}
});
});

According to the documentaion of jQuery, $.get is an asynchronous function. It is equivalent to
$.ajax({
url: url,
data: data,
success: success,
dataType: dataType
});
So you cannot predict the order of execution.
If you want it to happen in a synchronous manner, use $.ajax directly and turn off async.
$.ajax({
url: url,
data: data,
async: false,
success: success,
dataType: dataType
});
Also agree, that it is recommended to place all the code inside the success callback and follow the async process.

Related

How to correctly populate a drop down list via ajax and MVC controller

I've searched quite a bit for this answer and can't find much that covers what I need.
I have some data stored in a db table I want to populate certain drop down lists with. On the document.ready I have an AJAX call to the controller requesting the data based on a parameter I send it. The controller returns the data as Json. I'm new to the process of Json so, figuring out what to with it once it returns is where I'm stuck.
I'm able display the data returned from the controller in an alert or console.log when it returns, so I know the right values are there, but I can't figure out how to populate the dropdown list with those values. All the data is, is about 5 to 10 ints. (not returned as ints, I know, but they're things like 65, 70, 2, 10, 11) I've tried some various options and nothing seems to work.
I can static the values in an array and that actually will populate the drop down list. I've tried populating that same array with the returned data, but no success that way. Here is the ajax call:
//Fill symbols drop down list
function returnSymbols(cc) {
var sendData = JSON.stringify({ 'ul': cc });
$.ajax({
url: '/Trucking/returnSymbols',
type: 'POST',
contentType: 'application/json',
data: sendData,
success: function (data) {
//alert('success');
console.log('success, yes');
alert(data);
var numbers = [];
var obj = jQuery.parseJSON(data);
/* If I do this and static these, it does work
var numbers = [1, 2, 3, 4, 5] */
var option = '';
for (var i = 0; i < numbers.length; i++) {
option += '<option value="' + numbers[i] + '">' + numbers[i] + '</option>';
}
$('#ddlMcalSymbols').append(option); //fill ddl with these values.
},
error: function () {
//alert('Error');
console.log('Error');
}
});
}
To reiterate I have tried things like numbers.push(obj) or even. .push(data), but those aren't working.
Since the controller returns a Json value I was under the impression I needed to parse that Json in order to do anything with it. Here is the controller if it helps at all:
[HttpPost]
public ActionResult returnSymbols(string ul)
{
List<Get_CIF_SymbolsVM> symbols;
Guid newGuid = Guid.Parse(ul); //parse param as Guid
using (TruckingDb db = new TruckingDb())
{
symbols = db.GetSymbols.ToArray().OrderBy(x => x.RID).Select(x => new Get_CIF_SymbolsVM(x)).ToList();
}
var syms = (from s in symbols
where s.UniqLineType == newGuid
select s.SymbolCode).Distinct();
return Json(syms, JsonRequestBehavior.AllowGet);
}
Any help would be greatly appreciated.
EDIT: Updating the process to explain a bit more.
Had some success, but it's still not correct.
Here is the ajax call. I changed just a few items. It brings back the correct data, but it displays all array items as one line. I need each value in the array as a single value in the drop down list.
var sendData = JSON.stringify({ 'ul': cc });
$.ajax({
url: '/Trucking/returnSymbols',
type: 'POST',
contentType: 'application/json',
data: sendData,
success: function (data) {
//alert('success');
console.log('success, yes');
alert(data);
var numbers = [];
numbers.push(data);
var option = '';
//Added two for loops to show what I've tried.
for (var i = 0; i < numbers.length; i++) {
option += '<option value="' + numbers[i] + '">' + numbers[i] + '</option><br>';
}
$('#ddlMcalSymbols').append(option);
//Tried this option to fill ddl
for (var i = 0; i < numbers.length; i++) {
option = '<option value="' + numbers[i] + '">' + numbers[i] + '</option><br>';
$('#ddlMcalSymbols').append(option);
}
//This Jquery foreach only returns one value to the ddl
$.each(numbers, function (i, value) {
console.log(value);
option += '<option value="' + value[i] + '">' + value[i] + '</option>';
});
$('#ddlMcalSymbols').append(option);
},
error: function () {
//alert('Error');
console.log('Error');
}
});
It brings back the data, but in the drop down both of the for loops above fill the ddl as one long looking string. "61,62,64,66,70,71,72" .. They don't show up as single select values.
I tried parts of the code, and it seems you are overlooking that the var numbers never acquires values.
I also usually prefer to create jquery objects rather than manually compile html; it is easier to develop this way. The code fails with more detail.
Something on the lines of:
var listEl=$('#ddlMcalSymbols');
for (var key in obj) {
jQuery('<option value="' + obj[key] + '">' + obj[key] + '</option>').appendTo(listEl);
}
but in better order
Worked out a solution that while it functions, there is some odd behavior with the CSS of it. I'm using a multiselect drop down called bootstrap-select. Has a .js and .css file. I needed to fill the multiselect drop down with values from a db instead of hard-coding them in with the method.
I use a post ajax call to send a parameter to the controller which retrieves the values I need based on it. I don't know if it's the bootstrap-select or a limitation with multiselect, but it did not like displaying the Json data. My ajax call is already parsing the Json, so that wasn't it. After multiple attempts and trials I figured out the only thing that really works is with an int array. When I had the string array it would display everything as either one long string or only one value. Additionally, even now with it working as I would like, I have to reload the page every time I make a change to the .js file i'm working on. That screws up the bootstrap-select.css file. NO IDEA AS TO WHY. What happens is every 3 to 4 page reloads the values are outside the drop down list and smooshed together like a bunch of unreadable text. (See pic above) I press ctrl + shft + R to clear the chromes cached css and it goes back to how it should look and function. Long-winded, but true. Here is my ajax call with some comments, so you can see what I did. I'm sure there may be more elegant and straightforward ways of doing this, but it was an improvement on what I already had. Maybe it will help someone else.
function returnSymbols(cc) {
var sendData = JSON.stringify({ 'ul': cc });
$.ajax({
url: '/Trucking/returnSymbols',
type: 'POST',
contentType: 'application/json',
data: sendData,
success: function (data) {
var num = [];
var num1 = [];
//Push all returned values into num array
$.each(data, function (index, value) {
num.push(value);
});
console.log(num); // console out to ensure values have been pushed
//convert the string array into an int array
for (var i in num) {
num1[i] = parseInt(num[i]);
}
console.log(num1); //conosle out to ensure values have parsed correctly
fillddl(num1); // send int array to fill drop down func
},
error: function () {
//alert('Error');
console.log('Error');
}
});
}
Then the Function to actually send the values to the drop down list. Very similar to what I've found in other methods.
function fillddl(sym)
{
var s = '';
for (var i = 0; i < sym.length; i++)
{
s += '<option value="' + sym[i] + '">' + sym[i] + '</option>';
}
$(".ddlMcalSymbols").html(s);
}
you can do something like this
In action method
[HttpPost]
public ActionResult getCicitesAction(int provinceId)
{
var cities = db.cities.Where(a => a.provinceId == provinceId).Select(a => "<option value='" + a.cityId + "'>" + a.cityName + "'</option>'";
return Content(String.Join("", cities));
}
The ajax call would be like this:
$("province_dll").change(function(){
$.ajax({
url: 'getCitiesController/getCitiesAction',
type: 'post',
data: {
provinceId: provinceIdVar
}
}).done(function(response){
$("cities_dll").html(response);
});

How to select a value after a drop down menu is dynamically created in javascript

I have this function
function actInfo(fund) {
var acct = $('.account');
$.ajax({
url: "account.php",
type: 'get',
dataType: 'json',
data: {fund: fund}
}).done(function (response) {
var len = response.length;
acct.empty();
for (var i = 0; i < len; i++) {
var acctNum = response[i]['ID'];
var acctName = response[i]['NAME'];
acct.append("<option value='" + acctNum + "'>" + acctNum + ' -- ' + acctName + "</option>");
}
acct.prepend("<option value=''>- Select An Account -</option>").val('');
}).fail(function (jqXHR, textStatus, error) {
console.log("actInfo: " + error);
});
}
I call this function in another ajax call because I need to send the function variable that I get from the server. In the same ajax call that I use to call actInfo(fund) I am also trying to select a value. Here is the main ajax call.
function getPoInfo(trnum) {
$.ajax({
url: "edit.php",
data: {trnum: trnum},
type: "GET",
cache: false,
dataType: "json"
}).done(function (poInfo) {
$('#fund').val(poInfo[0]['TRANFUND']);
actInfo(poInfo[0]['TRANFUND']);
$('#account1').val(poInfo[0]['TRANACCOUNT'].trim());
}).fail(function (jqXHR, textStatus, error) {
console.log("getPoInfo(tran_num): " + error);
});
}
The actInfo(fund) function works fine. It builds my dropdown menu. But I don't know how to select a value. I am assuming I am trying to select a value before the dropdown is loading. I don't understand callbacks or promises, and I tried .bind and tried to append .done and .load but I can't get a value selected. How do I do tell when the dropdown menu is finished loading and then select a value?
Since actInfo() method contains an asynchronous operation (basically the <select> element is only built after you have received and parsed the returned JSON response). Therefore, what you want is to:
create a new deferred object, i.e. var deferred = new $.Deferred()
return its immutable promise at the end, i.e. return deferred.promise().
The new deferred object should be resolved (deferred.resolve()) or rejected (deferred.reject()) based on the outcome of the inner AJAX request.
If we take all these points into account, your code can be easily be refactored as follow (I have added comments where I have added the suggested changes):
function actInfo(fund) {
var acct = $('.account');
// Create new deferred object
var deferred = new $.Deferred();
// Perform AJAX call as usual
$.ajax({
// Truncated for brevity
// ...
}).done(function (response) {
var len = response.length;
acct.empty();
for (var i = 0; i < len; i++) {
var acctNum = response[i]['ID'];
var acctName = response[i]['NAME'];
acct.append("<option value='" + acctNum + "'>" + acctNum + ' -- ' + acctName + "</option>");
}
acct.prepend("<option value=''>- Select An Account -</option>").val('');
// Now that the DOM has been built, we can resolve the promise and return it!
deferred.resolve();
}).fail(function (jqXHR, textStatus, error) {
console.log("actInfo: " + error);
// If we encounter an error, we pass it on!
deferred.reject(error);
});
// Return immutable promise
return deferred.promise();
}
Then, in your getPoInfo() method, simply check for the resolution of the returned promise from actInfo():
function getPoInfo(trnum) {
$.ajax({
// Truncated for brevity
// ...
}).done(function (poInfo) {
$('#fund').val(poInfo[0]['TRANFUND']);
// actInfo returns a promise!
var actInfoPromise = actInfo(poInfo[0]['TRANFUND']);
// Wait for the promise to resolve using $.when
$.when(actInfoPromise)
.then(function() {
// If successful, we select the correct <option>
$('#account1').val(poInfo[0]['TRANACCOUNT'].trim());
}, function(error) {
// If failed, we log the error message that has been passed on
console.log(error);
});
}).fail(function (jqXHR, textStatus, error) {
console.log("getPoInfo(tran_num): " + error);
});
}

Javascript Array loses data

I'm having trouble getting my information into an array in an ajax call, if I alert the information right after I insert it into the array it works fine, but if I do it at the end it alerts unidentified. I made sure that books is declared outside so it doesn't interfere.
var books = [];
$.ajax({
url: 'getFolderContents.php',
dataType: 'json',
success: function (data)
{
for(var i=0;i<data.length;i++) {
var amm = 0;
if(data[i].indexOf(".epub") !== -1) {
//$('#bTable').append("<td><a id = '" + data[i] + "' href = 'book.html'><img src = 'book.png' width = '100px'/><br/>" + data[i] + "</a></td>");
books.push(data[i]);
//alert(books[0]) Works if I call it from here, but not at the end.
}
}
},
error: function()
{
alert("error");
}
});
alert(books[0]);
Your
alert(books[0]);
will be executed while the Ajax call is running and therefore will not have any elements at this point of execution yet. Ajax is asynchronous - while you are doing a request to your PHP script your script continues execution.
Put all actions with books in your success function.
Another hint: As of jQuery version 1.8 you cannot longer use the parameter async: false to create a synchronous "A"jax call. You have to use the callback functions. Have a look at the docs for $.ajax
Your array hasn't lost any data; the data hasn't been put in there yet. The 'A' stands for "asynchronous", meaning your success callback hasn't run yet at the time you call the alert.
Put the alert inside your callback instead:
success: function (data)
{
for(var i=0;i<data.length;i++) {
var amm = 0;
if(data[i].indexOf(".epub") !== -1) {
//$('#bTable').append("<td><a id = '" + data[i] + "' href = 'book.html'><img src = 'book.png' width = '100px'/><br/>" + data[i] + "</a></td>");
books.push(data[i]);
//alert(books[0]) Works if I call it from here, but not at the end.
}
}
alert(books[0]);
},
Your alert is executing before the success function is called. Perhaps seeing the same code using a promise will make things clearer.
$.ajax( url: 'getFolderContents.php', dataType: "json" )
//the then function's first argument is the success handler
.then(function( data ) {
for(var i=0;i<data.length;i++) {
var amm = 0;
if(data[i].indexOf(".epub") !== -1) {
//$('#bTable').append("<td><a id = '" + data[i] + "' href = 'book.html'><img src = 'book.png' width = '100px'/><br/>" + data[i] + "</a></td>");
books.push(data[i]);
//alert(books[0]) Works if I call it from here, but not at the end.
}
alert(books[0]
});
});
I always feel this syntax makes async stuff make more sense. Otherwise this code functions exactly like Blazemonger's correct answer.
Your AJAX call is asynchronous, that's why it is undefined.
The alert at the end happens before the ajax success callback, because ajax is asynchronous.

ajax calls and append, cannot select appended content or do anything

I've two problems with this code.
1 $(container + ' meta[data-level="' + level + '"]').length == 0 is always zero
I know this because I create extra call create_views(1); which should not get added, but it gets appended anyhow.
2 dpUniSlider doesn't work as it doesn't see li created via ajax call. If I move it into success message it works fine, but outside function does not. Problem is if I include it inside ajax success it will get called several times as it is under a loop
//Show levels using ajax, before slider is activated
function create_views(level) {
$.ajax({
url: "actions.php",
type: "GET",
data: "show_level=" + level,
cache: false,
success: function (data) {
var view = '<li data-level="' + level + '">' + data + '</li>';
var container = ".slides_container";
if ($(container + ' meta[data-level="' + level + '"]').length == 0) {
$(container).append(view);
} else { //check if element exists, if yes overwrite it.
//$(container + ' meta[data-level="' + level + '"]').replaceWith(view);
alert("Exists");
}
}
});
}
//Loop through all levels and display views
//level count can be rewritten to come from DB and not be hardcoded like now
var levels = 2;
for (var i = 1; i <= levels; i++) {
create_views(i);
} // for loop
create_views(1); //test, delete this
//Activate slide
var unislider = $(".slides_container").dpUniSlider({
//loop: false,
draggable: false
});
For handling multiple parallel asynchronous ajax calls where you want to perform something after they have all completed, you could keep a counter and check in the success callback to see when the last ajax call has succeeded.
It would look like this:
$(function() {
var TOTAL_TASKS = 2,
completedTasks = 0;
function performAjaxTask(taskNumber) {
$.ajax({
url: url,
type: 'GET',
data: data,
cache: false,
success: function(data) {
// Process the data
completedTasks++;
if (completedTasks == TOTAL_TASKS) {
// Perform actions that need to wait until all
// ajax calls have returned successfully.
}
}
});
}
for (var i = 1; i <= TOTAL_TASKS; i++) {
performAjaxTask(i);
}
});
I believe you can use jQuery deferred objects for handling the multiple parallel asynchronous ajax calls where you want to perform something after they have all completed successfully.
Try this:
$(function() {
var LEVELS = 2,
$container = $('.slides_container'),
deferreds = []; // This will hold the deferred objects.
// This function returns a deferred object.
function getViewAndReturnDeffered(level) {
return $.ajax({
url: 'actions.php',
type: 'GET',
data: 'show_level=' + level,
cache: false,
success: function(data) {
var $currentView = $container.find('[data-level="' + level + '"]'),
$newView = '<li data-level="' + level + '">' + data + '</li>';
if ($currentView.length > 0) {
$currentView.replaceWith($newView);
} else
$container.append($newView);
}
}
});
}
for (var i = 1; i <= LEVELS; i++) {
// Put the deferred objects in the array.
deferreds.push(getViewAndReturnDeffered(i));
}
// The function passed to `.done()` will execute when all the deferred
// objects have completed successfully.
$.when.apply($, deferreds).done(function() {
var unislider = $container.dpUniSlider({
//loop: false,
draggable: false
});
});
});
Do you have a <meta></meta> inside .container? Probably not. Use
$(container + ' li[data-level="' + level + '"]')

ForEach Array value in jQuery JSON result

Okay, i want to process another javascript request foreach value returned inside a JSON response from a jQuery Request, Here's the current code i'm using for this request
function waitForEvents(){
$.ajax({
type: "GET",
url: "/functions/ajax.php?func=feed&old_msg_id="+old_msg_id,
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:50000, /* Timeout in ms */
success: function(data){
var json = jQuery.parseJSON(data);
**//Foreach json repsonse['msg_id'] call another function**
setTimeout('waitForEvents()',"1000");
},
error: function (XMLHttpRequest, textStatus, errorThrown){
alert("Error:" + textStatus + " (" + errorThrown + ")");
setTimeout('waitForEvents()',"15000");
},
});
};
for each json response variable ['msg_id'] i want to call another javascript function but don't know how to process the array using a foreach in javascript, any idea how ?
As you're already using jQuery, you can use the $.each function:
http://api.jquery.com/jQuery.each/
$.each(json.msg_id, function (index, value) {
// Do something with value here, e.g.
alert('Value ' + index + ' is ' value);
})
use a simple for loop, likely faster than for each
function myfunction(id){
alert("myid:"+id):
}
var i=0;
for (i=0; i< json.length;i++){
var thisId = json[i].msg_id;
myfunction(thisId);
}
simpler:
function myfunction(id){
alert("myid:"+id):
}
var i=0;
for (i=0; i< json.length;i++){
myfunction(json[i].msg_id);
}
since you asked:
function checkArrayElements(element, index, array) {
console.log("a[" + index + "] = " + element);
var myId = element.msg_id;
};
json.forEach(checkArrayElements);
and discussion in case older browsers where not implimented: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach so you can do that

Categories

Resources