Botframework V4:关于输入表单卡的问题
Botframework V4: Question about Input forms cards
你好,我有这张输入表格卡片。它渲染正确但我怎样才能得到它的结果?我怎样才能让机器人在继续下一步之前等待用户提交?输入 stepContext.NextAsync 会自动触发下一步。但是删除它会导致错误,因为它需要 return 东西。
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;
}
这是卡片
{
"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"
}
谢谢大家。
编辑:供其他人将来参考,这是我找到的答案。
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();
});
让我倒序回答你的问题:
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.
是的,确实如此,您需要 return 从您的步骤开始,但正如您指出的那样,您还没有准备好进入下一步。答案是此时您想使用提示!现在我看到你在这里注释掉了一些代码来执行此操作,也许令人困惑的是,今天没有使用卡片的特定提示。相反,您确实想使用通用的 TextPrompt
,我们会将其上的 activity 设置为除简单文本以外的其他内容。
考虑到这一点,您可以保留上面使用 CreateReply
构建带有卡片附件的 Activity
的代码,而不是使用 Activity
自己发送 SendActivityAsync
你想将它设置为 TextPrompt
的 Prompt
属性 的值,像这样:
AddStep(async (stepContext, cancellationToken) =>
{
return await stepContext.PromptAsync(
"myPrompt",
new PromptOptions
{
Prompt = new Activity
{
Type = ActivityTypes.Message,
Attachments = new List<Attachment>()
{
CreateAdaptiveCardAttachment(_cards),
},
},
},
cancellationToken: cancellationToken);
});
好的,这就是问题的一半。现在考虑到这一点,让我们回到问题的第一部分:
Hello i have this input forms card. It is rendering properly but how can i get its results?
嗯,您的自适应卡正在使用 Submit
操作,这意味着您将收到一个 activity,其中包含 Values
属性 中的表单值Activity
的,但是因为我们在上面使用了 TextPrompt
,TextPrompt
的默认验证行为将验证是否为 Text
部分提供了一些值Activity
在这种情况下不会有。所以,要解决这个问题,当你配置 TextPrompt
时,你真的想像这样提供你自己的 PromptValidator<T>
:
Add(new TextPrompt("myPrompt", new PromptValidator<string>(async (pvc, ct) => true)));
这基本上表示无论如何输入都是有效的。如果您愿意,您可以通过实际检查值的详细信息来使其更丰富,但这应该暂时解除您的阻塞。
现在,回到您的 WaterfallDialog
,您的下一步将是接收 Activity
,其 Value
属性 将是一个 JObject
,您可以直接使用,也可以调用 JObject::ToObject<T>
将其转换为您创建的代表表单输入的特定 class:
AddStep(async (stepContext, cancellationToken) =>
{
// This will give you a JObject representation of the incoming values
var rawValues = (JObject)stepContext.Context.Activity.Values;
// You can convert that to something more strongly typed like so
// where MyFormValues is a class you've defined
var myFormValues = rawValues.ToObject<MyFormValues>();
});
我想结束这个回答说,在回答你的问题时,我已经记录了一堆反馈,我打算发送给产品团队以改善这种情况 API 设计和文档,因为很明显,这不是显而易见的或最佳的。
你好,我有这张输入表格卡片。它渲染正确但我怎样才能得到它的结果?我怎样才能让机器人在继续下一步之前等待用户提交?输入 stepContext.NextAsync 会自动触发下一步。但是删除它会导致错误,因为它需要 return 东西。
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;
}
这是卡片
{
"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"
}
谢谢大家。
编辑:供其他人将来参考,这是我找到的答案。
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();
});
让我倒序回答你的问题:
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.
是的,确实如此,您需要 return 从您的步骤开始,但正如您指出的那样,您还没有准备好进入下一步。答案是此时您想使用提示!现在我看到你在这里注释掉了一些代码来执行此操作,也许令人困惑的是,今天没有使用卡片的特定提示。相反,您确实想使用通用的 TextPrompt
,我们会将其上的 activity 设置为除简单文本以外的其他内容。
考虑到这一点,您可以保留上面使用 CreateReply
构建带有卡片附件的 Activity
的代码,而不是使用 Activity
自己发送 SendActivityAsync
你想将它设置为 TextPrompt
的 Prompt
属性 的值,像这样:
AddStep(async (stepContext, cancellationToken) =>
{
return await stepContext.PromptAsync(
"myPrompt",
new PromptOptions
{
Prompt = new Activity
{
Type = ActivityTypes.Message,
Attachments = new List<Attachment>()
{
CreateAdaptiveCardAttachment(_cards),
},
},
},
cancellationToken: cancellationToken);
});
好的,这就是问题的一半。现在考虑到这一点,让我们回到问题的第一部分:
Hello i have this input forms card. It is rendering properly but how can i get its results?
嗯,您的自适应卡正在使用 Submit
操作,这意味着您将收到一个 activity,其中包含 Values
属性 中的表单值Activity
的,但是因为我们在上面使用了 TextPrompt
,TextPrompt
的默认验证行为将验证是否为 Text
部分提供了一些值Activity
在这种情况下不会有。所以,要解决这个问题,当你配置 TextPrompt
时,你真的想像这样提供你自己的 PromptValidator<T>
:
Add(new TextPrompt("myPrompt", new PromptValidator<string>(async (pvc, ct) => true)));
这基本上表示无论如何输入都是有效的。如果您愿意,您可以通过实际检查值的详细信息来使其更丰富,但这应该暂时解除您的阻塞。
现在,回到您的 WaterfallDialog
,您的下一步将是接收 Activity
,其 Value
属性 将是一个 JObject
,您可以直接使用,也可以调用 JObject::ToObject<T>
将其转换为您创建的代表表单输入的特定 class:
AddStep(async (stepContext, cancellationToken) =>
{
// This will give you a JObject representation of the incoming values
var rawValues = (JObject)stepContext.Context.Activity.Values;
// You can convert that to something more strongly typed like so
// where MyFormValues is a class you've defined
var myFormValues = rawValues.ToObject<MyFormValues>();
});
我想结束这个回答说,在回答你的问题时,我已经记录了一堆反馈,我打算发送给产品团队以改善这种情况 API 设计和文档,因为很明显,这不是显而易见的或最佳的。