Live Chat Icon For mobile
Live Chat Icon
Popular Categories.NET  (151).NET Core  (25)Angular  (49)ASP.NET  (48)ASP.NET Core  (64)ASP.NET MVC  (86)Azure  (28)Blazor  (97)DocIO  (20)Essential JS 2  (71)Essential Studio  (163)File Formats  (20)Flutter  (111)JavaScript  (148)Microsoft  (101)PDF  (59)React  (33)Succinctly series  (122)Syncfusion  (480)TypeScript  (30)Uno Platform  (3)UWP  (4)Vue  (26)Webinar  (25)Windows Forms  (55)WinUI  (21)WPF  (122)Xamarin  (130)XlsIO  (24)Other CategoriesBarcode  (4)BI  (29)Bold BI  (3)Build conference  (6)Business intelligence  (53)Button  (4)C#  (107)Chart  (51)Cloud  (9)Company  (445)Dashboard  (6)Data Science  (3)Data Validation  (5)DataGrid  (47)Development  (257)Doc  (7)DockingManager  (1)eBook  (93)Enterprise  (22)Entity Framework  (5)Essential Tools  (14)Excel  (20)Extensions  (13)File Manager  (4)Gantt  (9)Gauge  (7)Git  (4)Grid  (27)HTML  (9)Installer  (2)Knockout  (2)LINQPad  (1)Linux  (2)M-Commerce  (1)Metro Studio  (11)Mobile  (227)Mobile MVC  (9)OLAP server  (1)Open source  (1)Orubase  (12)Partners  (21)PDF viewer  (23)Performance  (6)PHP  (1)PivotGrid  (4)Predictive Analytics  (6)Report Server  (3)Reporting  (10)Reporting / Back Office  (11)Rich Text Editor  (6)Road Map  (9)Scheduler  (26)SfDataGrid  (9)Silverlight  (21)Sneak Peek  (19)Solution Services  (2)Spreadsheet  (9)SQL  (7)Stock Chart  (1)Surface  (4)Tablets  (5)Theme  (11)Tips and Tricks  (60)UI  (131)Uncategorized  (68)Unix  (2)User interface  (68)Visual State Manager  (1)Visual Studio  (19)Visual Studio Code  (11)Web  (215)What's new  (140)Windows 8  (19)Windows App  (1)Windows Phone  (15)Windows Phone 7  (9)WinRT  (26)
Share on twitter
Share on facebook
Share on linkedin
How to Create a Dynamic Form Builder in Blazor

How to Create a Dynamic Form Builder in Blazor

In this blog post, you will learn the procedure to create a dynamic form builder in Blazor with the EditForm class and data annotation validation. You will also learn the steps to generate form components based on data type and display validation messages using data annotation. Every step is explained with a working sample and simple code examples.

Dynamic form builder

Form input and its validation are very important for any application. In this blog, we will display employee details through a form design. This application will have a minimum of 10 user input entries in a form. We are going to create a form using data model classes and data annotation.

We will create components dynamically and render them in the edit form:

  • For string data type: TextBox component.
  • For string with multiline data type: TextBox with multiline (text area) component.
  • For number data type (integer, decimal, etc.): NumericTextBox component.
  • For DateTime data type: DatePicker component.

Let’s see the steps to create a dynamic form builder based on the data model class and implement data validation using Syncfusion Blazor form components.

Prerequisites

Steps to generate a dynamic form builder

1. Create a Blazor server application

  1. Create a Blazor server app.
  2. Create a new model class file inside the Data folder with the name EmployeeDetails.
  3. In this class file, add the class definitions for the Countries and Cities classes with the required properties and methods to generate appropriate data for the Dropdown List.

Note: You can refer to this GitHub repository for the EmployeeDetails class.

2. Add data annotation’s DataType attribute in the form model

The dynamic form component will be rendered based on the data annotation’s DataType attribute in the model properties. The EmployeeDetails model has various properties and comes with built-in validation data annotation attributes like Required, EmailAddress, Phone, and Range.

Additionally, we should add DataType attributes for details like Text, MultilineText, Date, PhoneNumber, and custom types.

Refer to the following code example.

