Guide: Creating dynamic choices and carousels
# 📖tutorials
e
Hello everyone! Welcome to our quick and practical tutorial on setting up a dynamic options list in a Capture card. We'll focus on showcasing the products of a fictional store. 1. Initialization: Begin by creating a variable named
productOptions
. This will store the dynamic list of product choices. 2. Fetching Products: Next, add an Execute Code card to your workflow with the JS code:
Copy code
js
try {
  const listOfProducts = await ProductsTable.findRecords({
    filter: AI`${userQuery}`
  });
  // Make sure to make the columns with relevant data Searchable

  // If the data is in an object format, convert it using: const listOfProducts = Object.keys(yourObjectVariable)

  // The listOfProducts variable needs to be an array of strings or objects

  // Map the list to the format accepted by the Capture card
  workflow.productOptions = listOfProducts.map((product) => {
    return { label: product.Name, value: product.id };
  });
} catch (error) {
  console.log('Something went wrong: ', error);
}
This code retrieves a product list from a Table and formats the options for display. Customize it as per your needs. 3. Selecting a Product: Create a variable named
selectedProductId
. Its data type should correspond to the value format of your product options (string or number). 4. Setting up the Choice Card: Create a Single Choice card: - Store the result in
selectedProductId
. - Navigate to Choices > Items and select
@productOptions
by clicking the {x} button . 5. Using the Selection: Finally, use the
selectedProductId
variable according to your workflow's needs. This could be in another Execute Code card to fetch detailed information about the selected product, or within expressions to determine if a specific product has been chosen or if some property matches. Adjust and expand upon this code to fit your unique application and workflow requirements. Happy building!
We are assuming that the products returned from the server have the format below, but it doesn't need to be like this!
Copy code
js
{
   name: "Talking robot toy",
   id: "12345",
   otherInfo: "any"
}
Add to the label property, the name you want to be displayed, and to the value property, the information that's unique for each product. If you have a list of strings (text), you should use
{ label: product, value: product }
instead
I might record a video tutorial on this, let me know if it you are interested!
c
Hi Guilhermy ! Id love to test this out but which services provide this type of API ? Is it also possible to add different categories like "price" "delivery time" etc ?
g
@early-train-33247 legend
e
Hey Remo, this is a code to be run inside an Execute card. Services and apps with api endpoints will allow you making requests like this to get list of items and more. But where do you want to add this other info like price?
c
@early-train-33247 currently we are working on a very complex bot and were looking for alternatives to downsize it. In our case we have 4 user inputs we ask from our user. Based on the inputs we receive we change our formula variables (about 6) . We then execute our code. Can we somehow turn this into a dynamic list and let some third party service (Airtable?) just fetch the variables we want ?
h
I'm guessing the product database would be something our client provides? And where would we upload it to? Our KB?
e
I'm gonna answer you guys tomorrow! Sorry for the delay
Yes the database is what will contain the custom options. You would then make requests to it using Axios (not actually to it directly, you would probably have the database connected to a Node sever to receive requests)
I didn't actually get your requirements. Did you manage to achieve what you wanted? If not, please make a post on #1111009377525186570 with more details and tag me so I can follow up!
b
HAs someone try this with Assistant API?
C an I test this?
e
Hey @best-army-74344 feel free to test i!
c
@early-train-33247 I have been trying out your code but I could use some help finding the missing piece. Shouldn't the filter be populated by the query from the user captured in a variable (the product the user wants to search for?).
Copy code
js
try {
  const listOfProducts = await Data2Table.findRecords({
    filter: AI`${workflow.selectedProductId}`
  });
  // Make sure to make the columns with relevant data Searchable

  // If the data is in an object format, convert it using: const listOfProducts = Object.keys(yourObjectVariable)

  // The listOfProducts variable needs to be an array of strings or objects

  // Map the list to the format accepted by the Capture card
  workflow.productOptions = listOfProducts.map((product) => {
    return { label: product.Name, value: product.Manufacturer, price: product.Price };
  });
} catch (error) {
  console.log('Something went wrong: ', error);
}
e
You need to update the filter to be
Copy code
js
AI`Id is ${workflow.selectedProductId}`
Otherwise the ai won't know what's the column you are referring to
Alternatively you can remove the AI filter and do this instead
Copy code
js
filter: {
 id: workflow.selectedProductId
}
Since it's an exact query
The selectedProductId is the very id right?, and not a user query that could match several products
c
nice I got it to work! the confusion was in the set-up : the initial node should be the node that asks "what product are you looking for" . This will be stored in a variable called (workflow.query) (or any other name, I named mine workflow.lookup). Here is my code + some screens in case somebody was stuck :
Copy code
js
try {
  const listOfProducts = await Data2Table.findRecords({
    filter: AI`Find records with where the Name matches: ${workflow.lookup}`
  });
  workflow.productOptions = listOfProducts.map((product) => {
    return { 
      label: product.Name, 
      value: product.Manufacturer, 
      carbrand: product.CarBrand, 
      price: product.Price,
      stock: product.Stock
    };
  });
} catch (error) {
  console.log('Something went wrong: ', error);
}
Some questions I personally still have: - @early-train-33247 could you point me to display the result in a carousel instead ? this allows for more information per record to be displayed. - the search is not always accurate, even though the query is 100% matching the
id
(or in my case
Name
) . This is quite important to figure out so any tips are appreciated.
e
To display them in a carousel:
Copy code
js
// Create the carousel items
const carouselItems = products.map((product, index) => {
  const cardText = `${product.Name}\nThe price is ${product.Price}`
 // the /n breaks a line

  return {
    type: 'card',
    title: {
      dynamicValue: cardText ,
      valueType: 'dynamic'
    },
    imageUrl: {
      dynamicValue: `${product.ImageSrc}`,
      valueType: 'dynamic'
    },
    actions: [
      {
        action: 'postback',
        label: {
          dynamicValue: 'Open',
          valueType: 'dynamic'
        },
        value: {
          dynamicValue: `open_product_${product.id}`,
          valueType: 'dynamic'
        }
      }
    ]
  }
})

