Adaptive Cards - microsoft-campus-community/workshop-shopping-list-bot GitHub Wiki

Adaptive cards allow presenting little bites of information and actions in a user interface agnostic manner. Platforms that implement adaptive cards have full control over how a card is displayed. As the creator of adaptive cards, you can control the layout of information and the information itself. The layout of an adaptive card is defined by the payload of the card in JSON format. You can use variables in your adaptive card to replace during runtime of your app with data. This allows you to reuse a layout with different data. Adaptive cards are not limited to bots. However, adaptive cards and bots can go hand in hand. They allow your bot to send richer messages than only plain text. Also, for some cases, it might be easier for the chat participant to interact with a small user interface piece than writing an entire message. When getting the items for a shopping list, the bot could send them as an adaptive card. The adaptive card could contain an action to allow to check or uncheck which items should be marked as completed through checkboxes.

So, let's enrich the bot with adaptive cards.

Creating an Adaptive Card

A good place to learn more about adaptive cards is adaptivecards.io.

  1. Go to the adaptive cards designer where you will design the adaptive card for the bot right in your browser.
  2. In the top right corner, select 1.1 as the target version from the dropdown. Screenshot of Adaptive Cards designer website with a red circle around target version and red arrow point to '1.1' for the target version to select from the target version dropdown.
  3. Bevor you start creating the adaptive cards, make yourself familiar with the designer's layout.
    • The top row, which also contains the target version, allows you to switch between different platforms that implement adaptive cards. Note that it will look natural to the specific platform without you having to change the adaptive card. It is platform agnostic. When you click on the 'Preview mode' button, it will show you the actual adaptive card with the payload data inserted. Screenshot of Adaptive Cards designer website with a red square around row that contains dropdown to select a different platform for render preview.
    • On the left side is a handy list of all the elements you can use with the selected target version. You can drag them into the canvas. Screenshot of Adaptive Cards designer website red rectangle around 'Card Elements' side panel.
    • The canvas is in the center. It allows you to view your adaptive card, add new elements, delete, and move around elements. Screenshot of Adaptive Cards designer website with a red rectangle around the canvas in the center of the screen where the adaptive card can be seen and edited. * To the right side of the canvas, you have the 'Element Properties' panel. If you select an element in the canvas, you can edit the element's properties, for example, the content of a text element. Screenshot of Adaptive Cards designer website with a red rectangle around empty 'Element Properties' panel. * Left below the canvas is the 'Card Payload Editor'. This is the JSON you will copy and use in your application when sending an adaptive card. In the designer, changes in the payload will update the canvas and vice versa. So, there are effectively two ways to edit an adaptive card. Screenshot of Adaptive Cards designer website with a red rectangle around 'Card Payload Editor'. * To the right of the 'Card Payload Editor' is the 'Sample Data Editor'. Here you can define a JSON structure if you use variables in your adaptive card, so you can also see the card rendered with the sample data when changing to the 'Preview mode'. Screenshot of Adaptive Cards designer website with a red rectangle around 'Sample Data Editor'.
  4. The visual designer is awesome to get started with adaptive cards. For this workshop, you will make more use of the 'Card Payload Editor' and 'Sample Data Editor' because it is easier to instruct this way.
  5. Delete all the JSON in the 'Card Payload Editor' and 'Sample Data Editor' to empty.
  6. Type the following JSON into the 'Card Payload Editor'. This is the general structure of an empty adaptive card: some metadata, a body containing the elements and actions.
{
    "type": "AdaptiveCard",
    "body": [

    ],
    "actions": [

    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.1"
}
  1. The first element in the body array will be a headline where the bot could insert different values for the title.
{
            "type": "TextBlock",
            "size": "Medium",
            "weight": "Bolder",
            "text": "${title}"
}
  1. In the previous step, you inserted a text element. Note that the content of the text property is ${title}. The $ sign and squiggly brackets denote a variable. To fill the variable with data, you can paste the following JSON in the 'Sample Data Editor'. Note that there is no magic happening. We defined a variable title in the payload editor, so we must also provide some data for this variable.
{
    "title": "Shopping List"
}
  1. The next element you should insert in the body array after the TextBlock element involves a bit more magic. It defines a list of checkboxes, like a ToDo list. However, only one element in the payload is needed to define a list of variable length. No for-loops here. How about you type the element into the body array first and learn how it works in the next step?
,
{
  "$data": "${items}",
  "id": "${id}",
  "type": "Input.Toggle",
  "title": "${if(unit.value, unit.value, '')} ${if(unit.value && unit.unitName, unit.unitName, '')} ${itemName}",
  "value": "${marked}"
}
  1. Now, how does the list work?
  • All elements in the list have an id which uniquely identifies each list entry and is a variable you also named id. This will become important in a bit.
  • Each element in the list is of type Input.Toggle. In other words, this is a list of checkboxes.
  • Each checkbox has two parts.
    • First, the title is simply the text next to the actual checkmark. At first, the value for the title might look scary. However, it shows that building adaptive cards is quite powerful. Forget about the list for a second. With the shopping list bot, it is optional to have a unit for an item. If this information is available for the item, we want to display it in the list, which is what the first to if statements are for. The first if statement says that if unit.value is defined for this item, then insert it; otherwise, we place an empty string. Do you know what the second if statement does? If unit.value and unit.unitName are defined for the item, then the unitName is inserted and otherwise an empty string. Hence the syntax for if statements is ${if(condition , TRUE_PART, ELSE_PART)}. The itemName is always defined for an item, so we append it at the end of the title. Know the title's possibilities look like noodles, 2 apples and 1 kg flour.
    • The second part of the checkbox is a simple "true" or "false" value as a string to define if the checkbox is selected or not.
  • Now you know how each element in the list is constructed. But how is the list constructed? The $data property defines the array to construct the list. So, the $data property, in this case, turns a single element into a list of elements if the value we provide for data is an array. Here no data is provided inline. Instead, the data is expected to come from a variable with the name items.
  1. Let's define example data for the list of items so you can better understand how the list comes to life. In the 'Sample Data Editor' append the following JSON.
,
    "items": [
        {
            "id": "noodles-id",
            "itemName": "noodles",
            "marked": "true"
        },
        {
            "id": "apples-id",
            "itemName": "apples",
            "marked": "false",
            "unit": {
                "value": 2
            }
        },
        {
            "id": "flour-id",
            "itemName": "flour",
            "marked": "false",
            "unit": {
                "value": 1,
                "unitName": "kg"
            }
        }
    ]
  1. Again, you are simply providing data for the variables you defined in the payload. An array called items to create the list of elements in the card. Each entry in the items array must have the important id, an itemName, and whether it is marked. An item can have an optional unit used in the template to construct a more detailed checkbox element. To see the rendered list, you should make sure that you have the 'Preview mode' selected.
  2. Now, you have a complete body for an adaptive card to display a shopping list. Sadly, toggling the checkboxes does not really change anything. This is where actions come into play. In the 'Card Payload Editor', type in the following JSON into the empty actions array.
{
            "type": "Action.Submit",
            "title": "Save"
}
  1. This action adds a button with the title Save. There are different types of actions. The Action.Submit type extracts all the inputs, for example, the checkboxes in the adaptive card, and sends it as an event to whoever authored the adaptive card. In the Bot Framework case, this event will be converted into an activity and sent to the bot. You will learn later how the bot can handle this activity. For now, it is important to know that the extracted data for the Input.Toggle element is "$id": "true" if the toggle is checked or "$id": "false" if the toggle is unchecked. Therefore the id for each item is important. It allows the bot to know which item in the adaptive card changed its marked value. You can try this out in the designer by clicking the Save button in the adaptive cards. Then your browser will display a message with what is sent through the Submit event.

Now you have an adaptive card to display and modify a shopping list. To use the adaptive card, you will need to keep the payload JSON, which should look like the following.

{
    "type": "AdaptiveCard",
    "body": [
        {
            "type": "TextBlock",
            "size": "Medium",
            "weight": "Bolder",
            "text": "${title}"
        },
        {
            "$data": "${items}",
            "id": "${id}",
            "type": "Input.Toggle",
            "title": "${if(unit.value, unit.value, '')} ${if(unit.value && unit.unitName, unit.unitName, '')}  ${itemName}",
            "value": "${marked}"
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "title": "Save"
        }
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.1"
}

NEXT: Using Adaptive Cards with the Shopping List Bot.