I have two JSONs. One is from a variable and one from API call based on both of them I'm trying to generate HTML code. I'm using a recursive function as the JSONs can contain nested sections. Those JSON may also contain several 'selects' which should have 'options' based on another API call ("source"). When I'm trying to execute the code everything works, but instead of 'options' the function returns [object Promise] while in the console there are proper values.
Here is an example of the variable (API response is similar):
var constructor = {
"element" : {
"data" : [
{
"type" : "section",
"id" : "id1",
"elements" : [
{
"type" : "input",
"subtype" : "text",
"label" : "Address",
"id" : "address",
},
{
"type" : "section",
"id" : "id2",
"elements" : [
{
"type" : "h3",
"text" : "Some text"
},
{
"type" : "select",
"label" : "Klient",
"id" : "id3",
"width" : 100,
"elements" : "source",
"source" : "source_path"
}
]
},
{
"type" : "section",
"id" : "id4",
"elements" : [
{
"type" : "h3",
"text" : "Some text 2"
}
]
}
]
}
]
}
}
And here is the code:
async function getData(url = '') {
const response = await fetch(url, {
method: 'GET',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
}
});
return response.json();
}
const generateElements = (obj) => {
if(obj.type == 'input') doSomething();
if(obj.type == 'h3') doSomething();
if(obj.type == 'select'){
return `<select id="${obj.id}">
${
typeof obj.elements == 'object' ?
Array.isArray(obj.elements) &&
obj.elements.map((element) => {
return `<option id="${element}">${element}</option>`
}).join("")
: obj.elements == 'source' &&
getData(obj.source)
.then(data => {
data.map((element) => {
console.log(element.id, element.name)
return `<option id="${element.id}">
${element.name}
</option>`
}).join('')
})
}
</select>
<label for="${obj.id}">${obj.label}</label></div>`
}
if(obj.type == 'section'){
return `<section id="${obj.id}">
${ obj.hasOwnProperty('elements') &&
obj.elements.map((element) => {
return generateElements(element)
}).join('')
}
</section>`
}
}
getData('the_url2')
.then(data => {
data.map((element) => {
element.elementToAppend &&
jQuery(element.elementToAppend).append(generateElements(JSON.parse(element.data)))
})
});
constructor.map((element) => {
jQuery(element.elementToAppend).append(generateElements(JSON.parse(element.data)))
})
I've tried to change the generateElements function to async but then the code generates [object Promise] in place of sections. My another idea was to await for API calls but then 'await is only valid in async function' error appeared. When I assign the API call from select to const and then return the value it generates proper html code in the console, but still [object Promise] in the result of the function. I've also tried many other solutions but without success.
I have no idea how to proceed with that but I'm not able to change API responses. I'm limited by Qualtrics with using frameworks and libraries.
EDIT: Thanks to @Bergi support I was able to resolve my problems. So:
I used async for generateElements:
const generateElements = async (obj) => { ... }
await for the fetch function:
await getData(obj.source)
.then(data => {
data.map((element) => {
console.log(element.id, element.name)
return `<option id="${element.id}">
${element.name}
</option>`
}).join('')
})
and Promise for recursivness:
( await Promise.all(obj.elements.map(async (element) => {
return await generateElements(element)
})
) ).join('')
Thanks @Bergi once again! :)