Hello i have this input forms card. It is rendering properly but how can i get its results? And how can i make it so that the bot wait for the user to submit before proceding to the next step? Putting stepContext.NextAsync will automatically trigger the next step. But removing it will cause an error because it needs to return something.
public InitialQuestions(string dialogId, IEnumerable<WaterfallStep> steps = null)
: base(dialogId, steps)
{
AddStep(async (stepContext, cancellationToken) =>
{
var cardAttachment = CreateAdaptiveCardAttachment(_cards);
var reply = stepContext.Context.Activity.CreateReply();
reply.Attachments = new List<Attachment>() { cardAttachment };
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
// how can i wait for user to click submit before going to next step?
return await stepContext.NextAsync();
// return await stepContext.PromptAsync(
// "textPrompt",
// new PromptOptions
// {
// Prompt = MessageFactory.Text(""),
// },
// cancellationToken: cancellationToken);
});
AddStep(async (stepContext, cancellationToken) =>
{
// next step
});
}
private static Attachment CreateAdaptiveCardAttachment(string filePath)
{
var adaptiveCardJson = File.ReadAllText(filePath);
var adaptiveCardAttachment = new Attachment()
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(adaptiveCardJson),
};
return adaptiveCardAttachment;
}
This is the card
{
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"text": "What is your Occupation?"
},
{
"type": "Input.Text",
"id": "Occupation",
"placeholder": "Occupation"
},
{
"type": "TextBlock",
"text": "Are you married? "
},
{
"type": "Input.ChoiceSet",
"id": "Married",
"value": "true",
"choices": [
{
"title": "Yes",
"value": "true"
},
{
"title": "No",
"value": "false"
}
],
"style": "expanded"
},
{
"type": "TextBlock",
"text": "When is your birthday?"
},
{
"type": "Input.Date",
"id": "Birthday",
"value": ""
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Submit",
"data": {
"id": "1234567890"
}
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0"
}
Thanks guys.
EDIT: for future reference of others this is the answer i found.
AddStep(async (stepContext, cancellationToken) =>
{
var state = await (stepContext.Context.TurnState["BasicAccessors"] as BasicAccessors).BasicStateAccessor.GetAsync(stepContext.Context);
var jsonString = (JObject)stepContext.Context.Activity.Value;
BasicState results = JsonConvert.DeserializeObject<BasicState>(jsonString.ToString());
state.Occupation = results.Occupation;
state.Married = results.Married;
state.Birthday = results.Birthday;
return await stepContext.NextAsync();
});
Let me answer your questions in reverse order:
Yes, it's true, you need to return something from your step, but as you point out you're not ready for it to move to the next step yet. The answer is that you want to use a prompt at this point! Now I see you have some code in here commented out to do this and maybe what's confusing is that, today, there is no specific prompt for working with cards. Instead you do want to use a general purpose
TextPrompt
and we'll set the activity on that to something other than just simple text.With this in mind, you would keep your code above that is using
CreateReply
to build yourActivity
with card attachments, but, instead of sending thatActivity
yourself withSendActivityAsync
you want to set it as the value of thePrompt
property of theTextPrompt
like so:Ok, so that's one half of the problem. With that in mind now, let's circle back to the first part of your question:
Well, your adaptive card is using the
Submit
action which means that you will receive an activity that contains the values of the form in theValues
property of theActivity
, however because we used aTextPrompt
above the default validation behavior of theTextPrompt
is going to be validating that some value was supplied for theText
portion of theActivity
which there won't be in this case. So, to fix that, when you configure theTextPrompt
you really want to provide your ownPromptValidator<T>
like so:This basically says the input is valid no matter what. You could make it richer if you wanted by actually checking the details of the value, but this should unblock you for now.
Now, back in your
WaterfallDialog
your next step is going to be receiving theActivity
whoseValue
property will be aJObject
which you can either use directly or you can callJObject::ToObject<T>
to convert it into a specific class you've created that represents your form input:I want to just close this answer out by saying that, in answering your question, I've recorded a bunch of feedback that I intend to send to the product team to improve this situation both in terms of API design and documentation because, clearly, this is not obvious or optimal.