// Send the carousel
bp.dm.replyToEvent(event, {
  type: 'carousel',
  items: carouselItems
})
Also make your filter simpler:
Copy code
filter: AI`Name or description is like ${workflow.lookup}`
Remember to make the columns searchable
this will allow the user to click one of the options
add a Wait User Input card right below this code to get the clicked option. the event.preview value will be like
open_product_44
, to get the numeric product id use:
const id = Number(event.preview.split("_")[2])
the split part breaks the string into three parts and gets the third one (index 2 starting from 0)
c
learning allot here thank you man @early-train-33247 💎
e
Anytime! 🦾
Keep in mind this might not work in all platforms. You won't be able to test the action buttons in the Emulator, but you can type the button value instead like:
open_product_23
, it has the same effect as clicking
b
do u have a demo?
e
Hi @early-train-33247 I tried implementing this but i wasn't able to get the value on click action from event.preview is this still working?
e
Hey mate, can you send the code you are using? and a screenshot of the node where the code is (make sure to show the first following nodes as well)
b
thanks for this snippet but i am getting this error "Cannot find name 'bp'.(2304) " do i need to import bp from somewhere?
e
it's just a typescript error, the code will run just fine 😉
b
True it worked... Thanks alot Boss
Here's my node and the codes
Copy code
try {
  const listOfProducts = await ProductsTable.findRecords({
    filter: {
      id: 1
    }
  })
  // Make sure to make the columns with relevant data Searchable

  // If the data is in an object format, convert it using: const listOfProducts = Object.keys(yourObjectVariable)

  // The listOfProducts variable needs to be an array of strings or objects

  // Map the list to the format accepted by the Capture card
  workflow.productOptions = listOfProducts.map((product) => {
    return { label: product.Name, value: product.id }
  })
} catch (error) {
  console.log('Something went wrong: ', error)
}
Copy code
// Create the carousel items
const carouselItems = workflow.productOptions.map((product, index) => {
  const cardText = `${product.label}`

  return {
    type: 'card',
    title: {
      dynamicValue: cardText,
      valueType: 'dynamic'
    },
    imageUrl: {
      dynamicValue: '',
      valueType: 'dynamic'
    },
    actions: [
      {
        action: 'postback',
        label: {
          dynamicValue: 'Open',
          valueType: 'dynamic'
        },
        value: {
          dynamicValue: `open_product_${product.value}`,
          valueType: 'dynamic'
        }
      }
    ]
  }
})

// Send the carousel
bp.dm.replyToEvent(event, {
  type: 'carousel',
  items: carouselItems
})
e
below the wait for user input card
add a code card with the code:
event.preview = event.payload.text || event.payload.payload || event.payload.value || event.preview
This will assign the user selection to the event.preview variable
b
I was having the same error "Unsupported message type: postback" and tried this. although i got the selected product id "open_product_1" but how can i clear the Unsupported message type: postback error message please ?
e
Thank you very much this works very well! When i test it in the emulator i get a response "Unsupported message type: postback error message" but in the published linked it's not there, i think this is good for now but it will be great if we have a way to remove this response😁
e
Hey guys @brave-bird-91413 @early-optician-14851 this is just an automated response from the emulator, it won't show in production and it won't affect the extracted value 😉
Keep something in mind, on whatsapp the clicked value will include the preffix
p:
, in order to remove it and get the actual value, do something like this:
Copy code
js
let userSelection = event.payload.text || event.payload.payload || event.payload.value || event.preview || ''