public class EmployeeDetails
    {
        [Required]
        [Display(Name ="First Name")]
        [DataType(DataType.Text)]
        public string FirstName { get; set; }
        [Display(Name = "Last Name")]
        [DataType(DataType.Text)]
        public string LastName { get; set; }
        [Required]
        [Display(Name = "Email Address")]
        [DataType(DataType.EmailAddress)]
        [EmailAddress]
        public string Email { get; set; }
        [Required]
        [Display(Name = "PhoneNumber")]
        [DataType(DataType.PhoneNumber)]
        [Phone]
        public string PhoneNumber { get; set; }
        [Required]
        [Display(Name = "Date of Birth")]
        [DataType(DataType.Date)]
        public DateTime? DOB { get; set; }
        [Required]
        [DataType(DataType.Duration)]
        [Display(Name = "Total Experience")]
        [Range(0, 20, ErrorMessage = "The Experience range should be 0 to 20")]
        public decimal? TotalExperience { get; set; }
        [Required]
        [Display(Name = "Select a Country")]
        [DataType("DropdownList")]
        public string Country { get; set; }
        [Required]
        [Display(Name = "Select a City")]
        [DataType("ComboBox")]
        public string City { get; set; }
        [Required]
        [DataType(DataType.MultilineText)]
        [Display(Name = "Address")]
        public string Address { get; set; }
    }

We have added custom DataType attributes for the DropdownList and ComboBox, since we don’t have built-in data types for them. Based on the custom values, the components will be rendered.

3. Generate dynamic form components

