How to create dynamic buttons from a JSON API response (or array)

Sometimes a response from an API would be an array, and we won't know how many items will be in such array, neither we can create as many buttons as possible options are available.

In this workaround we are going to display the buttons in a carousel, so the number of options can be as many as items are in the array, without manual input. However, as the space is limited to display such carousel we will use a modal, that will be triggered right in the moment that the buttons are needed.

Here is an example of what we are going to build: 2016 Summer Olympics in Rio de Janeiro Medals

We start with the data

That we convert into a JSON

Now that we have our JSON and is stored (for the example we used the myjson service), we will go to the builder. Here is how it looks the builder:

The blocks are the following:

1. Default Welcome Message;

2. Send Webhook Block:

The URL is https://api.myjson.com/bins/j31zo with a GET Method.

 3. In the Save Response section we store all the response in a new variable called @dataset:

4. Question Block, where we define the options by continents, we store the option in a variable called @continent.

Also is where we pass the data from the response stored in the Landbot scope to Javascript scope:

Choose a Continent*{html}<script>dataSet = @dataset;</script>{/html}

5. Question Block, here we won't use any buttons, but will use this block to trigger different functions

<span class="modalSelection"></span><button class="customButton" id="openModalButton" onclick=" openModal()" style="width: 194px;">Choose a Country</button><script>filterDataSet("@continent"); generateSliders (); openModal();</script>

We need to hide the button of the question. To do so add the following code in the button text:

{html}<div id="hideme"></div>{/html}

Doing this we can identify later the exact area we want to hide

6. Send a Message block, where we will display the information from the country selected:

@country won @goldmedals Gold medals, @silvermedals Silver medals & @bronzemedals Bronze medals, and ranked #@rank

7. Ask A Question block (Yes/No). With this block we will reset the value of some variables, in case we want to restart the process

Do you want to search another country?<script>
dataset = "";
divSliders ="";
contentCarrousel ="";
dataSetFilterContinent = "";
dataSetFilterCountry = "";
</script>

8. Goodbye block, in case we want to finish the bot

Now, in Design - Advanced - Custom CSS will add the following code:

* {
z-index:1
}

.modal {
display: none;
position: fixed;
z-index: 2;
left: 0;
top: 0;
width: 100%;
height: 100px;
overflow: auto;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}


.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 0px;
border: 1px solid #888;
width: 95%;
}

#modalTitle{
font-family: Arial, Helvetica, sans-serif;
}


.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}

.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}

.customButton {
background-color: rgb(222, 69, 97);
border-color: rgb(172, 19, 47);
color: rgb(255, 255, 255);
letter-spacing: -.5px;
margin-right: 6px;
margin-bottom: 6px;
cursor: pointer;
transition: background-color .25s,border-color .25s,color .75s;
padding: 20px 25px;
width: calc(50% - 6px);
text-align: center;
justify-content: center;
display: flex;
border-radius: 3px;
border-width: 2px;
border-style: solid;
}

.customButtonCarrousel {
margin-left: 25%;
background-color: rgb(222, 69, 97);
border-color: rgb(172, 19, 47);
color: rgb(255, 255, 255);
letter-spacing: -.5px;
margin-right: 6px;
margin-bottom: 6px;
cursor: pointer;
transition: background-color .25s,border-color .25s,color .75s;
padding: 20px 25px;
width: calc(50% - 6px);
text-align: center;
justify-content: center;
display: flex;
border-radius: 3px;
border-width: 2px;
border-style: solid;
}

And the Custom HTML/JS:




<script src="https://cdn.jsdelivr.net/npm/siema@1.5.1/dist/siema.min.js"></script>



<div id="myModal" class="modal" style="min-width:100%; height:100%; padding:1px" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>

<div class="modal-content" style="margin-top: 30%;">
<span id="modalTitle" style="display: flex; justify-content: center;padding: 10;">Choose a Country (swipe and select)</span>
<span class="close">&times;</span>
<div class="innerModal" style="min-width:100%; padding:1px">

</div>
</div>
</div>

<script>

var modal = document.getElementById('myModal');
var span = document.getElementsByClassName("close")[0];
var innerModal = document.getElementsByClassName("innerModal")[0];

/* With the folling functions we control how the modal is closed, either by pressing X or outside the modal*/

function closeModal (){
modal.style.display = "none";
}

window.span.onclick = function() {
modal.style.display = "none";
}

window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";

}
}

var span = document.getElementsByClassName("close")[0]; // This span is the X used to close the modal
var modal;

