How to run ajax request in array loop - javascript

ok so i am creating a web app and i have run into some issues
first i make a request to an api endpoint this returns a json response i take what i need and ad it into a key value array
i then have to loop through all the items in this array and for each item i need to make a request to a second api endpoint that returns some html
i need to then append this html to an eliment on the page
i need this to be done one after another the issue i have is that the .each() loop finishes instantly while the requests are still on going in the background meaning the aliments are appended to the html eliment on the page way after they should
how can i make the loop wait untill the requests are done and html is appended before moving onto the next item in the array
$("#middlebox").fadeTo(3000, 0, function() {
$("#middlebox").html(LOADING);
});
$("#middlebox").fadeTo(3000, 1, function() {
var API_URL = window.location.href + 'api.php?action=channels&category='+$this.data("text")+'&username='+$("#username").val()+'&password='+$("#password").val();
var CHANNELS = {};
var API_CHANNELS = '<div class="Live">';
$.getJSON(API_URL).done( function( API_RESULT ) {
$.each( API_RESULT.items, function( API_INDEX, API_ITEM ) {
var API_ID = API_ITEM.stream_id;
var API_ICON = API_ITEM.stream_icon;
CHANNELS[API_ID] = API_ICON;
});
}).then( function() {
$.each( CHANNELS, function( CHANNEL_KEY, CHANNEL_VALUE ) {
var EPG_URL = window.location.href + 'api.php?action=epg&id='+CHANNEL_KEY+'&username='+$("#username").val()+'&password='+$("#password").val();
API_CHANNELS += '<div class="channel focusable"><div class="LiveIcon"><img src="' + CHANNEL_VALUE + '" class="TvIcon"></div>';
$.ajax({
url:EPG_URL,
type: 'GET',
dataType: 'html',
success:function(content,code) {
API_CHANNELS += content;
}
});
API_CHANNELS += '</div>';
});
$("#middlebox").fadeTo(3000, 0, function() {
API_CHANNELS += '</div>';
$("#middlebox").html(API_CHANNELS);
$("#middlebox").fadeTo(3000, 1, function() {
});
});
});
});

Ajax calls are asynchronous so you can't use a synchronous loop to process the requests.
You can use Promise.all to wait for all ajax requests and then process replies in a loop.
Promise.all(CHANNELS.map(function( CHANNEL_KEY, CHANNEL_VALUE ) {
var EPG_URL = window.location.href + 'api.php?action=epg&id='+CHANNEL_KEY+'&username='+$("#username").val()+'&password='+$("#password").val();
return $.ajax({
URL: EPG_URL,
type: 'GET',
dataType: 'html'
}).then(function(content) {
return [CHANNEL_KEY, CHANNEL_VALUE, content];
});
})).then(function(channels) {
$.each(channels, function(CHANNEL_KEY, CHANNEL_VALUE, content) {
API_CHANNELS += '<div class="channel focusable"><div class="LiveIcon"><img src="' + CHANNEL_VALUE + '" class="TvIcon"></div>';
API_CHANNELS += content;
API_CHANNELS += '</div>';
});
$("#middlebox").fadeTo(3000, 0, function() {
/* ... */
});
});

Related

Communication beetween Javascript and PHP scripts in web app

