Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
前回はフォームフローの概要を紹介しました。今回はフォームフローの高度な使い方を見ていきます。
FormBuilder
前回は以下のコードを使ってクラスからダイアログを自動生成しました。
return new FormBuilder<OutlookEvent>()
.Message("イベントを作成します。")
.AddRemainingFields() // すべてのフィールドを処理対象として追加
.OnCompletion(processOutlookEventCreate)
.Build();
FormBuilder は様々な機能がありますが、上記の例では AddRemainingFields メソッドで、クラスのすべてのフィールドをダイアログに追加、OnCompletion メソッドで完了時の処理呼び出しをしています。プロンプトの情報はクラスのプロパティに定義した Prompt や Template 属性から情報を得ています。
[Prompt("件名は?")]
public string Subject { get; set; }
以下で他の機能を見ていきます。
個別にフィールド追加
Field メソッドを使うことで、個別にフィールドが追加できます。例えば以下の場合は件名と詳細だけを含めています。
return new FormBuilder<OutlookEvent>()
.Message("イベントを作成します。")
.Field(nameof(OutlookEvent.Subject))
.Field(nameof(OutlookEvent.Description))
.OnCompletion(processOutlookEventCreate)
.Build();
Field メソッドは個別にフィールドを追加するだけでなく、プロンプトの指定、表示するかどうかの検証、入力した値の検証などを含めることが出来ます。
return new FormBuilder<OutlookEvent>()
.Message("イベントを作成します。")
.Field(nameof(OutlookEvent.Subject), prompt:"予定の件名は?", validate: async (state, value) =>
{
// 入力を検証
var subject = (string)value;
var result = new ValidateResult() { IsValid = true, Value = subject };
if (subject.Contains("FormFlow"))
{
result.IsValid = false;
result.Feedback = "FormFlow については予定を作れません。";
}
return result;
})
.Field(nameof(OutlookEvent.Description))
.Field(nameof(OutlookEvent.Start))
.Field(nameof(OutlookEvent.IsAllDay))
.Field(nameof(OutlookEvent.Hours), active: (state) =>
{
// 表示するかを検証
if (state.IsAllDay)
return false;
else
return true;
})
.OnCompletion(processOutlookEventCreate)
.Build();
エミュレーターで検証
件名の表示と検証の確認
Hours が表示されるか検証
メッセージをカスタマイズ
Message メソッドでユーザーに対して返信できますが、現在の値を使いたい場合は以下のようにできます。
return new FormBuilder<OutlookEvent>()
.Message("イベントを作成します。")
.Field(nameof(OutlookEvent.Subject))
.Message(async (state)=> { return new PromptAttribute($"現在の件名は{state.Subject}です。"); })
.Field(nameof(OutlookEvent.Description))
.OnCompletion(processOutlookEventCreate)
.Build();
最終確認
Confirm メソッドを使うと、確認を挟むことができます。
return new FormBuilder<OutlookEvent>()
.Message("イベントを作成します。")
.Field(nameof(OutlookEvent.Subject), prompt: "予定の件名は?", validate: async (state, value) =>
{
// 入力を検証
var subject = (string)value;
var result = new ValidateResult() { IsValid = true, Value = subject };
if (subject.Contains("FormFlow"))
{
result.IsValid = false;
result.Feedback = "FormFlow については予定を作れません。";
}
return result;
})
.Field(nameof(OutlookEvent.Description))
.Field(nameof(OutlookEvent.Start))
.Field(nameof(OutlookEvent.IsAllDay))
.Field(nameof(OutlookEvent.Hours), active: (state) =>
{
// 表示するかを検証
if (state.IsAllDay)
return false;
else
return true;
})
.Confirm(async (state) =>
{
if (state.IsAllDay)
return new PromptAttribute("終日イベントでいいですか?");
else
return new PromptAttribute($"イベントは{state.Hours}時間でいいですか?");
})
.OnCompletion(processOutlookEventCreate)
.Build();
最終的に以下のコードにしました。CreateEventDialog.cs を以下コードと差し替えます。
using Autofac;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FormFlow;
using Microsoft.Graph;
using O365Bot.Models;
using O365Bot.Services;
using System;
using System.Threading.Tasks;
namespace O365Bot.Dialogs
{
[Serializable]
public class CreateEventDialog : IDialog<bool> // このダイアログが完了時に返す型
{
public async Task StartAsync(IDialogContext context)
{
// FormFlow でダイアログを作成して、呼び出し。
var outlookEventFormDialog = FormDialog.FromForm(this.BuildOutlookEventForm, FormOptions.PromptInStart);
context.Call(outlookEventFormDialog, this.ResumeAfterDialog);
}
private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<OutlookEvent> result)
{
await context.PostAsync("イベントを作成しました。");
// ダイアログの完了を宣言
context.Done(true);
}
private IForm<OutlookEvent> BuildOutlookEventForm()
{
OnCompletionAsyncDelegate<OutlookEvent> processOutlookEventCreate = async (context, state) =>
{
using (var scope = WebApiApplication.Container.BeginLifetimeScope())
{
IEventService service = scope.Resolve<IEventService>(new TypedParameter(typeof(IDialogContext), context));
// TimeZone は https://graph.microsoft.com/beta/me/mailboxSettings で取得可能だがここでは一旦ハードコード
Event @event = new Event()
{
Subject = state.Subject,
Start = new DateTimeTimeZone() { DateTime = state.Start.ToString(), TimeZone = "Tokyo Standard Time" },
IsAllDay = state.IsAllDay,
End = state.IsAllDay ? null : new DateTimeTimeZone() { DateTime = state.Start.AddHours(state.Hours).ToString(), TimeZone = "Tokyo Standard Time" },
Body = new ItemBody() { Content = state.Description, ContentType = BodyType.Text }
};
await service.CreateEvent(@event);
}
};
return new FormBuilder<OutlookEvent>()
.Message("イベントを作成します。")
.Field(nameof(OutlookEvent.Subject))
.Field(nameof(OutlookEvent.Description))
.Field(nameof(OutlookEvent.Start))
.Field(nameof(OutlookEvent.IsAllDay))
.Field(nameof(OutlookEvent.Hours), active: (state) =>
{
// 表示するかを検証
if (state.IsAllDay)
return false;
else
return true;
})
.OnCompletion(processOutlookEventCreate)
.Build();
}
}
}
テストの実行
前回失敗するようになったテストも、今回の変更で通るはずです。コードをチェックインして確認します。
まとめ
フォームフローの便利さと柔軟性は素晴らしいです。今回紹介した機能のほかにも、クラス側の属性として色々設定できますので是非試してください。
次回はそろそろ多言語処理でも。