ajax call, bug with appending results to options - javascript

I'm not having any truly functional issues with this script but there's a slight bug here that I can't figure out.
The process: The user types in the input field #productInput. On each keystroke, the value they've typed thus far is sent to an endpoint via ajax, query results are returned and appended to an <option> dropdown for a <datalist>. This works but here's the issue:
If I type 'TES' I can see 7 products (TEST1, TEST2, TEST3, TEST4, TEST5, TEST6, TEST7). But when I complete the word and type 'TEST', it only shows one or two of those results, even though they all contain the word TEST. Is there an obvious issue here causing this bug that I'm missing?
$('#productInput').on('input', function() {
let _this = $(this);
let foundOption;
let optSelector = `option[value='${_this.val()}']`;
if (_this.val() === '') {
return;
} else if ($('#returnedProducts').find(optSelector).length) {
console.log("else");
} else {
const searchResult = $(this).val();
$.ajax({ url: '/autocomplete',
data: {
search_result:searchResult
},
"_token": "{{ csrf_token() }}",
type: "POST",
success: function (response) {
console.log(response);
$("#returnedProducts").empty();
var result = response.hits.hits;
//for each result in the object, append to option list
for (let i = 0; i < result.length; i++) {
$("#returnedProducts").append($("<option/>",
{
"srindex": i,
"data-attributes": JSON.stringify(result[i]._source.frame.group),
"data-covers":JSON.stringify(result[i]._source.covers[1]),
"value": result[i]._source.group_cover_color,
"html": result[i]._source.group_cover_color,
}
));
}
}
});
}
});

Related