if(userSelection.startsWith('p:')){
  userSelection = userSelection.split('p:')[1]
}

event.preview = userSelection
b
heyy guys, has someone been able to eliminated scroll bar?
when the dynamic carousel shows up
r
Hi there! I'm currently using Botpress v12 and planning to include a dynamic carousel. Could you help me? I can't seem to find documentation about it. I'm receiving the data in JSON format and need to convert it into a carousel card.
c
Hey there Sai, I would encourage learning from the examples in the thread after which you can open a help thread for more personalized help. 💪
e
For v12 questions check out #1112845893931110402
l
@fresh-fireman-491 here is my code `const carouselItems = workflow.products.map((product) => { return { title: product.prd_brand, subtitle: product.prd_name, image: { url: product.image_link }, actions: [ { type: 'postback', label:
Select ${product.prd_name}
, payload: product.prd_sku } ] } })` here is the log from carouselItems info card:Display products as a carousel card for user selection.
Copy code
action
{ title: 'Nandini',
  subtitle: 'Nandini Blue',
  image: { url: 'https://versal.one/cdn/tic/milk/Nandini.png' },
  actions: 
   [ { type: 'postback',
       label: 'Select Nandini Blue',
       payload: 'nandini_blue' } ] }, { title: 'Nandini',
  subtitle: 'Nandini Gold',
  image: { url: 'https://versal.one/cdn/tic/milk/Nandini-Gold.png' },
  actions: 
   [ { type: 'postback',
       label: 'Select Nandini Gold',
       payload: 'nandini_gold' } ] }, { title: 'Heritage',
  subtitle: 'Nandini Daily',
  image: { url: 'https://versal.one/cdn/tic/milk/heritage-daily.jpg' },
  actions: 
   [ { type: 'postback',
       label: 'Select Nandini Daily',
       payload: 'heritage-green' } ] }
but images are not shown
I found my answer in this
@early-optician-14851 - Thank you for sharing the code
c
Copy code
js
   image: {
      url: product.image_link
    },
Can you try :
Copy code
js
   image: {
      url: `${product.image_link}` 
    },
