In part 1, we have seen how quickly we can create a guided conversation using the FormFlow feature of Microsoft Bot Framework. In this part, we will see some of the advanced stuff that we can use with FormFlow in real time applications while dealing with the real business scenarios.
While FormFlow is packed with great features and great customization abilities, yet there are a few things that are always required when dealing with real-life business scenarios. For example, suppose there is a food ordering use case where the food menu items need to get populated from a specific data source like a database maybe or some REST API. As we have seen earlier in the previous post, the basic FormFlow is not capable of handling dynamic options. Thankfully, Microsoft has also given us that extension point as well where we can customize our form using the form builder class to support dynamic form options.
Another use case that may also be required when solving real-time business problems is the need to display Menu Items with some pictures and a description rather than just displaying their names. Something which is by default supported while we use Dialog, we can easily display Hero card in a carousel format. But what about this feature in FormFlow? Can we get the best of both worlds? The answer is yes. Microsoft Bot Framework has given us the flexibility to achieve that as well.
So, let us proceed to the examples.
Dynamic FormFlow
In order to show how we can achieve dynamic FormFlow, I will use the simple food ordering use case that I used in the previous part. Here, instead of using an enum with predefined values for MenuItems, we are going to use a sample data source to populate the MenuItems. Let's look at the OrderDetails Form to understand better.
Edit the OrderDetails class.
- [Serializable]
- public class OrderDetails {
- public List < MenuOption > MenuItems {
- get;
- set;
- }
- public DeliveryOptions DeliveryMode {
- get;
- set;
- }
- public string UserName {
- get;
- set;
- }
- public string Phone {
- get;
- set;
- }
- public string address {
- get;
- set;
- }
- }
-
- public enum DeliveryOptions {
- TakeAway = 1,
- Delivery
- }
- [Serializable]
- public class MenuOption {
- public string ItemName {
- get;
- set;
- }
- public string Description {
- get;
- set;
- }
- public string ItemImage {
- get;
- set;
- }
- }
- }
If we see above, everything else is the same as the previous example except for MenuItems which is now List<MenuOption> and as we can see, MenuOption is a class and not an enum now with predefined set of values.
So now, it is clear that we are going to populate the values of MenuItems in OrderDetails from some external data source. For simplicity, let us define a class and a method that returns List<MenuItems>.
- public class MenuDB {
- public static List < MenuOption > GetAllMenuOptions() {
- return new List < MenuOption > () {
- new MenuOption() {
- ItemName = "CrispyChicken"
- },
- new MenuOption() {
- ItemName = "ChickenWings"
- },
- new MenuOption() {
- ItemName = "ChickenDrumStick"
- },
- new MenuOption() {
- ItemName = "ChickenPopcorn"
- },
- };
- }
- }
In a real life use case, you can always replace this method with your own implementation.
And now, the most important part - Modifying the Formbuilder’s BuildForm() method. Let us now modify the BuildForm() method to support dynamic form values.
- public static IForm < OrderDetails > BuildForm() {
- var menuItems = MenuDB.GetAllMenuOptions();
- var builder = new FormBuilder < OrderDetails > ();
- builder.Message("Welcome to demo Restaurant bot!").Field(new FieldReflector < OrderDetails > (nameof(MenuItems)).SetType(null).SetDefine((state, field) => {
- foreach(var item in menuItems) {
- field.AddDescription(item, item.ItemName).AddTerms(item, item.ItemName);
- }
- return Task.FromResult(true);
- })).AddRemainingFields().OnCompletion(async (context, order) => {
- await context.PostAsync("Thanks for your order!");
- });
- return builder.Build();
- }
- }
Before going into details about the above code sample, let's look at the changes that we have done so far compared to the previous basic FormFlow.
So we can see that two methods have been added to the form builder. One is the Field method and the other one is the AddRemainingFields method.
In order to use the Field method and the FieldReflector class, we will first require importing the package -Microsoft.Bot.Builder.FormFlow.Advanced
What this does is basically gives us the extensibility to completely define a form field of our own and totally override the default behavior. The Advanced.SetType(null) field type null represents an enumeration field.
Advanced.SetDefine specifies an async delegate that defines a field. In our example, we have looped through the data source to populate the values. The delegate is passed on the current state object and the Advanced.Field that is being dynamically defined. The delegate uses the field’s fluent methods to dynamically define values. In this example, the values are strings. The AddDescription and AddTerms methods specify the descriptions and terms for each value.
That completes our changes to support dynamic FormFlow, but this is just the beginning of the en-numerous possibilities that formbuilder class provides to customize the behavior of your forms.
To know more about Advanced package and form builder class in FormFlow, please visit the Microsoft doc here.
HeroCard and carousel in FormFlow
Now, for more exciting stuff let us look into the ability to display field options in form of HeroCard in a carousel format.
If you are new to Bot Framework and don’t have any idea about carousel of cards, you can check out the samples below.
Below is a sample of how a carousel of cards would actually look like.
In order to demonstrate the display of menu items in a carousel format, let us change the use case for ordering food a little bit. Since the display of Hero card in carousel format is actually used to display for the choice of one item and not multiple, thus we are going to consider that only one menu item can be selected at a time. This is just for simplicity so that we can see the HeroCard display without much modification in the demo application.
Let us look at the code now.
In MenuDB class, now we have returned all the MenuItem properties.
- public class MenuDB {
- public static List < MenuOption > GetAllMenuOptions() {
- return new List < MenuOption > () {
- new MenuOption() {
- ItemName = "CrispyChicken", Description = "4 pcs of crispy chicken.", ItemImage = "http://divascancook.com/wp-content/uploads/2015/01/IMG_0231.jpg"
- },
- new MenuOption() {
- ItemName = "ChickenWings", Description = "6 pcs of crispy chicken wings.", ItemImage = "https://www.munatycooking.com/wp-content/uploads/2016/05/crispy-spicy-chicken-wings-5.jpg"
- },
- new MenuOption() {
- ItemName = "ChickenDrumStick", Description = "4 pcs of chicken drumsticks.", ItemImage = "https://www.budgetbytes.com/wp-content/uploads/2016/03/Crispy-Baked-Honey-Sriracha-Chicken-Drumsticks-above-straight.jpg"
- },
- new MenuOption() {
- ItemName = "ChickenPopcorn", Description = "1 medium chicken popcorn.", ItemImage = "http://mybodymykitchen.com/wp-content/uploads/2016/02/jalapeno-popcorn-chicken-1024x1024.jpg"
- },
- };
- }
- }
This is pretty straightforward. As of the image url’s, they are taken from random sites that came in Google search. Thus, moving on to the next class i.e. the
OrderDetails class.
- [Serializable]
- public class OrderDetails {
- public MenuOption MenuItems {
- get;
- set;
- }
- public DeliveryOptions DeliveryMode {
- get;
- set;
- }
- public string UserName {
- get;
- set;
- }
- public string Phone {
- get;
- set;
- }
- public string address {
- get;
- set;
- }
- public static IForm < OrderDetails > BuildForm() {
- var menuItems = MenuDB.GetAllMenuOptions();
- var builder = new FormBuilder < OrderDetails > ();
- builder.Message("Welcome to demo Restaurant bot!").Field(new FieldReflector < OrderDetails > (nameof(MenuItems)).SetType(null).SetDefine((state, field) => {
- foreach(var item in menuItems) {
- field.AddDescription(item, new DescribeAttribute() {
- Title = item.ItemName, Description = item.ItemName, SubTitle = item.Description, Image = item.ItemImage
- }).AddTerms(item, item.ItemName);
- }
- return Task.FromResult(true);
- }).SetPrompt(new PromptAttribute(" What would you like to order? \n {||} \n") {
- ChoiceStyle = ChoiceStyleOptions.Carousel
- }).SetAllowsMultiple(false)).AddRemainingFields().OnCompletion(async (context, order) => {
- await context.PostAsync("Thanks for your order!");
- });
- return builder.Build();
- }
- }
Here, we can see that there are some changes, 1st of all the
MenuItemsproperty in the
OrderDetails class is not of type
MenuOption.
And in the BuildForm() method we have changed the field.AddDescription Method. We have used another overload for that methodwhere DescriptionAttribute takes care of all the properties required for a field option to be displayed as HeroCard in carousel format in which ever channel possible.
- field.AddDescription(item, new DescribeAttribute()
- {
- Title = item.ItemName, Description = item.ItemName, SubTitle = item.Description, Image = item.ItemImage
- })
The last setting is the SetPrompt method where we have specified the ChoiseStyle to be carousel
- .SetPrompt(new PromptAttribute(" What would you like to order? \n {||} \n")
- {
- ChoiceStyle = ChoiceStyleOptions.Carousel
- })
We can even set that to ChoiceStyleOptions.Auto which would have been the better option as in that case FormFlow would take care of displaying carousel format whenever possible else it will fallback to the default option.
Conclusion
This wraps up some of the advanced features of FormFlow. I hope you guys would find this useful. As I mentioned before there are plenty of customizations possible in FormFlow in bot framework. So do take a look at the docs for all possible options.
I have uploaded all the code used in the demo on GitHub, you can access it here. Do let me know in the comments if this post is helpful.
<<Click here for previous part