Build a dynamic form generator with Blazor
My journey into Blazor started with the birth of my lovely daughter on 20–02–2020. The little dino-baby (one of her nicknames 😉) was eating, sleeping and being cute. As a new parent a lot of stuff is coming your way. You need to keep up with many things, especially the last time the baby had their milk. Looking at the apps that help you track these, any many other events, I had found that a lot require me to create an account and were sharing my data. Because don’t want to store that data somewhere other people could use it, I thought: let’s build my own.
This article is the first part of a series. You could read it to get the idea behind the Form generator. The second part of the series is a improvement of this Form generator so i would recommend read this and then go here.
Babysteps
Tracking your baby requires data, that data needs to be collected, and how do you collect that data?
So we need forms. Let’s create a feeding session form in Blazor.
We’ll start with a model that represents a feeding session based on time.
Now let’s create a razor page that allows to fill these properties with values.
Working example found on BlazorFiddle here
A little growth jump
Having this form functional I wasn’t happy with all the necessary manual bootstrapping. The app will have a least 10 models that needed to be a form. The idea of creating and maintaining them wasn’t something I was looking forward to.
Fortunately In Angular I’ve created multiple form generators so it was time to explore the dynamic way. The idea is to generate forms based on POCO’s and data-annotations. This should result in dynamic forms. Having the experience with the built-in EditForm I wanted to leverage as much of the out-of-box behaviour, because there is no need to reinvent the wheel.
Requirements
- Must work with EditForm
- So we can use the built-in validation and form handling logic
- Must be based on InputBase
- So the EditForm is able to use it’s built-in validation and change handling logic
- CustomFormElements Must be bind-able
- The form fields that are custom implemented like a datepicker should be able to use the
bind-Value
syntax - CustomFormElements Must have input validation
- The input validation should be extendable from the InputBase
- Must be generated by a POCO
- The form must use reflection to list all properties and it’s decorators to render them as form fields
- Should be extendable with custom components
- The form should have a repository that contains a mapping to a component that should be used as form element in the form when a property is of a specific Type
- Should be easy to change CSS framework
- The form should be able to switch, for example, to: Bootstrap or Material Design
Implementation of Dynamic Forms
The intend is that I lay down the basic groundwork for a fully featured dynamic form generator. So don’t expect a fully grown baby yet 😉.
Now that is out of the way let’s start by creating a new folder and component.
- Navigate to project root of you Blazor application
- Create the folders
Components\FormGenerator
- Create a new
FormGenerator.razor
file
4. Create a new FormGenerator.razor.cs
file
(Make sure it’s in the same place as the .razor
file)
5. We will need to have a loader component, so let create a new file FormElement.razor
6. Let’s create the backend file FormGenerator.razor.cs
(Make sure it’s in the same place as the .razor
file)
7. Now let’s create a component mapping, we use the Type of the property to show a corresponding HTML Element represented by a component.
8. Create in the FormGenerator
folder a new file FormGeneratorComponentsRepository.cs
Let’s setup the mapping for the dynamic form, open Startup.cs
and register the component mapping repo as a singleton to the Dependency Injection system.
This will, for example, render properties of type: String as a Blazor InputText component
Let’s use it
Running this application will result in a un-styled rendered dynamic form generated by a POCO!!! Unfortunately the the InputSelect
is not rendering the KindOfFood
enum as the possible options for the select
element.
This is because InputSelect
expects <option>
elements as it's child. So we need to append children dynamically to the InputSelect
component.
Append the children
There are multiple ways to create an architecture for this. I’ve chosen the following architecture that results in a simple and scoped solution:
- Leaving the
FormGeneratorComponentsRepository
unchanged - Adding a interface called
IRenderChildren
- Extend the
FormElement
to check for theIRenderChildren
interface - Extend the
FormElement
to execute therenderChildren
methods with the correct parameters
Let’s add the support for rendering children:
- Create a new file in
FormGenerator
folder called:IRenderChildren.cs
2. Create a new file in FormGenerator
folder called: InputSelectOption.razor
. This will be the option
HTML element.
3. Now let’s use this component to render it in the InputSelect
component
4. Create a new file in FormGenerator
folder called: RenderChildrenInputSelect.cs
The last thing is to set it up in the dynamic Form component mapping in the startup.cs
The mapping is done as a string because it could be used in a different way than Type
to Type
mapping.
Run the application and you should now have a generated form based on the POCO with working selection element! 🎉✨
working example is found here: https://github.com/Aaltuj/BlazorFormGeneratorDemo
Now we need to add styling…. unfortunately this article is getting too long if we handle that part as well. This is just a proof of concept, I would like to make some improvements to make it more production ready:
- Move to separate project
- Implement the styling for frameworks like Bootstrap or your own CSS styling
ValidationMessage
in the element- Translatable
ValidationMessage
- Pass-through of the
EditForm
events
This article is the first part of a series. Thank you for reading till here, the second part of the series is a improvement of this Form generator so i would recommend to go here. Any feedback is welcome