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

This article corresponds to the V2 (Classic) of the app. You can check the equivalent for the new version V3 (Bricks) here

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:

 3. Callback Save block, all the data from the request 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

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

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 restrart the process

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

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

9. To restart the loop we connect the Ask a Question block, with the a Set a Variable that we will use to reset the variable that stores the Callback save.

Now in the Design/Advanced section, in the 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 () {
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?