How to Create Dynamic Choices from API response

I solved my problem
here is my code

async function action(bp: typeof sdk, event: sdk.IO.IncomingEvent, args: any, { user, temp, session } = event.state) {
 
  const getServices = async () => {
    const services = session['services']
    const singleChoice = {
      text: 'Select Any One service',
      typing: true,
      choices: []
    }
    for (let i = 0; i < services.length; i++) {
      const serv = services[i]
      const obj = {
        title: serv.service_name,
        value: serv.service_name
      }
      singleChoice.choices.push(obj)
    }
    const eventDestination = {
      target: event.target,
      botId: event.botId,
      channel: event.channel
    }
    try {
      const payloads = await bp.cms.renderElement('#builtin_single-choice', singleChoice, eventDestination)
      await bp.events.replyToEvent(eventDestination, payloads)
    } catch (e) {
      console.error(`error : ${e}`)
    }
  }

  return getServices()
}

Have you find a way to do this?
I am too stuck at this.

Try the above code. it is the solution to create dynamic choices

Thanks i did tried your code and after receiving error i did write here. but now i did able to solve that its just removing # in bp.cms.renderElement(’#builtin_single-choice’, parameter. thanks for your code.

Hi monika, I’m new to botpress.

Nice your code!
I have a question: where do you call your http API endpoint?
I ask this because in my case I need to consult an external http api and return the data in buttons format for the user to choose an option.

congratulations for the work

Hello @danilorslima ,

You can find the documentation for the action here. I will updated my thread with picture to describe how to create action.

Botpress Action

In the botpress list > select a bot > Click on Code Editor

Click on the + in the action > Name the action > Copy the cope in the editor

Thanks for the feedback Daehli.

I did as you reported. See if that’s right, please.

In my case I need to return a list of options that came from the api and for each item returned it must be presented as a button.

I’m having two problems.
The first: returns “object” instead of the title name
The second is that the list of “objects” is returned in a single button. Example:

Code:

if i put
{{session.response.body.0.title}}
It returns the first record correctly, but that’s not what I want.
I want it to return all API titles being one on each button, as options.

Thanks for your help

JSON

Instead of JSON.parse could you try JSON.stringify(). The data returned by Axios is a Object. The Object need to be serialize for botpress to display it correctly.

Or you can pass the object without transformation to the renderElement function.

Button

I used the code sample at the top. I modified it to fit your need.

  // Get the data
  const { data } await axios.get('http://localhost:3333/projects')


  const singleChoice = {
    text: 'Select Any One service',
    typing: true,
    choices: []
  }
  
  // Create the array of choice
  data.map(element => {
    // build choice Object
    const obj = {
      title: obj.title, // Change the value here
      value: obj.value // Change the value here
    }
    singleChoice.choices.push(obj)
  })


  const eventDestination = {
    target: event.target,
    botId: event.botId,
    channel: event.channel
  }
  try {
    const payloads = await bp.cms.renderElement('#builtin_single-choice', singleChoice, eventDestination)
    await bp.events.replyToEvent(eventDestination, payloads)
  } catch (e) {
    console.error(`error : ${e}`)
  }

Hello @Daehli
Thank you for your help.
I chose to pass the object without formatting to the renderElement() function.

I run the code you proposed and it’s giving a basic error that so far I haven’t been able to solve it. It is the following error:

Here’s the code I’m running:

function action(bp: typeof sdk, event: sdk.IO.IncomingEvent, args: any, { user, temp, session } = event.state) {
  /** Your code starts below */

  const axios = require('axios')

  /**
   * @title Chamada API Projetcts: http://localhost:3333/projects
   * @category Testes
   */

  const callApi = async () => {
    // We call the API Get the data
    const { data } = await axios.get('http://localhost:3333/projects')

    const singleChoice = {
      text: 'Selecione uma opção:',
      typing: true,
      choices: []
    }

    // Create the array of choice
    data.map(element => {
      // build choice Object
      const obj = {
        title: element.title // Change the value here
      }
      singleChoice.choices.push(obj)
    })
    const eventDestination = {
      target: event.target,
      botId: event.botId,
      channel: event.channel
    }
    try {
      const payloads = await bp.cms.renderElement('#builtin_single- choice', singleChoice, eventDestination)
      await bp.events.replyToEvent(eventDestination, payloads)
    } catch (e) {
      console.error(`error : ${e}`)
    }
    return callApi()
  }
}

/** Your code ends here */

I’ve debugged all the code several times and haven’t found any unexpected tokens as shown in the error. Have you ever gone through this problem? Do you have any suggestions?
Thanks again.

ChoiceOption

In the loop to build the Choice. The obj need to have a value keys

    data.map(element => {
      // build choice Object
      const obj = {
        title: element.title // Change the value here
        value: 'random value for the moment'
      }
      singleChoice.choices.push(obj)
    })

