Simple to use dynamic form-generator powered by Blazor

Alex Knijf
7 min readSep 20, 2020

--

Photo by Todd Quackenbush on Unsplash

After writing my Proof of Concept for dynamic form generation with Blazor a couple of months ago, I saw a lot of improvements that should be made. You could read the first article about dynamic form generation here, because it elaborates more on the initial idea.

Having said that, let’s summarize what I wanted to improve after building the POC.

  • Move to separate project
  • Pass-through of the EditForm events
  • Implement the styling for frameworks like Bootstrap or your own CSS styling
  • ValidationMessage to the element
  • Translatable ValidationMessage

The summary will be used as the outline for the article, I’ll start with creating and moving all the code to a library. I will skip this part because I’m assuming you know how to do that sweet ctrl + c, ctrl + v magic. After all.. you are probably a programmer. 😉😂

Moving

A quick note regarding the move: when I moved the Blazor components the VxFormGenerator project could not recognize some of them anymore. So I needed to clean the solution and create components in the VxFormGenerator project again and moved the code to that newly created files then deleting the old file🤔 Didn’t had the time to go deeper into this issue.

Simplify the usage

The POC had a FormGenerator component that wrapped an EditForm this was useful so the component could read the properties of a POCO and generate FormElement components.

POC Formgenerator

Having a wrapper around the EditForm turned out to be tedious because the FormGenerator was simply calling a RenderTree function and was constraining the developer. For example, if the developer didn't want to use a ValidationSummary it needed to be passed as parameter to the component. This would turn into a lot of options. So the dynamic form-generator needed a different way to be initiated. Also it made the assumption that the developer always had a model. Based on feedback by a visitor of the repository , this was not always the usage some people needed.

Hook into the EditForm

Taking inspiration from the DataAnnotationsValidator and the ValidationSummary having it hooked to the EditForm with one line. I found out that I was able to use the CascadedParameter approach for connecting to the EditContext of the EditForm.

This allows the component to get the EditContext.Model value. Having the model allows the component to render the class as a dynamic form easy-peasy. It's now a one-liner 🎉.

Read the Model and create form-elements

The component RenderFormElements is a class without a razor file, and is a layout component, that's a component that has the task to render a structure. It's without a razor file because the sole purpose is to render generic components dynamically. So a razor file appeared to me as overhead.

The implementation is as followed:

The CascadingParameter loads the EditForm, and the BuildRenderTree method renders the FormElementLoader based on the data-type of EditContext.Model. When the model is a Poco it creates all necessary methods for binding to that property. This binding methods are stored in a ValueReferenceand passed along to the FormElementLoader. The loader will then inject the binding methods and key name into the rendered FormElement. When a value changes it now updates the model.

The RenderFormElements component is also able to create a form based on a dynamic ExpandoObject this allows the developer to create a model at runtime. This behaviour is useful when, for example, creating a form based on the API JSON response containing a dynamic form structure stored in a database.

At the moment of writing rendering a form based on a ExpandoObject doesn't support validation. This is one of the improvements for the library. Please note that this approach needs more attention to be production ready. Will go deeper into this variant in a separate article.

Let’s take a look a the the code that renders the form:

VxFormElemenLoader is a component that renders the FormElement by looking up the type of the property in the FormGeneratorComponentsRepository and will use the mapped component for that type.

Using the FormElement and implement styling

When the VxFormElementLoader is loading the FormElementComponent it should have a registered FormElement component in the FormGeneratorComponentsRepository.

The FormElement component contains the label and field and allows the developer to implement styling like Bootstrap. We'll start with a plain HTML version.

FormElement Razor
Plain HTML form

The output would look like above, a basic HTML form dynamically generated. This lacks most of the styling. Now let’s create a Bootstrap FormElement. This is where BootstrapFormElementComponent comes in

You’ve created a Bootstrap version of the FormElement the last thing we need is to update the FormGeneratorComponentsRepository to use the BootstrapFormElement as a container to load the inputs.

Because the FormElement component allows for default CSS classes by setting the DefaultFieldClasses property it inherited from FormElementBase, the form is almost completely styled like a Bootstrap form. Some components may need a new component because the built-in Blazor components not always meet the structure that is needed to style a input based on the CSS framework. As you could see in the image below, we need to create a checkbox component because the DefaulFieldClasses isn't applying the Bootstrap style.

Partial Bootstrap styled form

Let’s create a checkbox component with a label.

VxInputCheckbox Razor

After creating a checkbox with a label we need to extend this with the Bootstrap styling.

Bootstrap styled checkbox

The Bootstrap checkbox styling is now applied. 🎉 One missing feature of the checkbox is is multiple selection. We need to create a component that renders multiple check-boxes when we have a multiple-choice property.

It will now render multiple Checkboxes and will set the selected values in a ValueReference object. The ValueReference object is created for generic handling for multiple selection components. This allows for enums and "unknown value at compile time" to be handled the same way without forcing the unknown values to use int values are there keys. The last step to take is make the InputCheckboxMultipleWithChildren switch from child component without a lot of custom code. For example, rendering a Bootstrap version of the child component without changing the logic of the container component. The approach I took was to created an interface called IRenderChildrenSwapable. This interface extends the regularIRenderChildren with a overload of the Renderchildren method allowing a component to specify a child component that it should render. This results in a minimum amount of code to implement a new framework.

Adding these components would show a fully styled Bootstrap form!

Fully styled Bootstrap form

Add ValidationMessage to the element

The last part of this article entails the ValidationMessage. The built-in version of the component adds a hard-coded class. So I've took the approach described in this blog post. He explains further the inner workings of the component, if you're interested. So let's create our own ValidationMessage .

So now we can use it in our FormElement. We can use the ValueExpression to connect the property that should be monitored.

When we set a MinLength attribute on the Note property in the model, the validation message is shown with Bootstrap styling when entering a invalid value. This approach allows the developer to implement multiple styling frameworks with reusable form feedback logic.

Bootstrap feedback message built-in Blazor InputText

At the moment of writing the EditForm component sets an valid, modified and invalid class when a input has changed and validated. This class in hard-coded so until https://github.com/dotnet/aspnetcore/pull/24835 is accepted, merged and published. We need a workaround like the one below.

It’s a wrap (for now)

While updating the form generator with the new insights and improvements, showing it to colleagues and receiving feedback I came to the conclusion that this POC is eligible to be a library. So I’ve published the first workable version on Nuget so you could play with it. BE AWARE it’s BETA and I have a lot of wishes that I would like to incorporate.

The top list:

  • Better Documentation for the library
  • Translatable validation messages
  • All HTML5 built-in inputs as component
  • Extending support for ExpandoObject with validation (will be a new article )

Any feedback is appreciated. If you want to contribute, you can find the GitHub here.

End result

--

--

Alex Knijf

Senior Front-end Developer working with TypeScript, JavaScript, .NET (Core) WebApi and DB stuff on Digital Tables, Mobile and Desktop living in the Netherlands