FormFlow 为字符串字段定义选项
FormFlow define options for string field
我正在使用机器人框架,我正在尝试使用 FormFlow 动态定义表单。我有一个特定领域的问题:
.Field(new FieldReflector<IssueFormModel>(nameof(IssueResultModel.ProductPlatform))
.SetType(typeof(string))
.SetDefine(async (issue, field) =>
{
if (issue.ProductName != null)
{
foreach (var platform in productsWithPlatforms[issue.ProductName])
{
field.AddDescription(platform, platform).AddTerms(platform, platform);
}
}
return await Task.FromResult(true);
})
.SetPrompt(new PromptAttribute("Which platform of {ProductName}{||}?"))
.SetAllowsMultiple(false)
.SetValidate(async (state, value) => await ValidateProductPlatform(value, state)))
问题是 ProductPlatform 依赖于 ProductName,因此它是一个字符串。这工作正常,但是,通过这种方式,机器人不会显示可能平台的选项(尽管 SetPrompt 中有 {||})。
当我将类型设置为 null SetType(null)
时,机器人现在将可能的平台显示为按钮,但是,当用户决定输入错误时,它永远不会进入 ValidateProductPlatform平台而不是点击正确的平台(我想验证本身已经在 SetDefine 级别完成)。我需要通过 ValidateProductPlatform 验证用户输入的唯一原因是我想在 3 次尝试失败后取消表单。
那么,有什么办法可以做到这一点吗?:
用户有基于 ProductName 的 ProductPlaftorm 选项(作为按钮),但他们(可能)没有点击,而是输入了错误的平台,并且在 3 次错误尝试后,表单结束。
PS:我看到了 Microsoft Bot : How to capture Too Many Attempts in Form Flow? 但我无法使用它,因为在我的案例中它似乎忽略了 SetValidate(当 SetType(null)
)
创建同时使用按钮和自定义验证方法(在文本输入和按钮按下时)的提示器的一种技术是在您的表单上插入提示器覆盖。
对于您的特定用例,这看起来类似于以下代码块(此实现覆盖 ProductPlatform 字段上的原始提示,以便向卡片添加按钮菜单):
form.Prompter(async (context, prompt, state, field) =>
{
//this part is added in on top of the original implementation of form.Prompter
if (field.Name == nameof(IssueFormModel.ProductPlatform) && prompt.Buttons.Count == 0)
{
foreach (var fieldValue in field.Values)
{
var asString = fieldValue as string;
prompt.Buttons.Add(new DescribeAttribute(asString, title: asString));
}
}
//this check prevents prompts that are sent through from ValidateResult without a FeedbackCard from crashing the bot.
if (prompt.Description != null) {
var preamble = context.MakeMessage();
var promptMessage = context.MakeMessage();
if (prompt.GenerateMessages(preamble, promptMessage))
{
await context.PostAsync(preamble);
}
await context.PostAsync(promptMessage);
}
return prompt;
});
这意味着您构建表单的整个方法应该如下所示:
private IForm<IssueFormModel> BuildProductForm()
{
var form = new FormBuilder<IssueFormModel>()
.Field(new FieldReflector<IssueFormModel>(nameof(IssueFormModel.ProductName))
.SetPrompt(new PromptAttribute("Type either product1 or product2"))
.SetValidate(async (state, value) => await ValidateProductName(value, state)))
.Field(new FieldReflector<IssueFormModel>(nameof(IssueFormModel.ProductPlatform))
.SetType(typeof(string))
.SetDefine(async (issue, field) =>
{
if (issue.ProductName != null)
{
foreach (var platform in productsWithPlatforms[issue.ProductName])
{
field.AddDescription(platform, platform).AddTerms(platform, platform);
}
}
return await Task.FromResult(true);
})
.SetPrompt(new PromptAttribute("Which platform of {ProductName}{||}?"))
.SetAllowsMultiple(false)
.SetValidate(async (state, value) => await ValidateProductPlatform(value, state)))
.AddRemainingFields()
.Confirm(prompt: "Is this your issue? {*}{||}");
form.Prompter(async (context, prompt, state, field) =>
{
if (field.Name == nameof(IssueFormModel.ProductPlatform) && prompt.Buttons.Count == 0)
{
foreach (var fieldValue in field.Values)
{
var asString = fieldValue as string;
prompt.Buttons.Add(new DescribeAttribute(asString, title: asString));
}
}
if (prompt.Description != null) {
var preamble = context.MakeMessage();
var promptMessage = context.MakeMessage();
if (prompt.GenerateMessages(preamble, promptMessage))
{
await context.PostAsync(preamble);
}
await context.PostAsync(promptMessage);
}
return prompt;
});
return form.Build();
}
这里的主要变化是在最后一次调用 FormBuilder 之后插入 form.Prompter,而不是立即返回新表单。覆盖的提示使用类型 "string" 调用您的自定义验证,您应该能够对不成功的条目以及其他任何内容执行您想要的操作。
我正在使用机器人框架,我正在尝试使用 FormFlow 动态定义表单。我有一个特定领域的问题:
.Field(new FieldReflector<IssueFormModel>(nameof(IssueResultModel.ProductPlatform))
.SetType(typeof(string))
.SetDefine(async (issue, field) =>
{
if (issue.ProductName != null)
{
foreach (var platform in productsWithPlatforms[issue.ProductName])
{
field.AddDescription(platform, platform).AddTerms(platform, platform);
}
}
return await Task.FromResult(true);
})
.SetPrompt(new PromptAttribute("Which platform of {ProductName}{||}?"))
.SetAllowsMultiple(false)
.SetValidate(async (state, value) => await ValidateProductPlatform(value, state)))
问题是 ProductPlatform 依赖于 ProductName,因此它是一个字符串。这工作正常,但是,通过这种方式,机器人不会显示可能平台的选项(尽管 SetPrompt 中有 {||})。
当我将类型设置为 null SetType(null)
时,机器人现在将可能的平台显示为按钮,但是,当用户决定输入错误时,它永远不会进入 ValidateProductPlatform平台而不是点击正确的平台(我想验证本身已经在 SetDefine 级别完成)。我需要通过 ValidateProductPlatform 验证用户输入的唯一原因是我想在 3 次尝试失败后取消表单。
那么,有什么办法可以做到这一点吗?: 用户有基于 ProductName 的 ProductPlaftorm 选项(作为按钮),但他们(可能)没有点击,而是输入了错误的平台,并且在 3 次错误尝试后,表单结束。
PS:我看到了 Microsoft Bot : How to capture Too Many Attempts in Form Flow? 但我无法使用它,因为在我的案例中它似乎忽略了 SetValidate(当 SetType(null)
)
创建同时使用按钮和自定义验证方法(在文本输入和按钮按下时)的提示器的一种技术是在您的表单上插入提示器覆盖。 对于您的特定用例,这看起来类似于以下代码块(此实现覆盖 ProductPlatform 字段上的原始提示,以便向卡片添加按钮菜单):
form.Prompter(async (context, prompt, state, field) =>
{
//this part is added in on top of the original implementation of form.Prompter
if (field.Name == nameof(IssueFormModel.ProductPlatform) && prompt.Buttons.Count == 0)
{
foreach (var fieldValue in field.Values)
{
var asString = fieldValue as string;
prompt.Buttons.Add(new DescribeAttribute(asString, title: asString));
}
}
//this check prevents prompts that are sent through from ValidateResult without a FeedbackCard from crashing the bot.
if (prompt.Description != null) {
var preamble = context.MakeMessage();
var promptMessage = context.MakeMessage();
if (prompt.GenerateMessages(preamble, promptMessage))
{
await context.PostAsync(preamble);
}
await context.PostAsync(promptMessage);
}
return prompt;
});
这意味着您构建表单的整个方法应该如下所示:
private IForm<IssueFormModel> BuildProductForm()
{
var form = new FormBuilder<IssueFormModel>()
.Field(new FieldReflector<IssueFormModel>(nameof(IssueFormModel.ProductName))
.SetPrompt(new PromptAttribute("Type either product1 or product2"))
.SetValidate(async (state, value) => await ValidateProductName(value, state)))
.Field(new FieldReflector<IssueFormModel>(nameof(IssueFormModel.ProductPlatform))
.SetType(typeof(string))
.SetDefine(async (issue, field) =>
{
if (issue.ProductName != null)
{
foreach (var platform in productsWithPlatforms[issue.ProductName])
{
field.AddDescription(platform, platform).AddTerms(platform, platform);
}
}
return await Task.FromResult(true);
})
.SetPrompt(new PromptAttribute("Which platform of {ProductName}{||}?"))
.SetAllowsMultiple(false)
.SetValidate(async (state, value) => await ValidateProductPlatform(value, state)))
.AddRemainingFields()
.Confirm(prompt: "Is this your issue? {*}{||}");
form.Prompter(async (context, prompt, state, field) =>
{
if (field.Name == nameof(IssueFormModel.ProductPlatform) && prompt.Buttons.Count == 0)
{
foreach (var fieldValue in field.Values)
{
var asString = fieldValue as string;
prompt.Buttons.Add(new DescribeAttribute(asString, title: asString));
}
}
if (prompt.Description != null) {
var preamble = context.MakeMessage();
var promptMessage = context.MakeMessage();
if (prompt.GenerateMessages(preamble, promptMessage))
{
await context.PostAsync(preamble);
}
await context.PostAsync(promptMessage);
}
return prompt;
});
return form.Build();
}
这里的主要变化是在最后一次调用 FormBuilder 之后插入 form.Prompter,而不是立即返回新表单。覆盖的提示使用类型 "string" 调用您的自定义验证,您应该能够对不成功的条目以及其他任何内容执行您想要的操作。