var dataset;
var divSliders;
var contentCarrousel;
var displayButtons = ""

/* With generateSliders content for the carrousel is created from the Array dataSetFilterContinent*/

var perPage = 3;

function generateSliders () {
for (var i = 0; i < dataSetFilterContinent.length; i++){
if (dataSetFilterContinent.length == 1){
divSliders = `<div><button class="customButtonCarrousel" onclick="submitInput('${dataSetFilterContinent[i].Country}'); filterDataSetCountry('${dataSetFilterContinent[i].Country}')">${dataSetFilterContinent[i].Country}</button></div>`
perPage = 1;
displayButtons = "none";
} else if (i == dataSetFilterContinent.length - 1){
divSliders += `<div><button class="customButtonCarrousel" onclick="submitInput('${dataSetFilterContinent[i].Country}'); filterDataSetCountry('${dataSetFilterContinent[i].Country}')">${dataSetFilterContinent[i].Country}</button></div>`
perPage = 2;
displayButtons = "";
} else {
divSliders += `<div><button class="customButtonCarrousel" onclick="submitInput('${dataSetFilterContinent[i].Country}'); filterDataSetCountry('${dataSetFilterContinent[i].Country}')">${dataSetFilterContinent[i].Country}</button></div>`+'+'
perPage = 2;
displayButtons = "";
}
}
// contentCorrousel we use it as a builder-template for the carrousel based on the Siema library
contentCarrousel = `<div class="siema">
${divSliders}
</div>
<div style="display: flex; justify-content: center;padding: 10;">
<button class="prev" style="display:${displayButtons}">prev</button>
<button class="next" style="display:${displayButtons}">next</button>
</div>`
}


/* This function is activated as soon as is called the block to choose the country*/

function openModal () {
setTimeout(function(){
var hide = document.getElementById("hideme");
var par = hide.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement
par.style.display = "none"
},100)



window.modal.style.display = "block";
window.innerModal.innerHTML = contentCarrousel

/*Here is where the siema carrousel library is set up*/
const mySiema = new Siema({
perPage: perPage,
momentum: true,
loop: true,
weight: 3.75,
duration: 300
});

document.querySelector('.prev').addEventListener('click', () => mySiema.prev());
document.querySelector('.next').addEventListener('click', () => mySiema.next());
}

/*This function is triggered by the buttons that are disaplyed in the carrousel*/

var counter = 0;
function submitInput(inputValue) {

modal.style.display = "none"; // To hide the modal
document.getElementsByTagName("button")[counter].style.display = "none" // To hide the button that is displaying in the chat
document.getElementsByClassName("modalSelection")[counter].innerHTML = "Choose a country" //To insert a text, as a native selection we insert the text
counter++ //In case more than one queries are done, we need to get correctly the selectors
Landbot.exec('landbot-custom-data', { country: inputValue }) // To specify the variable @country in Landbot, depending on the button selected
Landbot.exec('landbot-msg-send', {
message:inputValue ,
text: '***',
payload:'****',
type: 'button',
}); // So the flow continues after is pressed a button, by sending a message as the user.
}



var dataSet; // To inicialize the variable where we will store the content from the callback save as " @ dataset"
var continentElection;

// This function triggers the first step filter, filter the countries by continent, and stores it in the array "dataSetFilterContinent"
function filterDataSet (continent) {

continentElection = continent
dataSetFilterContinent = dataSet.filter(function (item) {
return item.Continent == continent
})
console.log("dataSetFilterContinent: ",dataSetFilterContinent)
}

//This functions triggers the second filter bases in continent and country, returning a single object, with it's different variables that in this same function, we store in Landbot variables.
function filterDataSetCountry (country) {

dataSetFilterCountry = dataSet.filter(function (item) {
return item.Continent == continentElection && item.Country == country
})
Landbot.exec('landbot-custom-data', {
rank: dataSetFilterCountry[0].Rank,
goldmedals: dataSetFilterCountry[0].Gold,
silvermedals: dataSetFilterCountry[0].Silver,
bronzemedals: dataSetFilterCountry[0].Bronze
})
console.log("dataSetFilterCountry: ",dataSetFilterCountry)
}

</script>
These scripts and how to's are not native functionalities. Landbot won't be able to support, help or guarantee these scripts and how to's. These Workarounds and How to's are for developers, as a learning and example material on how to extend or modify some of the platform limitations.  Due to platform updates, some scripts might stop working in the future.Please, note that in case of Scripts and Workaround the Custom Success Team can deliver limited support.


How did we do?