2

From a Microsoft Teams bot I send an Adaptive Card with input fields and a Submit action. When the user clicks Submit I receive the data entered but the form fields are cleared.

Why is this? What am I doing wrong? This behavior is extremely annoying as I can't verify the input and ask the user to correct it.

This happens in the desktop Teams app, Teams in a browser, Teams webchat in a web page and the Bot Emulator. In the Emulator it suffices for the field to loose focus.

In case it matters I use nodejs.

Kyle Delaney
  • 11,616
  • 6
  • 39
  • 66
mplwork
  • 1,120
  • 10
  • 21
  • Are you willing to accept one of the answers? – Kyle Delaney Jul 09 '20 at 16:34
  • @KyleDelaney to be honest, no. Yours doesn't provide an actual solution but is valuable as hint and I don't like to accept my own nonsense. – mplwork Jul 13 '20 at 13:11
  • Stack Overflow is about answering questions. You asked why Adaptive Card inputs were getting cleared and what you're doing wrong. Can you explain what part of those questions were not answered? – Kyle Delaney Jul 13 '20 at 17:18

2 Answers2

3

You're not doing anything wrong. That's just how Adaptive Cards work in Teams, perhaps as a way to signify that the data has been sent to the bot successfully. There may be something you can do to fix your problem though.

Adaptive Card input fields have a value property that allows you to specify the field's initial value. If you send a card to the user and the input fields' value properties are populated, the fields won't be empty. This means you can send such a card as an update instead of a new activity and it will look like the card has been modified in place, since Teams supports updating activities. If the update uses the same card but with the values the user entered then it will look like the card remains unchanged, which would fix your problem of the values disappearing.

There was a question about dynamically adding input fields to Adaptive Cards, and the answer contains sample code that preserves input field values:

var inputId = `text${i}`;
body.push({
    type: "Input.Text",
    id: inputId,
    value: cardData[inputId] // This is where the value is preserved
});

If you want this whole process to be made easier with prebuilt code that you can install in NuGet packages, feel free to voice your support for these ideas on GitHub:
Bot.Builder.Community.AdaptiveCards
AdaptiveCard Prompt

Kyle Delaney
  • 11,616
  • 6
  • 39
  • 66
  • Thanks Kyle. Good to know I wasn't on the wrong track. While I haven't checked everything in your answer yet I post as an answer what I did in the meantime. – mplwork Sep 11 '19 at 09:52
  • @mplwork - It's great that you've managed to answer your own question, but it's still good etiquette to upvote and accept the answers that other people work to provide for you – Kyle Delaney Sep 11 '19 at 17:39
1

While I was waiting for an answer to my question I came pretty much to the same conclusion as Kyle Delaney outlined above, you have to resend the data entered.

So I started to fiddle with my code and came up with this solution, not sure this is the best way.

As part of a waterfall step:

   async W2_showCard(step) {
        const card = CardFactory.adaptiveCard(this.makeFormCard());
        return await step.prompt('formPrompt', { prompt: MessageFactory.attachment(card) });
    }

The trick is in formPrompt which also ensures the user submits the form instead of doing something else.

       // Workaround to make user click Submit or cancel dialog
        this.dialogs.add(new ActivityPrompt('formPrompt', async prompt => {

            const recognizedValue = prompt.recognized.value;
            if (recognizedValue.type === ActivityTypes.Message) {
                if (recognizedValue.value) {

                    const replyToId = recognizedValue.replyToId;

                    var oldCard = prompt.options.prompt.attachments[0];

                    var validated = true;
                    oldCard.content.body.forEach((item, i, body) => {
                        if (item.type === "Input.Text" || item.type === "Input.ChoiceSet") {

                            // preserve the user input
                            const newValue = recognizedValue.value[item.id];
                            item.value = newValue;

                            // some rudimentary input validation:
                            // assumes there is a corresponding text field just 
                            // prior to the input field (input fields 
                            // can't change their color)

                            if (newValue == '') {
                                body[i - 1].color = 'Attention';
                                body[i - 1].weight = 'Bolder';
                                validated = false;
                            } else {
                                delete body[i - 1].color;
                                delete body[i - 1].weight;
                            }
                        }
                    });

                    if( validated ) {
                        // remove the submit and cancel actions (not required, debatable)
                        delete oldCard.content.actions;
                    }

                    // update the card
                    const activity = prompt.context.activity;
                    activity.attachments = [oldCard];
                    activity.id = replyToId;
                    await prompt.context.updateActivity(activity);

                    if (validated) {
                        // this is to make input available in next waterfall step
                        prompt.recognized.value = recognizedValue.value;
                        return true;
                    } else {
                        await prompt.context.sendActivity(`Please check the form. Some values are missing`);
                    }
                } else {
                    await prompt.context.sendActivity(`Please fill out form and press *"submit"* button or type *"cancel"* to stop.`);
                }

            }
            return false;

        }));

mplwork
  • 1,120
  • 10
  • 21
  • Hey @mplwork,i tried to replicate the above code ,same as in a waterfallstep,but i was getting an Error in Updateactivity step . ERROR: Activity resulted into multiple skype activities. On further analysis the error was in step activity.attachments = [oldCard]; ,activity did not have an attachment option.. is there any solution?? – sree Jul 11 '20 at 04:06
  • @sree unfortunately, there are channels which don't support `updateActivity`. WebChat is one of them and Skype apparently too. You have to catch the exception of `updateActivity` and redisplay the card in the catch block. Unfortunately, this clutters up your chat history with old cards. You should keep track of them, otherwise a user might submit them again. I have code which does all of this but it is quite a bit different from the above, so I couldn't easily integrate it into the answer. – mplwork Jul 13 '20 at 13:02
  • not other channels, i was just doing it with msteams. Update activity gives out that error. But i used delete activity and sendactivity again as replacement for the time being. – sree Jul 14 '20 at 07:51