JavaScript is not catching the correct list item from JQuery-AJAX [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 2 years ago.
I have two functions main function which JQuery , and the other one in Javascript.
jQuery :
$(document).ready(function () {
$('#ProvisionLink').click(function () {
var queryTargetList = [ "Item1","Item2","Item3","Item4"]
var data = ["Data1","Data2","Data3","Data4" ]
event.preventDefault();
var i;
for(i = 0; i < queryTargetList.length; ++i) {
var dataItem = data[i];
var QueryItems = queryTargetList[i];
console.log("Before Launching the Javascript : " +QueryItems)
$.ajax({
url: '/operation',
data: {DataType: dataItem,},
type: 'POST',
success: function (response){ getInventory(response,QueryItems) },
error: function (error) {console.log(error);}
});
}
});
});
Java script :
function getInventory(data,queryTarget){
console.log("In Get Inventory Function in javascript ... " +queryTarget)
var queryTarget = "#"+queryTarget
// get the query id
const SelectBoxQuery = document.querySelector(queryTarget)
console.log(SelectBoxQuery)
// resetting the values to enter all the new one.
SelectBoxQuery.options.length = 0; // reset the values
// making a loop to reach each element item in the list
for (var i = 0; i < data.length; ++i) {
// add new element which is option to the targeted ID
var SelectBoxQuery_Addition = document.createElement('option');
// adding a text to that option
SelectBoxQuery_Addition.text = data[i];
// apply the adding .
SelectBoxQuery.add(SelectBoxQuery_Addition)
}}
The problem is, after i get response from python FLASK to Jquery function. in success part it should launch the javascript function with the same i of the queryTargetList.
for example if i pass data[1], i expect to have queryTargetList[1] also.
but in javascript console.log function. that does not happen. it printing the last item of the queryTargetList list.
Print:
Before Launching the Javascript : Item1
JQueryTests.js:11 Before Launching the Javascript : Item2
JQueryTests.js:11 Before Launching the Javascript : Item3
JQueryTests.js:11 Before Launching the Javascript : Item4
operationJavaScript.js:115 In Get Inventory Function in javascript ... Item4
In Get Inventory Function in javascript ... Item4
In Get Inventory Function in javascript ... Item4
In Get Inventory Function in javascript ... Item4
I do not know what am doing wrong :(
Since the ajax call is an async operation, the loop variable i would have changed by the time the success method gets called. There is no guarantee that the i would have reached the end.
You would need to wrap the variables, in a closure so that it success methods gets the correct item.
Sample code not tested.
$(document).ready(function () {
$('#ProvisionLink').click(function () {
var queryTargetList = [ "Item1","Item2","Item3","Item4"]
var data = ["Data1","Data2","Data3","Data4" ]
event.preventDefault();
var i;
for(i = 0; i < queryTargetList.length; ++i) {
var dataItem = data[i];
var QueryItems = queryTargetList[i];
console.log("Before Launching the Javascript : " +QueryItems)
(data,queryItem)=>{
$.ajax({
url: '/operation',
data: {DataType: data,},
type: 'POST',
success: function (response){ getInventory(response,queryItem) },
error: function (error) {console.log(error);}
});
})(dataItem,QueryItems);
}
});
});
set the async option to false:
$.ajax({
url: '/operation',
data: {DataType: dataItem,},
async: false,
type: 'POST',
success: function (response){ getInventory(response,QueryItems) },
error: function (error) {console.log(error);}
});
Another way: send queryTargetList[i] value to server and response get from server..
You must consider the issue of synchronization and non-synchronization, because you did not set the async attribute, so the default will be true. The easiest way is to set the async attribute to false
Like my example below, for detailed usage, you can refer to here
$.ajax({
url: '/operation',
data: {DataType: dataItem,},
type: 'POST',
async: false,
success: function (response){ getInventory(response,QueryItems) },
error: function (error) {console.log(error);}
});

How to Execute ajax call after the scroller reaches the bottom

I'm correcting working on lazy loading for 200 array of objects and APIs are provided to me to extract JSON from the server (by passing index, row count as parameter for the get AJAX and on response I get the data and the Boolean of whether there are more rows or not). But the problem is that initially I was able to get the data of 10 from the 200 but while I set the scroll function on the div it displays duplicate data which are already appended on the div. Stucked in this problem for a day.
Hope you guys shed some light on me.
var listgen = (function() {
var mc = {};
mc.startindex = 1;
mc.rowcount = 10;
mc.hasmorerows = false;
mc.entity = "requests"
//Declared variables:
mc.initComponent = function() {
var entity = "requests";
mc.callAjaxForList(mc.entity, mc.startindex, mc.rowcount);
$("#reqbody").on('scroll', function() {
if (mc.hasmorerows && ($(this)[0].scrollHeight <= $(this).scrollTop() + $(this).innerHeight())) {
console.log('reached')
mc.callAjaxForList(mc.entity, mc.startindex, mc.rowcount);
}
console.log("scroll");
})
}
mc.callAjaxForList = function(entity, startindex, rowcount) {
var options = {
"list_info": {
"row_count": rowcount,
"start_index": startindex
}
}
$("#reqbody").addClass("loading");
$.ajax({
url: "/data/" + entity,
data: {
"input_data": JSON.stringify(options)
},
contentType: "application/json; charset=utf8",
type: "GET",
success: function(json) {
mc.hasmorerows = json.list_info.has_more_rows
mc.onDataLoading(json);
},
});
}
mc.onDataLoading = function(json) {
//this is where i append the data from the json
mc.startindex += mc.rowcount
}
return mc;
})()
listgen.initComponent();
Scroll is a very high-frequent event, so I think that you have several ajax calls with same data before actually your onDataLoading called, and range incremented. So I whould add mutex.
// ...
mc.loaging = false; // mutex
$("#reqbody").on('scroll', function(){
if(mc.hasmorerows && ($(this)[0].scrollHeight<=$(this).scrollTop()+$(this).innerHeight())){
console.log('reached')
if (!mc.loading) // add check here
mc.callAjaxForList(mc.entity,mc.startindex,mc.rowcount);
}
console.log("scroll");
})
}
mc.callAjaxForList= function(entity,startindex,rowcount){
// ...
mc.loading = true;
$.ajax({
// ...
success:function(json){
mc.hasmorerows=json.list_info.has_more_rows
mc.onDataLoading(json) ;
mc.loading = false;
},
error: ()=> mc.loading = false
});
}
So our mc.loading will tell us if ajax already completed (do not forget to reset it's value on ajax error)

Success function not working based on submit

I have a Ajax call that is working, but the success function isn't. I have a a few dates that I am inputting, after hitting submit, there should be a little alert popup saying "Data saved to the DB". The data is getting saved to the DB, however I am not getting the popup alert window.
$("#btnSubmit").bind("click", function () {
createUpdateArrays();
var url = "/Sample/Selection";
$.ajax({
type: "GET",
url: url,
data: { ids: ids, dates: dates },
success: function (success) {
if (success === true) {
alert("Success");
}
else {
alert("error");
}
}
});
ids = "";
dates = "";
});
function createUpdateArrays() {
var i = 0;
$('input.remedy-id:checkbox').each(function () {
if ($(this).is(':checked')) {
var rid = $(this).attr("id");
$('.planned-date').each(function () {
var did = $(this).attr("id");
if (did === rid) {
var date = $(this).val();
ids += rid + ",";
dates += date + ",";
}
});
};
});
};
I can't seem to understand the reason behind this..
EDIT: Before doing ANYTHING else, make sure that your server is actually returning a response to begin with.
Your success function is expecting a boolean to be returned by the server, but this is probably not what is happening. If you're returning a simple string "success" from the server, then the comparison should be if (success === "success"). This is entirely dependent on what your server is returning as a response.
Perhaps your server is returning a status code of 2xx. In either case, you can use the jQuery status code callbacks:
$.ajax({
type: "GET",
url: url,
data: { ids: ids, dates: dates },
statusCode: {
200: function(){alert("Success!")},
201: function(){alert("Success!")}
}
});
And if you don't want to do that and just want to use the success callback, try something like this:
success: function (success) {
if (success || (success.length && success.length == 0)) { // this will almost definitely evaluate to true
console.log(success) // Do this to see what is actually being returned. I guarantee it isn't a boolean value.
alert("Success");
}
else {
alert("error");
}
}

Ajax calls inside loop need sequential responses

I need to make 3 or less ajax calls, and the responses need to be appended to the dom in the same order they were requested.
I have the following function, but the problem is that the responses that I get are not necessarily in the correct order when they get appended to the dom.
I wouldn't want to use the async: false property because it blocks the UI and it's a performance hit of course.
mod.getArticles = function( ){
//mod.vars.ajaxCount could start at 0-2
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[i];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
});
}
};
You need to append the article to a certain position, based on for example the i variable you have. Or you could wait for all of the requests and then append them in order. Something like this:
mod.getArticles = function( ){
var load = function( id ) {
return $.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
});
};
var onDone = function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
};
var requests = [];
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
requests.push(load(mod.vars.pushIds[i]));
}
$.when.apply(this, requests).done(function() {
var results = requests.length > 1 ? arguments : [arguments];
for( var i = 0; i < results.length; i++ ){
onDone(results[i][0]);
}
});
};
Here is an example using i to append them in the proper order when they all finish loading:
mod.getArticles = function( ){
// initialize an empty array of proper size
var articles = Array(3 - mod.vars.ajaxCount);
var completed = 0;
//mod.vars.ajaxCount could start at 0-2
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
// prevent i from being 3 inside of done callback
(function (i){
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[i];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
completed++;
if (data.length) {
// store to array in proper index
articles[i - mod.vars.ajaxCount] = data;
} else {
console.error('get article ajax output error');
}
// if all are completed, push in proper order
if (completed == 3 - mod.vars.ajaxCount) {
// iterate through articles
for (var j = mod.vars.ajaxCount; j < 3; j++) {
// check if article loaded properly
if (articles[j - mod.vars.ajaxCount]) {
mod.appendArticle(articles[j - mod.vars.ajaxCount]);
}
}
}
});
}(i));
}
};
var success1 = $.ajax...
var success2 = $.ajax...
var success3 = $.ajax...
$.when(success1, success2, success3).apply(ans1, ans2, ans3) {
finalDOM = ans1[0]+ans2[0]+ans3[0];
}
Check this for more reference. This is still async, but it waits for all of them to complete. You know the order of invocation already, as its done through your code, so add the dom elements accordingly.
Solutions that rely solely on closures will work up to a point. They will consistently append the articles of a single mod.getArticles() call in the correct order. But consider a second call before the first is fully satisfied. Due to asynchronism of the process, the second call's set of articles could conceivably be appended before the first.
A better solution would guarantee that even a rapid fire sequence of mod.getArticles() calls would :
append each call's articles in the right order
append all sets of articles in the right order
One approach to this is, for each article :
synchronously append a container (a div) to the DOM and keep a reference to it
asynchronously populate the container with content when it arrives.
To achieve this, you will need to modify mod.appendArticle() to accept a second parameter - a reference to a container element.
mod.appendArticle = function(data, $container) {
...
};
For convenience, you may also choose to create a new method, mod.appendArticleContainer(), which creates a div, appends it to the DOM and returns a reference to it.
mod.appendArticleContainer = function() {
//put a container somewhere in the DOM, and return a reference to it.
return $("<div/>").appendTo("wherever");
};
Now, mod.getArticles() is still very simple :
mod.getArticles = function() {
//Here, .slice() returns a new array containing the required portion of `mod.vars.pushIds`.
//This allows `$.map()` to be used instead of a more cumbersome `for` loop.
var promises = $.map(mod.vars.pushIds.slice(mod.vars.ajaxCount, 3), function(id) {
var $container = mod.appendArticleContainer();//<<< synchronous creation of a container
return $.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML'
}).then(function(data) {
if (data.length) {
mod.appendArticle(data, $container);//<<< asynchronous insertion of content
} else {
return $.Deferred().reject(new Error("get article ajax output error"));
}
}).then(null, function(e) {
$container.remove();//container will never be filled, so can be removed.
console.error(e);
return $.when(); // mark promise as "handled"
});
});
return $.when.apply(null, promises);
};
mod.getArticles() now returns a promise of completion to its caller, allowing further chaining if necessary.
Try utilizing items within mod.vars array as indexes; to set as id property of $.ajaxSettings , set returned data at this.id index within an array of responses. results array should be in same order as mod.vars values when all requests completed.
var mod = {
"vars": [0, 1, 2]
};
mod.getArticles = function () {
var results = [];
var ids = this.vars;
var request = function request(id) {
return $.ajax({
type: "GET",
url: "/ajax/article/" + id + "/",
// set `id` at `$.ajaxSettings` ,
// available at returned `jqxhr` object
id: id
})
.then(function (data, textStatus, jqxhr) {
// insert response `data` at `id` index within `results` array
console.log(data); // `data` returned unordered
// set `data` at `id` index within `results
results[this.id] = data;
return results[this.id]
}, function (jqxhr, textStatus, errorThrown) {
console.log("get article ajax error", errorThrown);
return jqxhr
});
};
return $.when.apply(this, $.map(ids, function (id) {
return request(id)
}))
.then(function () {
$.map(arguments, function (value, key) {
if (value.length) {
// append `value`:`data` returned by `$.ajax()`,
// in order set by `mod.vars` items:`id` item at `request`
mod.appendArticle(value);
} else {
console.error("get article ajax output error");
};
})
});
};
mod.getArticles();
jsfiddle http://jsfiddle.net/6j7vempp/2/
Instead of using a for loop. Call your function in response part of previous function.
//create a global variable
var counter = 0;
function yourFunc(){
mod.getArticles = function( ){
//mod.vars.ajaxCount could start at 0-2
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[counter ];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
//increment & check your loop condition here, so that your responses will be appended in same order
counter++;
if (counter < 3)
{ yourFunc(); }
});
};
}
I'm faced same problem i'm solve this problem using following way.
just use async for get sequence wise response
<script type="text/javascript">
var ajax1 = $.ajax({
async: false,
url: 'url',
type: 'POST',
data: {'Data'},
})
.done(function(response) {
console.log(response);
});