THE CONTEXT
I'm developing a web app that loads contents dynamycally, retrieving data from a
catalogue of items stored as a MongoDB database in which records of the items and their authors are in two distinct collections of the same database.
Authors ID are stored in the item field creator and refer to the author field #id. Each item can have none,one or many authors.
Item sample
{
"_id" : ObjectId("59f5de430fa594333bb338a6"),
"#id" : "http://minerva.atcult.it/rdf/000000016009",
"creator" : "http://minerva.atcult.it/rdf/47734211-2637-3895-a690-4f33412931ec",
"identifier" : "000000016009",
"issued" : "fine sec. XIV - inizi sec. XV",
"title" : "Quadrans vetus",
"label" : "Quadrans vetus"
}
Author sample
{
"_id" : ObjectId("59f5d8e80fa594333bb1d72c"),
"#id" : "http://minerva.atcult.it/rdf/0007e43e-107f-3d18-b4bc-89f8d430fe59",
"#type" : "foaf:Person",
"name" : "Risse, Wilhelm"
}
WHAT WORKS
I query the database submitting a string in a form, using this PHP script
ITEM PHP SCRIPT
<?php
require 'vendor/autoload.php';
$title=$_GET['item'];
$client = new MongoDB\Client("mongodb://localhost:27017");
$db=$client->galileo;
$collection=$db->items;
$regex=new MongoDB\BSON\Regex ('^'.$title,'im');
$documentlist=$collection->find(['title'=>$regex],['projection'=>['_id'=>0,'title'=>1,'creator'=>1,'issued'=>1]]);
$items=$documentlist->toArray();
echo (json_encode($items));
?>
called by a Javascript script (new_search.js) using ajax, that has also the responsibility to attach to html document a <li class=item> for every item that matches the query, inserting the JSON fields and putting them in the provided tags ( <li class=item-name>,<li class=auth-name, and the last <li> in div class=item-info for date).
WHAT DOES NOT WORK
My intent is reproduce the pattern to retrieve author names from another collection in the same database, querying it using author field #id from the html tag <li class=auth-name, using a similar php script and a similar ajax call.
I tried to make a nested ajax call (in the one I used to retrieve the items infos) to invoke author_query.php that performs the MongoDB query on the collection of authors.
So, the question is: Is it possible use the $_GET superglobal to get the html tag that contains the author id #id in order to search it in the database?
Otherwise, how can I adjust the code to pass a javascript variable to php (not by user input) that lets me keep the content already loaded on the page?
UPDATES
To make clearer the question, I follow the tips in the comments and I updated my scripts using JSON directly to provide the needed data.
I also perfom a debug on the js code and it's clear that PHP don't provide any response,in fact ajax calls for authors name fails systematically.
I suppose that occurs because PHP don't receive the data dueto the fact I'm not using the correct syntax probably (in js code or in the php with $_GET or in both) to pass the variable author (I also tried data:'author='+author treating the JSON object author has a string). Anyway I don't understand what is the correct form to write the variable to pass using the data field of ajax().
MY SCRIPTS
JS SCRIPT new_search.js
$(document).ready(function () {
$("#submit").on("tap", function () {
var item = document.getElementById("search").value;
var author;
$.ajax({
url: "item_query.php",
type: "GET",
data: 'item=' + item,
dataType: "json",
async:false,
success: function (items) {
for (var i = 0; i < items.length; i++) {
$("#items-list").append(
'<li class="item">' +
'<div class="item-photo-container">' +
'<img src=images/item_126.jpg>' +
"</div><!--end item-photo-container-->" +
'<div class="item-info">' +
'<ul>' +
'<li><a><h3 class="item-name">' + items[i].title + '</h3></a></li>' +
'<li class="auth-name">' + items[i].creator+ '</li>' +
'<li>' + items[i].issued + '</li>' +
'</ul>' +
'</div><!--end item-info-->' +
'</li><!--end item-->'
);
}
}
});
$('.item').each(function () {
author = $(this).find('.auth-name').text();
if (author == 'undefined')
$(this).find('.auth-name').text('Unknown');
else if(author.indexOf(',')!=-1) {
author='[{"author":"'+author+'"}]';
author=author.replace(/,/g,'"},{"author":"');
author = JSON.parse(author);
console.log(author);
$.ajax({
url: "author_query.php",
type: "GET",
data: author,
dataType: "json",
processData: false,
success: function (auth_json) {
$(this).find('.auth-name').text('');
var author_text=' ';
for(var i=0;i<auth_json.length;i++)
author_text+=auth_json.name+' ';
$(this).find('.auth-name').text(author_text);
},
error: function () {
console.log('Error 1');
}
});
}
else{
author='{"author":"'+author+"}";
author=JSON.parse(author);
$.ajax({
url: "author_query.php",
type: "GET",
data: author,
dataType: "json",
processData: false,
success: function (auth_json) {
$(this).find('.auth-name').text(auth_json.name);
},
error: function () {
console.log('Error 2');
}
});
}
});
});
});
AUTHOR PHP SCRIPT author_query.php
<?php
require 'vendor/autoload.php';
$auth=$_GET['author'];
$client = new MongoDB\Client("mongodb://localhost:27017");
$db=$client->galileo;
$collection=$db->persons;
if(is_array($auth)){
foreach ($auth as $a){
$document=$collection->findOne(['#id'=>$a],['projection'=>['_id'=>0,'name'=>1]]);
$auth_json[]=( MongoDB\BSON\toJSON(MongoDB\BSON\fromPHP($document)));
}
}
else{
$document=$collection->findOne(['#id'=>$auth],['projection'=>['_id'=>0,'name'=>1]]);
$auth_json=( MongoDB\BSON\toJSON(MongoDB\BSON\fromPHP($document)));
}
echo (json_encode($auth_json));
?>
"I'm sure that authors array... is not empty and actually contains the authors IDs". You mean the jQuery object $('.item')? I think that it is empty, because it is created too soon.
The first $.ajax call sends an ajax request and sets a handler to add more stuff to the HTML, including elements that will match the CSS selector .item. But the handler doesn't run yet because it's asynchronous. Immediately after this, the object $('.item') is created, but it's empty because the new .item elements haven't been created yet. So no more ajax requests are sent. Some time later, the call to item_query.php returns, and the new HTML stuff is added, including the .item elements. But by now it's too late.
You say the array was not empty. I suspect you checked this by running the CSS selector after doing the search, after the return of the ajax call.
A lot of newbies have problems like this with asynchronous javascript. If you want to use the result of an asynchronous function in another function, you have to call the second function inside the callback function of the first one. (Actually there are more sophisticated ways of combining asynchronous functions together, but this is good enough for now.)
On a side note, you've done this in a slightly strange way where you save data in HTML, and then read the HTML to do some more stuff. I wouldn't use HTML as a storage place - just use variables like you would for most other things.
Try this:
$.ajax({
url: "item_query.php",
...
success: function (items) {
for (var i = 0; i < items.length; i++) {
var author = items[i].creator;
var authors;
// insert code here to generate authors from author.split(',') .
// authors should look something like this: [{author: 'http://minerva.atcult.it/rdf/47734211-2637-3895-a690-4f33412931ec'}] .
$.ajax({
url: "author_query.php",
type: "GET",
data: JSON.stringify(authors),
...
success: function (auth_json) {
...
},
error: function () {
console.log('Error 1');
}
});
$("#items-list").append(
'<li class="item">' +
'<div class="item-photo-container">' +
'<img src=images/item_126.jpg>' +
"</div><!--end item-photo-container-->" +
'<div class="item-info">' +
'<ul>' +
'<li><a><h3 class="item-name">' + items[i].title + '</h3></a></li>' +
'<li class="auth-name">' + items[i].creator+ '</li>' +
'<li>' + items[i].issued + '</li>' +
'</ul>' +
'</div><!--end item-info-->' +
'</li><!--end item-->'
);
}
}
});
I make the first call to retrieve the item infos asynchronous and the nested that search for the authors name synchronous. In this way I solved the problem.
For sure it is not the best solution, and it needs a quite long,but acceptable, time (<1 second) to load the content.
JS SCRIPT
$(document).ready(function () {
$("#submit").on("tap", function () {
var item = document.getElementById("search").value;
$.ajax({
url: "item_query.php",
type: "GET",
data: 'item=' + item,
dataType: "json",
success: function (items) {
for (var i = 0; i < items.length; i++) {
var authors_names=' ';
var authors= JSON.stringify(items[i]);
if(authors.indexOf('creator')!=-1){
if(authors.charAt(authors.indexOf('"creator":')+'"creator":'.length)!='[')
authors=authors.substring(authors.indexOf('"creator":"'),authors.indexOf('"',authors.indexOf('"creator":"')+'"creator":"'.length)+1);
else
authors=authors.substring(authors.indexOf('"creator"'),authors.indexOf(']',authors.indexOf('"creator"'))+1);
authors='{'+authors+'}';
//console.log(authors);
$.ajax({
url: "author_query_v3.php",
type: "GET",
data: 'authors='+authors,
dataType:"json",
async:false,
success: function (auth_json) {
authors=[];
authors=auth_json;
var author;
for(var j=0;j<authors.length;j++){
author=JSON.parse(authors[j]);
authors_names+=author.name+" | ";
}
console.log(authors_names);
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(jqXHR+' '+textStatus+ ' '+errorThrown);
}
});
}
else{
authors_names='Unknown';
}
$("#items-list").append(
'<li class="item">' +
'<div class="item-photo-container">' +
'<img src=images/item_126.jpg>' +
"</div><!--end item-photo-container-->" +
'<div class="item-info">' +
'<ul>' +
'<li><a><h3 class="item-name">' + items[i].title + '</h3></a></li>' +
'<li class="auth-name">' + authors_names+ '</li>' +
'<li>' + items[i].issued + '</li>' +
'</ul>' +
'</div><!--end item-info-->' +
'</li><!--end item-->'
);
}
}
});
});
});
PHP SCRIPT
<?php
require 'vendor/autoload.php';
$auth=$_GET['authors'];
$client = new MongoDB\Client("mongodb://localhost:27017");
$db=$client->galileo;
$collection=$db->persons;
$auth=json_decode($auth);
$auth=$auth->creator;
if(is_array($auth)) {
foreach ($auth as $a) {
$document = $collection->findOne(['#id' => $a], ['projection' => ['_id' => 0, 'name' => 1]]);
$auth_json[] = (MongoDB\BSON\toJSON(MongoDB\BSON\fromPHP($document)));
}
}
else{
$document=$collection->findOne(['#id'=>$auth],['projection'=>['_id'=>0,'name'=>1]]);
$auth_json[]=( MongoDB\BSON\toJSON(MongoDB\BSON\fromPHP($document)));
}
echo(json_encode($auth_json));
?>