After creating the required model classes, access them inside the index.razor component in the Pages folder. In that folder, we are going to design the dynamic EditForm with Syncfusion form components.

  1. Add the following code to include the EditForm component. It creates an EditContext that tracks the fields that are modified in the dynamic form components and tracks the validation messages.
    <EditForm OnValidSubmit="@Submit">
    </EditForm>
  2. Then, create an instance of the EmployeeDetails class for binding it to the model of the EditForm, like in the following code example.
    <EditForm Model="@employeeDetails" OnValidSubmit="@Submit">
    </EditForm>
    
    @code {
    
        EmployeeDetails employeeDetails;
        protected override void OnInitialized()
        {
            employeeDetails = new EmployeeDetails();
        }
    }
  3. Then, add the DataAnnotationsValidator component in the EditForm to validate the input fields.
    <EditForm Model="@employeeDetails" OnValidSubmit="@Submit">
        <DataAnnotationsValidator />
        <ValidationSummary></ValidationSummary>
    </EditForm>
  4. Now, render the form components based on the data type using the CreateComponent method call, as shown in the following code example.
    <EditForm Model="@employeeDetails" OnValidSubmit="@Submit">
        <DataAnnotationsValidator />
        @CreateComponent()
        <ValidationSummary></ValidationSummary>
        <div class="form-group">
            <button type="submit" class="btn btn-primary">Save</button>
        </div>
    </EditForm>
  5. Then, render the Syncfusion form components based on the data type and bind the default value, placeholder, float label, and other properties.Refer to the following code example.
    public RenderFragment CreateComponent() => builder =>
        {
            var proList = typeof(EmployeeDetails).GetProperties();
            foreach (var prp in proList)
            {
                Type T = prp.GetType();
                if (prp.GetCustomAttributes(typeof(DataTypeAttribute), false).Length != 0)
                {
                    var attrList = (DataTypeAttribute)prp.GetCustomAttributes(typeof(DataTypeAttribute), false).First();
                    var displayLabel = (DisplayAttribute)prp.GetCustomAttributes(typeof(DisplayAttribute), false).First();
                // Get the initial property value.
                var propInfoValue = typeof(EmployeeDetails).GetProperty(prp.Name);
                // Create an expression to set the ValueExpression-attribute.
                var constant = System.Linq.Expressions.Expression.Constant(employeeDetails, typeof(EmployeeDetails));
                    var exp = System.Linq.Expressions.MemberExpression.Property(constant, prp.Name);
                    switch (attrList.DataType)
                    {
                        case DataType.Text:
                        case DataType.EmailAddress:
                        case DataType.PhoneNumber:
                        case DataType.MultilineText:
                            {
                                builder.OpenComponent(0, typeof(SfTextBox));
                            // Create the handler for ValueChanged.
                            builder.AddAttribute(3, "ValueChanged", RuntimeHelpers.TypeCheck<Microsoft.AspNetCore.Components.EventCallback<System.String>>(Microsoft.AspNetCore.Components.EventCallback.Factory.Create<System.String>(this, Microsoft.AspNetCore.Components.EventCallback.Factory.CreateInferred(this, __value => propInfoValue.SetValue(DataContext, __value), (string)propInfoValue.GetValue(DataContext)))));
                                builder.AddAttribute(4, "ValueExpression", System.Linq.Expressions.Expression.Lambda<Func<string>>(exp));
                                if (attrList.DataType == DataType.MultilineText)
                                    builder.AddAttribute(5, "Multiline", true);
                                break;
                            }
                        case DataType.Date:
                            builder.OpenComponent(0, typeof(SfDatePicker<DateTime?>));
                            builder.AddAttribute(3, "ValueChanged", RuntimeHelpers.TypeCheck<Microsoft.AspNetCore.Components.EventCallback<DateTime?>>(Microsoft.AspNetCore.Components.EventCallback.Factory.Create<DateTime?>(this, Microsoft.AspNetCore.Components.EventCallback.Factory.CreateInferred(this, __value => propInfoValue.SetValue(DataContext, __value), (DateTime?)propInfoValue.GetValue(DataContext)))));
                            builder.AddAttribute(4, "ValueExpression", System.Linq.Expressions.Expression.Lambda<Func<DateTime?>>(exp));
                            break;
                        case DataType.Duration:
                            builder.OpenComponent(0, typeof(SfNumericTextBox<decimal?>));
                            builder.AddAttribute(3, "ValueChanged", RuntimeHelpers.TypeCheck<Microsoft.AspNetCore.Components.EventCallback<decimal?>>(Microsoft.AspNetCore.Components.EventCallback.Factory.Create<decimal?>(this, Microsoft.AspNetCore.Components.EventCallback.Factory.CreateInferred(this, __value => propInfoValue.SetValue(DataContext, __value), (decimal?)propInfoValue.GetValue(DataContext)))));
                            builder.AddAttribute(4, "ValueExpression", System.Linq.Expressions.Expression.Lambda<Func<decimal?>>(exp));
                            break;
                        case DataType.Custom:
                            if (attrList.CustomDataType == "DropdownList")
                            {
                                builder.OpenComponent(0, typeof(Syncfusion.Blazor.DropDowns.SfDropDownList<string, Countries>));
                                builder.AddAttribute(1, "DataSource", countries.GetCountries());
                                builder.AddAttribute(4, "ChildContent", (Microsoft.AspNetCore.Components.RenderFragment)((builder2) =>
                                {
                                    builder2.AddMarkupContent(5, "\r\n    ");
                                    builder2.OpenComponent<Syncfusion.Blazor.DropDowns.DropDownListFieldSettings>
                                  (6);
    
                                    builder2.AddAttribute(7, "Value", "Code");
                                    builder2.AddAttribute(8, "Text", "Name");
                                    builder2.CloseComponent();
                                    builder2.AddMarkupContent(9, "\r\n");
                                }));
    
                            }
                            else if (attrList.CustomDataType == "ComboBox")
                            {
                                builder.OpenComponent(0, typeof(Syncfusion.Blazor.DropDowns.SfComboBox<string, Cities>));
                                builder.AddAttribute(1, "DataSource", cities.GetCities());
                                builder.AddAttribute(4, "ChildContent", (Microsoft.AspNetCore.Components.RenderFragment)((builder2) =>
                                {
                                    builder2.AddMarkupContent(5, "\r\n    ");
                                    builder2.OpenComponent<Syncfusion.Blazor.DropDowns.ComboBoxFieldSettings>
                                  (6);
    
                                    builder2.AddAttribute(7, "Value", "Code");
                                    builder2.AddAttribute(8, "Text", "Name");
                                    builder2.CloseComponent();
                                    builder2.AddMarkupContent(9, "\r\n");
                                }));
                            }
                            builder.AddAttribute(3, "ValueChanged", RuntimeHelpers.TypeCheck<Microsoft.AspNetCore.Components.EventCallback<System.String>>(Microsoft.AspNetCore.Components.EventCallback.Factory.Create<System.String>(this, Microsoft.AspNetCore.Components.EventCallback.Factory.CreateInferred(this, __value => propInfoValue.SetValue(DataContext, __value), (string)propInfoValue.GetValue(DataContext)))));
                            builder.AddAttribute(4, "ValueExpression", System.Linq.Expressions.Expression.Lambda<Func<string>>(exp));
                            break;
                        default:
                            break;
                    }
                    var defaultValue = propInfoValue.GetValue(employeeDetails);
                    builder.AddAttribute(1, "Value", defaultValue);
                    builder.AddAttribute(6, "PlaceHolder", displayLabel.Name);
                    builder.AddAttribute(6, "FloatLabelType", FloatLabelType.Auto);
    
                    builder.CloseComponent();
                }
            }
        };

Now, we have rendered the form components dynamically, and the inputs will be validated using data annotation, listing all the form validations when the Submit button is clicked.

Look at the following screenshot to see how the form is rendered and validation messages are displayed in the employee details form.
Output

GitHub sample