The Interface for ChoiceOption can be find here

builtin_single-choice

I see an extra space in the #builtin_single- choice parameters. Probably it’s the formatting in discourse.

Could you try with this.

    try {
      const payloads = await bp.cms.renderElement('#builtin_single-choice', singleChoice, eventDestination)
      await bp.events.replyToEvent(eventDestination, payloads)
    } catch (e) {
      console.error(`error : ${e}`)
    }

Error

Normally the error is related to a JSON value or a closing bracket in the code :thinking:. Can you copy&paste your Action/CallApi.js from the botpress code editor?

Hi @Daehli

The space in #builtin_single-choice is only in the post was probably past pasting time. In the original code is without the space.

Include the value in obj.

Unexpected token error is occurring on line 44.

follow the code:

function action(bp: typeof sdk, event: sdk.IO.IncomingEvent, args: any, { user, temp, session } = event.state) {
  /** Your code starts below */

  const axios = require('axios')

  /**
   * @title Chamada API Projetcts: http://localhost:3333/projects
   * @category Testes
   *
   *
   */

  const callApi = async () => {
    // We call the API Get the data

    const { data } = await axios.get('http://localhost:3333/projects')
    const singleChoice = {
      text: 'Selecione uma opção:',
      typing: true,
      choices: []
    }

    // Create the array of choice
    data.map(element => {
      // build choice Object
      const obj = {
        title: element.title, // Change the value here
        value: element.title
      }
      singleChoice.choices.push(obj)
    })

    const eventDestination = {
      target: event.target,
      botId: event.botId,
      channel: event.channel
    }
    try {
      const payloads = await bp.cms.renderElement('#builtin_single-choice', singleChoice, eventDestination)
      await bp.events.replyToEvent(eventDestination, payloads)
    } catch (e) {
      console.error(`error : ${e}`)
    }
  }
  return callApi()
}
/** Your code ends here */

Hello @danilorslima ,

I tested the code locally on everything is working fine.

I made some change on the axios endpoint because I didn’t know what was call. Can you copy this in your callAPI.js file ?

function action(bp: typeof sdk, event: sdk.IO.IncomingEvent, args: any, { user, temp, session } = event.state) {
  /** Your code starts below */

  const callApi = async () => {
    // We call the API Get the data

    const data = [
      { title: 'title1', value: 'value1' },
      { title: 'title2', value: 'value2' }
    ]
    const singleChoice = {
      text: 'Selecione uma opção:',
      typing: true,
      choices: []
    }

    // Create the array of choice
    data.map(element => {
      // build choice Object
      const obj = {
        title: element.title,
        value: element.value
      }
      singleChoice.choices.push(obj)
    })

    console.log(singleChoice)
    const eventDestination = {
      target: event.target,
      botId: event.botId,
      channel: event.channel
    }
    try {
      const payloads = await bp.cms.renderElement('#builtin_single-choice', singleChoice, eventDestination)
      await bp.events.replyToEvent(eventDestination, payloads)
    } catch (e) {
      console.error(`error : ${e}`)
    }
  }
  return callApi()

  /** Your code ends here */
}

Hi @Daehli how are you?

Grateful for the feedback.

I rewrote all the code and it worked! The unexpected token error has been resolved for some reason I haven’t figured out yet.

Thank you for your support!
Image:

Below is the rewritten code:

function action(bp: typeof sdk, event: sdk.IO.IncomingEvent, args: any, { user, temp, session } = event.state) {
  /** Your code starts below */
  const axios = require('axios')
/**
   * @title Chamada API Projetcts: http://localhost:3333/projects
   * @category Testes
   */
  const callApi = async () => {
    bp.logger.info('----------------------------> Dentro de callApi')
// We call the API Get the data
    const { data } = await axios.get('http://localhost:3333/projects')
    bp.logger.info('----------------------------> Imprimindo o log 000')
  const singleChoice = {
      text: 'Selecione uma opção:',
      typing: true,
      choices: []
    }
    bp.logger.info('----------------------------> Imprimindo o log 001')
// Create the array of choice
    data.map(function(element) {
      // build choice Object
      bp.logger.info(element)
      bp.logger.info('----------------------------> ' + element.title)
      const obj = {
        title: element.title, // Change the value here
        value: element.title
      }
      singleChoice.choices.push(obj)
    })
    bp.logger.info('----------------------------> Imprimindo o log 002')
  const eventDestination = {
      target: event.target,
      botId: event.botId,
      channel: event.channel
    }
const payloads = await bp.cms.renderElement('builtin_single-choice', singleChoice, eventDestination)
    await bp.events.replyToEvent(eventDestination, payloads)
  }
  return callApi()
  /** Your code ends here */
}
1 Like