Create Bot for Microsoft Graph with DevOps 10: BotBuilder features – FormFlow 201 FormBuilder

In this article, I explain about FormBuilder, which builds a form from model, and it gives you flexible options to customize the form.

FormBuilder
In previous article, I used following code to build a form.

 return new FormBuilder<OutlookEvent>()
 .Message("Creating an event.")
 .AddRemainingFields() // add all (remaing) fields to the form.
 .OnCompletion(processOutlookEventCreate)
 .Build();

I also get prompt string from model by using Prompt attribute. I uses AddRemainingFields to add all fields at once, but I use Field method this time to add them one by one.

Add a Field

If you want to add fields one by one, you can use Field method. In this example, I added Subject and Description only for the form.

 return new FormBuilder<OutlookEvent>()
    .Message("Creating an event.")
    .Field(nameof(OutlookEvent.Subject))
    .Field(nameof(OutlookEvent.Description))
    .OnCompletion(processOutlookEventCreate)
    .Build();

Add Prompt

Let’s add prompt string.

 return new FormBuilder<OutlookEvent>()
    .Message("Creating an event.")
    .Field(nameof(OutlookEvent.Subject), prompt: "What is the title?")
    .Field(nameof(OutlookEvent.Description), prompt: "What is the detail?")
    .OnCompletion(processOutlookEventCreate)
    .Build();

Control display

In case you want to control if the field should be displayed or not, you can use active parameter. In this case, I check if Hours field should be displayed depending on if it’s all day event.

 return new FormBuilder<OutlookEvent>()
    .Message("Creating an event.")
    .Field(nameof(OutlookEvent.Subject), prompt: "What is the title?")
    .Field(nameof(OutlookEvent.Description), prompt: "What is the detail?")
    .Field(nameof(OutlookEvent.Start), prompt: "When do you start? Use dd/MM/yyyy HH:mm format.")
    .Field(nameof(OutlookEvent.IsAllDay), prompt: "Is this all day event?{||}")
    .Field(nameof(OutlookEvent.Hours), prompt: "How many hours?", active: (state) => 
    {
        // If this is all day event, then do not display hours field.
        if (state.IsAllDay)
            return false;
        else
            return true;
    })
    .OnCompletion(processOutlookEventCreate)
    .Build();

Validate the input

You can also validate the input by using validate parameter.

 return new FormBuilder<OutlookEvent>()
    .Message("Creating an event.")
    .Field(nameof(OutlookEvent.Subject), prompt: "What is the title?", 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 = "You cannot include FormFlow as subject.";
        }
        return result;

    })
    .Field(nameof(OutlookEvent.Description), prompt: "What is the detail?")
    .Field(nameof(OutlookEvent.Start), prompt: "When do you start? Use dd/MM/yyyy HH:mm format.")
    .Field(nameof(OutlookEvent.IsAllDay), prompt: "Is this all day event?{||}")
    .Field(nameof(OutlookEvent.Hours), prompt: "How many hours?", active: (state) => 
    {
        // If this is all day event, then do not display hours field.
        if (state.IsAllDay)
            return false;
        else
            return true;
    })
    .OnCompletion(processOutlookEventCreate)
    .Build();

Try with emulator

Run the application and try with emulator.

image image

Now all the tests should pass as it behave exactly same as before.

Use current value for message

If you want to show message with current field value, you can do so by using following code.

 return new FormBuilder<OutlookEvent>()
    .Message("Creating an event.")
    .Field(nameof(OutlookEvent.Subject), prompt: "What is the title?", 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 = "You cannot include FormFlow as subject.";
        }
        return result;

    })
    .Message(async (state) => { return new PromptAttribute($"The current subject is{state.Subject}"); })
    .Field(nameof(OutlookEvent.Description), prompt: "What is the detail?")
    .Field(nameof(OutlookEvent.Start), prompt: "When do you start? Use dd/MM/yyyy HH:mm format.")
    .Field(nameof(OutlookEvent.IsAllDay), prompt: "Is this all day event?{||}")
    .Field(nameof(OutlookEvent.Hours), prompt: "How many hours?", active: (state) =>
    {
        // If this is all day event, then do not display hours field.
        if (state.IsAllDay)
            return false;
        else
            return true;
    })
    .OnCompletion(processOutlookEventCreate)
    .Build();

Confirmation

If you want to add confirm step, you can do so by using Confirm method. Again, you can use current value for fields.

 return new FormBuilder<OutlookEvent>()
    .Message("Creating an event.")
    .Field(nameof(OutlookEvent.Subject), prompt: "What is the title?", 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 = "You cannot include FormFlow as subject.";
        }
        return result;

    })
    .Message(async (state) => { return new PromptAttribute($"The current subject is{state.Subject}"); })
    .Field(nameof(OutlookEvent.Description), prompt: "What is the detail?")
    .Field(nameof(OutlookEvent.Start), prompt: "When do you start? Use dd/MM/yyyy HH:mm format.")
    .Field(nameof(OutlookEvent.IsAllDay), prompt: "Is this all day event?{||}")
    .Field(nameof(OutlookEvent.Hours), prompt: "How many hours?", active: (state) =>
    {
        // If this is all day event, then do not display hours field.
        if (state.IsAllDay)
            return false;
        else
            return true;
    })
    .Confirm(async (state) =>
    {
        if (state.IsAllDay)
            return new PromptAttribute("Are you sure if this is all day events?");
        else
            return new PromptAttribute($"Are you sure the event is {state.Hours} hours long?");
    })
    .OnCompletion(processOutlookEventCreate)
    .Build();

Try with emulator by yourself.

Summery

FormFlow is very easy to use, yet flexible enough. It also provides out-of-box features such as ‘help’ and ‘state’, so please read the document as you need.

GitHub: https://github.com/kenakamu/BotWithDevOps-Blog-sample/tree/master/article10

Ken