f
Thank you! @bland-sandwich-37784
l
Thanks all, is there a documentation for this I can read?
e
This is a beta feature so unfortunately not
w
Hi. I need some help please. It works good with the emulator. But in messenger, after the button is clicked I get the right value shown but the bot still waits for user input until I write something myself. @early-train-33247
c
unfortunately callbacks on carousels are blocked for now and only work in the emulator, not in the webchat. We are all eagerly awaiting a solution 🛠️
w
Okey. So it can't work for messenger too right now ? Thank you
c
I haven't tried it with messenger but it could work (just not officially supported yet).
e
it works for WhatApp only afaik
could you please create a #1111009377525186570 post @worried-artist-99581 we shall be able to help you further there
w
OK thank you
l
It was working for me until yesterday in webchat and today it stopped working. Now I am getting an error An invalid payload is sent from the webchat frontend: { type: 'postback', payload: 'blue' }
c
Keep posted, there might be a fix underway
h
Hello guys, great thread here I am going to start working on a property recommendation bot today and happy to find this chat. I am going to build a bot where users will enter the property type and amenities they are interested in and we should be able to show them a carousel of the available properties matching their criteria. Just leaving this here for notes and will come leave my updates here as I progress. Thank you everyone for your contribution and giving us a starting point.
b
Please how did you handle each of the buton click action?
s
How can I show my products fetched from the products table in form of a card or any other component. I cant find information anywhere using the execute code. I would like to show products to seller depending on their id. like I filter their saved products from table seller_product
b
the translator agent has a bug, does someone is having issues with it ???
@acceptable-smartphone-22753 do you still having the same issue??'
a
It seems to be working now
s
Hello, is it possible to disable text input while carousel is active? I want user to use carousel not text
c
Hi there, unfortunately this is not possible (yet) but ive been told its on the planning 🛠️
w
#1159090477329551410 You can have a look at this thread 🙂
b
Is it possible to query multiple records? I have a tournament table and would like to query the next three (in relation to the current date) tournaments. These should be displayed afterwards in the carousel. Is there a solution? I have already tried a lot but unfortunately have had no success.
h
Thank you x3! I just tried your codes for dynamic choices and dynamic carousel and it worked beatifully, I was wondering how it would behave on the whatsapp integration and it worked perfectly. I wonder why the BP team hasn't made a youtube video about these dynamic features, I knew VoiceFlow had made a similar video, but I wasn't aware if Botpress could support dynamic cards.
e
In the Emulator I get the "Unsupported message type: postback", but also the response after. So Kinda Work. But in the Webchat, once teh user click on the action button, the bot display the ... but do nothing, looks like it freeze. Any idea/update on when the Postback will be supported in Webchat and Messenger? Really important feature.
i
Hi! Any idea why my carousel don't display images and buttons? I've tried both option to return object, but both didn't work.
Copy code
if (workflow.productOptions && workflow.productOptions.length > 0) {

  function createCardForProduct(product, index) {
    // return {
    //   type: 'card',
    //   title: {
    //     dynamicValue: `${product.productName}`,
    //     valueType: 'dynamic'
    //   },
    //   image: {
    //     dynamicValue: `${product.productImageUrl}`,
    //     valueType: 'dynamic'
    //   },
    //   buttons: [
    //     {
    //       action: 'url',
    //       label: {
    //         dynamicValue: 'Open',
    //         valueType: 'dynamic'
    //       },
    //       value: {
    //         dynamicValue: `${product.productUrl}`,
    //         valueType: 'dynamic'
    //       }
    //     }
    //   ]
      
      // Add other card properties like subtitle, actions
      // ...
    // }

    return {
      title: `${product.productName}`,
      image: `${product.productImageUrl}`,
      buttons: [
        {
          type: 'web_url',
          title: 'Open',
          url: `${product.productUrl}`
        }
      ]
    };
  }

  const carouselItems = workflow.productOptions.map((product, index) => createCardForProduct(product, index))

  workflow.productOptions = [];

  bp.dm.replyToEvent(event, {
    type: 'carousel',
    items: carouselItems
  })

}
c
Hey Ania, it seems your code is generated by AI which often cuts of code to optimize it's output token size. Also // is used to comment out code , making it non-functional. A few things you could try: - prompt the AI to give you as complete code possible, based on the variable names you provide. - make sure all the variables match within your workflow. - look trough the dynamic carousel threads which are a few #1270343173784342529 . Key here is to populate your carousel with the right variables you have created in your code.
c
How could I do the same but generating text instead of carousel? So I want to generate a text response programmatically before and after the carousel.
a
You can do that by using: bp.dm.replyToEvent(event, { type: 'text', text: 'Here is a dynamic message with product: ' + allP[0].title });
@average-lawyer-81822 try this
c
I am able to display the caroursel. I have two queries. 1) I want to display a button with text "Book Now" and add a data attribute to it called
propertyId
. Once this button is clicked, I want to set a user.propertyId = button's bound property id. 2) I want to display a hyperlink next to "Book Now" that should open link in a new browser window. How to do these two?
w
you need two actions
Copy code
typescript
    actions: [
      {
        action: 'postback',
        label: 'Book Now',
        value: `BOOKING_${product.propertyId}`
      },
      {
        action: 'url',
        label: 'View Property',
        value: 'https://your.domain.site'
      }
    ]
EDITED: i wrote
type
instead of
action
c
thanks @white-nest-40686
w
I'm glad I was able to help you.
Here is a complete example.:
Copy code
ts
/* example data to populate carousel */
const products = [
  {
    prd_sku: 'sku 1',
    prd_brand: 'brand 1',
    prd_name: 'product 1',
    image_link: 'https://placehold.co/300x200?text=image+1&font=roboto'
  },
  {
    prd_sku: 'sku 2',
    prd_brand: 'brand 2',
    prd_name: 'product 2',
    image_link: 'https://placehold.co/300x200?text=image+2&font=roboto'
  }
]

/* Create carousel */
const carouselItems = products.map((product) => {
  return {
    title: product.prd_brand,
    subtitle: product.prd_name,
    imageUrl: product.image_link,
    actions: [
      {
        action: 'postback',
        label: 'Buy Now',
        value: `BOOKING_${product.prd_sku}`
      },
      {
        action: 'url',
        label: 'View Details',
        value: 'https://www.botpress.com'
      }
    ]
  }
})

/* Show carousel */
bp.dm.replyToEvent(event, {
  type: 'carousel',
  items: carouselItems
})
r
Please can you make a video on this. I've gone through everything and I still can't understand 😢😢
w
Hi, please copy my example and paste it into ChatGPT or any other language model, and ask it to explain the code. The script does everything you need: it receives an array of data to populate the carousel, creates the carousel, and displays it.
r
Ok I'll do just that and get back to you
71 Views