You can download the complete source code of this example from this GitHub repository.

Conclusion

Thanks for reading! I hope you now understand how easy is it to build a dynamic form using Syncfusion Blazor components with data annotation validation. This will definitely reduce your code size and also enhance code re-usability.

Syncfusion offers the largest selection of components for the Blazor platform. It has popular components like Charts, DataGrid, Scheduler, Diagram, Document Editor, and Maps. It also provides unique file-format libraries for manipulating Excel, Word, PDF, and PowerPoint files.

Try our Blazor components by downloading a free 30-day trial or check out our NuGet package. Feel free to have a look at our online examples and documentation to explore other available features.

If you have any questions, please let us know in the comments section below. You can also contact us through our feedback portal, support forum or Direct-Trac. We are always happy to assist you!

Tags:

Share this post:

Share on twitter
Share on facebook
Share on linkedin

Comments (7)

It’s works with blazor webassembly project?

Hi Andre,

Yes, it will work webassembly project also. Pleae find the below sample link.

https://www.syncfusion.com/downloads/support/directtrac/general/ze/WASM_BlazorApp_Form-1417268007.zip

Kindly check the above sample and get back to us if you need any further assistance on this.

Regards,
Saravanan G

Hi Saravanan,
Thanks for your answer.

Hi Saravanan,
Very nice demo, it is giving my some ideas 🙂

The code is really only partially dynamic, if you are hard coding the combobox type to Cities and DropDownlist to Countries. It will only work for the form in the demo. I would be interested in making your CreateComponent() generic where any class type could be passed in to produce a form. I am wondering how that would even work when controls need to be bound to lists of data.

Demos that make me think are great!
-Ken

Hi Ken,

Thank you for your appreciation :-),

Yes, we have provided the ExpandoObject type support in our components, so we can bind the dynamic class list data to Dropdown List and ComboBox components like below and also I have modified the sample and attached the below link.

builder.OpenComponent(0, typeof(Syncfusion.Blazor.DropDowns.SfDropDownList));

Sample link – https://www.syncfusion.com/downloads/support/directtrac/general/ze/FormBuilder-514046883.zip

Kindly check the above sample and get back to us if you need any further assistance on this.

Regards,
Saravanan G

Hi Saravanan,
very great example! I changed the property Country to

public int CountryId { get; set; }

public class Countries
{
public string Name { get; set; }
public int Id { get; set; }

public List GetCountries()
{
List Country = new List
{
new Countries() { Name = “Australia”, Id = 1 },
new Countries() { Name = “United Kingdom”, Id = 2 },
new Countries() { Name = “United States”, Id = 3 },
};
return Country;
}
}

and of course I get an error: Unable to cast object of type ‘System.Int32’ to type ‘System.String’.
builder.AddAttribute(3, “ValueChanged”, RuntimeHelpers.TypeCheck<Microsoft.AspNetCore.Components.EventCallback>(Microsoft.AspNetCore.Components.EventCallback.Factory.Create(this, Microsoft.AspNetCore.Components.EventCallback.Factory.CreateInferred(this, __value => propInfoValue.SetValue(employeeDetails, __value), (string)propInfoValue.GetValue(employeeDetails)))));

I’ve tried a lot. Unfortunately without any result. Where does the correct change need to be made?

Regards,
Elko

Hello Elko,

Thank you for your appreciation,

Yes, you can bind the int data type to the Dropdown List component. For this, you need to change the type in Dropdown List creation and ValueChanged event call back like the below code in the CreateComponent method.

builder.OpenComponent(0, typeof(Syncfusion.Blazor.DropDowns.SfDropDownList));
……
……
if (prp.PropertyType == typeof(int?))
{
builder.AddAttribute(3, “ValueChanged”, RuntimeHelpers.TypeCheck<Microsoft.AspNetCore.Components.EventCallback>(Microsoft.AspNetCore.Components.EventCallback.Factory.Create(this, Microsoft.AspNetCore.Components.EventCallback.Factory.CreateInferred(this, __value => propInfoValue.SetValue(employeeDetails, __value), (int?)propInfoValue.GetValue(employeeDetails)))));
builder.AddAttribute(4, “ValueExpression”, System.Linq.Expressions.Expression.Lambda<Func>(exp));
}

I have modified the sample and attached the below link.

https://www.syncfusion.com/downloads/support/directtrac/general/ze/blazor-dynamic-form1750749387.zip

Check the above sample and get back to us if you need any further assistance on this.
Regards,
Saravanan G

Comments are closed.

Popular Now

Be the first to get updates

Subscribe RSS feed
Scroll To Top