How to speed up my Ajax call when there are multiple same ajax call made on a page?

When the page loads, I am also dynamically creating a block . I am using ajax call to go and fetch data from another page and then populating it and creating my structure that is then added to a particular dom element. However, the problem is when I do this several times on the page during page loads, it takes quite some time for all Ajax call to finish. Do you know how I can speed up the ajax call?
$('.content-tile').each(function (idx, ele) {
// Step 1: Get the stuffs and add it in the right place on page load
var node_id = $(ele).find('article').attr('data-history-node-id');
$.get('/node/' + node_id , function (data) {
var $title = $(data).find('.title').text();
var $summary = $(data).find('.article__body').text();
var $ctaText = $(data).find('.article__field-read-more-text').text();
var $redirectToFile = $(data).find('.article__field-nova-redirect-to-file').find('a').attr('href');
var $redirectToLink = $(data).find('.article__field-redirect-link').find('a').attr('href');
// Either redirect to file or redirect to link in the order
var $ctaLinkHref = $redirectToFile;
if ($redirectToLink) {
$ctaLinkHref = $redirectToLink;
}
var $contentHover = "<a class='content-added contenthover hoveredLink' href= " + $ctaLinkHref + "></a>";
$(ele).find('article').after($contentHover); // Add the div that will be targeted for the hover
var $contentHoverHeader = "<h2 class='contenthover__header'>" + $title + '</h2>';
var $contentHoverContent = "<p class='contenthover__content'>" + $summary + '</p>';
var $contentHoverLink = "<a class='contenthover__link' href=" + $ctaLinkHref + '>' + $ctaText + '</a>';
$(ele).find('.contenthover').append($contentHoverHeader, $contentHoverContent, $contentHoverLink);
});
});
As Rory mentioned, instead of calling multiple times, just create the single object, post it back and return all the related data in one go.
// construct the array
var nodes = []
$('.content-tile').each(function (idx, ele) {
var node_id = $(ele).find('article').attr('data-history-node-id');
nodes.push(node_id);
}
// call ajax now
$.ajax({
url: "/node/RetrieveDataByNodes", //the new method which can accept the array and return data
data: JSON.stringify(nodes),
type: 'GET',
dataType: 'json'
}).done(function(result) {
$.each(result, function (k, v) {
//do something for each value
console.log(v);
}
});

retrieving data from jsonp api that contains pages

I'm a little stuck on how I can retrieve all data from a jsonp api when the data is split in pages. The callback looks like this:
{
entries: [],
page: 1,
pages: 101,
posts: 10054
}
the code under only gets me results from page 1, but I would like to get results from all 101 pages. If I add a query to the url like : &page=2 or &page=3 I can access objects from that page only, what I'm trying to is access all objects from all pages in one go....
would appreciate some help :)
$.getJSON('http://hotell.difi.no/api/jsonp/mattilsynet/smilefjes/tilsyn?callback=?', function(data){
var html = "";
$.each(data.entries, function(key,value){
html += '<p>' + value.navn + '</p>';
})
$('#test').html(html);
})
You can make first call to get the number of pages and in next calls you can loop through them.
Try below code
$.getJSON('http://hotell.difi.no/api/jsonp/mattilsynet/smilefjes/tilsyn?callback=?', function(data) {
var pages = data.pages;
for (var i = 1; i <= data.pages; i++) {
$.getJSON('http://hotell.difi.no/api/jsonp/mattilsynet/smilefjes/tilsyn?callback=?&page=' + i, function(data) {
$('#test').append("Page " + data.page + " Data >> ");
var html = "";
$.each(data.entries, function(key, value) {
html += '<p>' + value.navn + '</p>';
})
$('#test').append(html);
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test">
</div>
You can use jsonp using $.ajax() not by $.getJSON(). and you have to declare your callback method in your javascript code
$("#btn").click(function(){
$.ajax({
url: "http://hotell.difi.no/api/jsonp/mattilsynet/smilefjes/tilsyn?callback=myJsonpCallbackMethod",
dataType: "jsonp",
success: function( response ) {
//console.log( response ); // server response
}
});
});
function myJsonpCallbackMethod(data){
alert("Hello");
console.log( data ); // server response
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<button id="btn">Click Me</button>

cancel all ajax request when search field is empty

Cancel all Ajax Request if search field is empty and if the search character length is < 3
$('#search-box').keyup(function() { // bind the search data
var input = $('.search-input').val();
if (input.length >= 3) {
$.getJSON({ // get JSON data
url: 'example.com?keyword=' + input,
success: function(data) {
// do processing.
var output = "<ul class='search-lists'>"; //output search data list
$.each(data, function(key, val) {
output += '<li><a>' + val.term + '</a></li>';
});
output += '</ul>';
$('.search-results').html(output);
}
}); // JSON request
}
}); // data bind
From your code it's OK to use but what happens when second request finish after first request? for example first send "ab" and send "abc" later, the second result will be replace with the first. You can use abort() to abort the first connection before start the second one.
if(ajaxRequest){ajaxRequest.abort();}
ajaxRequest = $.ajax({
url: '...',
success: function(){
/* ... */
}
});
Use keyup event of search inputbox instead of section element.
$('#search').keyup(function() { // bind the search data
var input = $('.search-input').val();
if (input.length >= 3) {
$.getJSON({ // get JSON data
url: 'example.com?keyword=' + input,
success: function(data) {
// do processing.
var output = "<ul class='search-lists'>"; //output search data list
$.each(data, function(key, val) {
output += '<li><a>' + val.term + '</a></li>';
});
output += '</ul>';
$('.search-results').html(output);
}
}); // JSON request
}
}); // data bind

jQuery - Do action when all ajax have completed

I've read about the jQuery's Deferred object, but I can't seem to make much sense out of it, here's my problem, I've got the following code:
function preprocess(form) {
$(form).find(".input input").each(function() {
var required = $(this).attr("required");
var checkField = $(this).closest(".inputcontainer").children(".check");
var errorField = $(this).closest(".inputcontainer").children(".errormessage");
if (typeof required !== 'undefined') {
$(checkField).each(function() {
$(this).css("color", "#FFFF00");
$(this).html("✘");
});
$(errorField).each(function() {
$(this).css("color", "#FFFF00");
$(this).html("(Required)");
});
}
else {
$(checkField).each(function() {
$(this).css("color", "#FFFF00");
$(this).html("✔");
});
$(errorField).each(function() {
$(this).css("color", "#000000");
$(this).html("");
});
}
});
$(form).find("datalist").each(function() {
var datalist = $(this);
callService({
name: "datalist_" + $(this).attr("id"),
data: { },
success: function(json) {
$(json).each(function() {
var html = "";
$(this.options).each(function() {
html += "<option value='" + this.value + "'>";
});
$(datalist).append(html);
});
}
});
});
$(form).find("select").each(function() {
var select = $(this);
callService({
name: "select_" + $(this).attr("name"),
data: { },
success: function(json) {
$(json).each(function() {
var html = "";
$(this.options).each(function() {
html += "<option value='" + this.id + "'>" + this.value + "</option>";
});
$(select).append(html);
});
}
});
});
}
This code prepares a form to be ready to be presented to the user, which involves AJAX calls, which I have wrapped in a callService({}); call, what you can see is the following:
It checks input and puts possibly (Required) next to the fields. (No AJAX)
It loads options from <datalist> and <select>s dynamically. (AJAX)
Then I also have the following (simplified):
function setContent(html, url) {
html = $.parseHTML(html);
$(html).filter("form").each(function() {
preprocess($(this));
});
$("#pagemain").html(html);
}
This gets html from an AJAX call, then calls preprocess on all its forms and updates the #pagemain.
However now data is being displayed before the preprocess has completely finished.
The question: How can I do the $("#pagemain").html(html); after preprocessed ánd involving AJAX processes, have been finished?
Try:
function preprocess(form) {
//Your above code is omitted for brevity
var promises = [];
$(form).find("datalist").each(function() {
var defered = $.Deferred();//create a defered object
promises.push(defered.promise());//store the promise to the list to be resolved later
var datalist = $(this);
callService({
name: "datalist_" + $(this).attr("id"),
data: { },
success: function(json) {
$(json).each(function() {
var html = "";
$(this.options).each(function() {
html += "<option value='" + this.value + "'>";
});
$(datalist).append(html);
});
defered.resolve();//resolve the defered when ajax call has finished
}
});
});
$(form).find("select").each(function() {
var defered = $.Deferred();//create a defered object
promises.push(defered.promise());//store the promise to the list to be resolved later
var select = $(this);
callService({
name: "select_" + $(this).attr("name"),
data: { },
success: function(json) {
$(json).each(function() {
var html = "";
$(this.options).each(function() {
html += "<option value='" + this.id + "'>" + this.value + "</option>";
});
$(select).append(html);
});
defered.resolve();//resolve the defered when ajax call has finished
}
});
});
return promises;
}
Your setContent:
function setContent(html, url) {
html = $.parseHTML(html);
var promises = [];
$(html).filter("form").each(function() {
promises = promises.concat(preprocess($(this)));//Concatenating all promises arrays
});
$.when.apply($,promises).then(function(){// Use $.when to execute a function when all deferreds are resolved.
$("#pagemain").html(html);
});
}
Deferred's can be a little intimidating to learn at first, but, like most things, once the light bulb goes on and you get it, it's pretty simple. The simple setup for creating a deferred object is like this:
var defer = $.Deferred(function(dfd) {
// do the processing you need, and then...
// when processing is complete, make a call to...
dfd.resolve(/* return data goes here, if required */);
}).promise();
// use the deferred object like it was an ajax call
defer.then(/* do the stuff that needed to wait */);
So, using your example:
function setContent(html, url) {
html = $.parseHTML(html);
var defer = $.Deferred(function(dfd) {
$(html).filter("form").each(function() {
preprocess($(this));
});
dfd.resolve();
}).promise();
defer.then($("#pagemain").html(html));
}
neat solution will be using when :
http://api.jquery.com/jQuery.when/
$.when( {//your preprocessing function here
} ).done(function( x ) {
//your done action here
});

Categories

Resources