Finding properties in a javascript object

I have a javascript object, which I have received from a JSONP file.
The object contains multiple "options" and "results", which are used to adjust the html on the page when a user clicks.
Right now, I am able to check if the HTML string (inserted via json reference) exists in the json file. What I want to do is take that string, find the next "result" or "option" in the json file, and then return that "option" or "result" value so I can use it to change the html...
How do I do that? I've been trying the .indexOf method to find the current index but that doesn't really help me find a specific property like an "option".
This is the code I'm using to iterate through the JSONP file and find if the current string exists.
$.ajax({
url: "http://www.myurl.com/jsonp.php",
type: "GET",
dataType: "jsonp",
jsonpCallback: "otmjsonp",
async: false,
success: function (JSON) {
$(".result").on("click", function () {
var currentResult = $(this).text(); //.result is the line of HTML the user has clicked
for (var playerSelection in JSON) {
if (JSON.hasOwnProperty(playerSelection)) {
if (JSON[playerSelection] === currentResult) {
alert("this selection exists in the JSON");
}
}
}
})
}
});
And here is a VERY simple version of the large JSONP file:
otmjsonp({
"situation1" : "Your opponent is trying to tackle you", "playerPrompt1" : "What will you do first?",
"option1" : "avoid him",
"result1" : "he tackles you",
"situation2" : "you were tackled", "playerPrompt2" : "Your opponent begins to slow down",
"option2" : "chase after him",
"result2" : "you caught up",
)}
etc. etc.
Even vague ideas/directions would be appreciated as I'm completely stuck.
Part of the issue here is how you've coupled your UI with your data initialization. What I think you really want to do is to separate out the JSON request getting the data from the handling of the click.
$(function() {
var setupHTML,
handleClick,
updateHTML,
originalData,
resultData;
updateHTML = function(JSON) {
// Add or activate the html the person is clicking
$('.result').text()
};
handleClick = function(e) {
var currChoice = $(e.target).text();
if (resultData === undefined) {
e.stopPropagation();
e.preventDefault();
return;
}
for (var ps in resultData) {
if (resultData.hasOwnProperty(ps) && resultData[ps] === currChoice) {
resultData = resultData[ps];
updateHTML(resultData);
}
}
}
$('.result').on('click', handleClick)
$.ajax({
url: "http://www.myurl.com/jsonp.php",
type: "GET",
dataType: "jsonp",
jsonpCallback: "otmjsonp",
async: false,
success: function(data) {
resultData = origData = data;
// make the UI visible
setupHTML(JSON);
}
});
});
If you re-structure your JSON to nest the options/result inside the respective parent it becomes easy to get all the possible options. You would need to change your code to this:
$.ajax({
url: "http://www.myurl.com/jsonp.php",
type: "GET",
dataType: "jsonp",
jsonpCallback: "otmjsonp",
async: false,
success: function (JSON) {
$(".result").on("click", function () {
var currentResult = $(this).text(); //.result is the line of HTML the user has clicked
if (JSON.hasOwnProperty(playerSelection)) {
for (var outcome in JSON[playerSelection]) {
if (JSON[playerselection].hasOwnProperty(outcome)) {
alert("This is the next outcome " + JSON[playerSelection][outcome]);
}
}
}
})
}
});
I would suggest thinking through and organizing your JSON structure before progressing much further. Organized and logical JSON will make the Javascript easier. For this situation -- as much as I can glean from the description and example -- I think a JSON structure that would make logical sense and prove useful in later Javascript might look something like this:
{
'situations': [
{
'description': 'Your opponent is trying to tackle you.',
'prompt': 'What will you do?',
'options': [
{
'action': 'Avoid him.',
'result': 'He tackles you.'
},
{ /* another option (an action plus a result) */ }
]
},
{ /* another situation (a description, a prompt, and an array of options) */ }
]
}
I know this isn't a complete answer to your problem, but I think it would be a good place to start re-thinking your approach.
You access an Object property like:Object.property or Object['some property']. You can use a for in loop to loop over Objects, and most Arrays, like:
var property, value;
for(var i in Object){
property = i;
value = Object[i];
}

Categories