Reputation: 163
I have a problem when using the jquery.each()
function within a jquery.getJSON
.
There's 2 types of element's I'm dealing with in my code in this case. "Sources" and "Streams"
I want to use getJSON
to first get the sources, iterate over them and generate an accordion header out of those. Then, for each of those "sources" I again use getJSON
with that source's id to get it's corresponding "streams". Then I append those streams to it's sources accordion body, to get a list of all the streams, sorted by their sources.
But it seems while I'm getting the JSON, the next statements in my procedure are already executed. As I'm basically dynamically building a large HTML String and adding it to an element using jQuery, the String doesn't get all the data it needs.
The code looks as follows:
var html1 = "<div class='panel-group' id='manageAccordion'>";
$.getJSON(url, function(data){
$.each(data, function(i, json){
html1 += getAccordionPart(...); //creates the accordion for the given source
});
}).done(function(){
html1 += "</div>";
$('#elementList').html(html);
});
function getAccordionPart(id, parent, count, json){
//new string html2 is created and a bunch of stuff added
//...
html2 += "....";
html2 += getAccordionBody(json);
html2 += "</div></div></div></div>";
return html2
}
function getAccordionBody(json){
//new string "html3" gets created
//...
var url = standardUrl + "sources/" + encodeURIComponent(json.elementId) + "/streams";
$.getJSON(url, function(data) {
$.each(data, function(i, json) {
html3 += "<li class='list-group-item'>";
html3 += json.name;
html3 += "</li>";
});
}).done(function(){
html3 += "</ul>";
return html3;
});
}
What I specifically end up with is an accordion header that has "undefined" in it's body,
because it seems the getAccordionBody()
function doesn't return before the html string gets added to the DOM.
I already tried changing the $.getJSON
to $.ajax
with async = false
, on both of my $.getJSON
calls, this seems to fix the problem that the statements don't execute in the order I want them to, but it's horribly slow and returns undefined anyway for some reason..
Any suggestions? Am I missing something really stupid?
Upvotes: 1
Views: 493
Reputation: 346
You have to first collect everything which is required for "getAccordionPart" and "getAccordionBody" (eg: encodeURIComponent(json.elementId)) in a global array. like:
var arrIds = [];
$.getJSON(url, function(data){
$.each(data, function(i, json){
arrIds.push(encodeURIComponent(json.elementId))//collect everything which is required for "getAccordionPart" and "getAccordionBody"
});
then Iterate on this collection and collect all the data for accordian body of these ids in any global variable.
var bodyData = {};
$.each(arrIds, function(){
var url = standardUrl + "sources/" + this + "/streams";
$.getJSON(url, function(data) {
bodyData[this] = data;//collect all the Data for accordian body of these ids
}});
Also do a litle change in getAccordionPart like:
function getAccordionPart(id, parent, count, /*json*/ elementId){
//new string html2 is created and a bunch of stuff added
//...
html2 += "....";
html2 += getAccordionBody(elementId);
html2 += "</div></div></div></div>";
return html2
}
also change getAccordionBody like:
function getAccordionBody(/*json*/ elementId){
//new string "html3" gets created
//...
var data = bodyData[elementId];
$.each(data, function(i, json) {
html3 += "<li class='list-group-item'>";
html3 += json.name;
html3 += "</li>";
});
html3 += "</ul>";
return html3;
}
Your Calling will also be changed:
$.each(arrIds , function(){
html1 += getAccordionPart(,,,this); //creates the accordion for the given source
});
You Final code will be:
var arrIds = [];
$.getJSON(url, function(data){
$.each(data, function(i, json){
arrIds.push(encodeURIComponent(json.elementId))//collect everything which is required for "getAccordionPart" and "getAccordionBody"
});
var bodyData = {};
$.each(arrIds, function(){
var url = standardUrl + "sources/" + this + "/streams";
$.getJSON(url, function(data) {
bodyData[this] = data;//collect all the Data for accordian body of these ids
}});
function getAccordionPart(id, parent, count, /*json*/ elementId){
//new string html2 is created and a bunch of stuff added
//...
html2 += "....";
html2 += getAccordionBody(elementId);
html2 += "</div></div></div></div>";
return html2
}
function getAccordionBody(/*json*/ elementId){
//new string "html3" gets created
//...
var data = bodyData[elementId];
$.each(data, function(i, json) {
html3 += "<li class='list-group-item'>";
html3 += json.name;
html3 += "</li>";
});
html3 += "</ul>";
return html3;
}
$.each(arrIds , function(){
html1 += getAccordionPart(,,,this); //creates the accordion for the given source
});
Upvotes: 0
Reputation: 664620
it seems the getAccordionBody() function doesn't return before the html string gets added to the DOM
That's correct. Your way of return
ing the response (html3
) from ajax is flawed - you cannot return
from a done
handler.
You can however fix this by using promises. Every function returns a promise so that they are easily chained, and then
is used to transform the data and get a new promise for it:
$.getJSON(url).then(function(data){
return $.when.apply($, $.map(data, function(i, json){
return getAccordionPart(…); //creates the accordion for the given source
}).then(function() {
var html1 = "<div class='panel-group' id='manageAccordion'>";
html1 += Array.prototype.join.call(arguments, "\n");
html1 += "</div>";
return html1;
});
}).done(function(html){
$('#elementList').html(html);
});
function getAccordionPart(id, parent, count, json){
return getAccordionBody(json).then(function(result) {
var html2 = "…"; //new string html2 is created and a bunch of stuff added
html2 += result;
html2 += "</div></div></div></div>";
return html2;
});
}
function getAccordionBody(json) {
var url = standardUrl + "sources/" + encodeURIComponent(json.elementId) + "/streams";
return $.getJSON(url).then(function(data) {
var html3 = "…"; //new string "html3" gets created
$.each(data, function(i, json) {
html3 += "<li class='list-group-item'>";
html3 += json.name;
html3 += "</li>";
});
html3 += "</ul>";
return html3;
});
}
Upvotes: 1
Reputation: 1295
Rather than using sync calls you can use the Deferred / promise pattern - http://api.jquery.com/deferred.promise/.
Upvotes: 0