Most Blazor developers have learned how to validate a form. That code looks something like this:
<EditForm Model="@MyModel"> <DataAnnotationsValidator /> <!-- enter the rest of the form here --> <button type="submit">Submit</button> </EditForm>
But how do you validate a form when you’re dynamically generating it? Let’s go look at a few different ways to handle that.
First, we’ll let the form generator inside the CG.Blazor.Forms library handle everything for us:
<DynamicForm Model="@Model" /> @code { MyVm Model {get;set;} = new MyVm(); [RenderDataAnnotationsValidator] class MyVm { [RenderInputText] [Required] public string Name {get;set;} [RenderInputNumber] public int Age {get;set;} } }
This directs the form generator to create a form with a DataAnnotationValidator
and two fields, containing the name and age, bound to the model. Validation happens just like it normally would, and we don’t have to really do much of anything else.
But wait, what if we wanted a validation summary on the form? Let’s go look at that:
<DynamicForm Model="@Model" /> @code { MyVm Model {get;set;} = new MyVm(); [RenderDataAnnotationsValidator] [RenderValidationSummary] class MyVm { [RenderInputText] [Required] public string Name {get;set;} [RenderInputNumber] public int Age {get;set;} } }
Notice, the only thing we added, from the earlier example, was the RenderValidationSummary
attribute, on the MyVM
class. That’s really all we need to do. In turn, the form generator will create a form with a DataAnnotationValidator
, two fields, and a validation summary at the bottom of the form.
But wait! What if we had a complex model, or for some other reason, we couldn’t use the DataAnnotationsValidator
object? How might we handle that?
Well, if we wanted to use a fluent validator, there is an integration for that in the CG.Blazor.Forms._FluentValidations library. Let’s look at that:
<DynamicForm Model="@Model" /> @code { MyVm Model {get;set;} = new MyVm(); [RenderFluentValidationValidator] [RenderValidationSummary] class MyVm { [RenderInputText] public string Name {get;set;} [RenderInputNumber] public int Age {get;set;} } class MyVMValidator : AbstractValidator<ValidatorVM> { MyVMValidator() { RuleFor(x => x.A1) .NotEmpty() .WithMessage("Eek! you missed the name!"); } } }
Notice that we swapped out the RenderDataAnnotationsValidator
attribute, on the model, with a new RenderFluentValidationValidator
attribute. That directs the form generator to embed an FluentValidationValidator
inside the generated edit form.
Fluent validators are beyond the scope of this article, but, note that we also added the MyVMValidator
class, which handles actually validating the form, for us, through the services of the fluent validator library.
But wait!! What if we needed a completely custom validation solution, something not yet integrated into the form generation library?
Well, that’s a little more involved, but still easily accomplished. Let’s start by creating a custom attribute class, to render our custom validator:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class RenderMyValidator : FormValidationAttribute { public override int Generate( RenderTreeBuilder builder, int index, IHandleEvent eventTarget, Stack<object> path, PropertyInfo prop, ILogger<IFormGenerator> logger ) { builder.RenderUIComponent<MyValidator>(index++); return index; } }
We derive from FormValidationAttribute
, so the form generator will know that we are all about validating forms. We override the Generate
method, since that’s what the form generator calls in order to render HTML dynamically. In our case, we want to render our hypothetical MyValidator
class, so we call the RenderUIComponent
extension method to do that for us.
Using this approach, we could then do this in our project:
<DynamicForm Model="@Model" /> @code { MyVm Model {get;set;} = new MyVm(); [RenderMyValidator] [RenderValidationSummary] class MyVm { [RenderInputText] public string Name {get;set;} [RenderInputNumber] public int Age {get;set;} } }
… And the form generator would generate our form, with our MyValidator
component embedded into the resulting edit form.
The other way we could do this, is to manually render that part of the form, ourselves. Something like this:
<DynamicForm Model="@Model"> <MyValidator /> </DynamicForm> @code { MyVm Model {get;set;} = new MyVm(); [RenderValidationSummary] class MyVm { [RenderInputText] public string Name {get;set;} [RenderInputNumber] public int Age {get;set;} } }
Notice that we included our content – the MyValidator
tag, inside the DynamicForm
tag, as child content. That causes the form generator to dynamically render the form, along with the specified child content, at runtime. In our example, that child content is a custom validator component, but, it could just as easily by any valid HTML we wanted to include in our generated form.
There you have it, loads of different ways to validate a dynamically generated form. Thanks for reading!
Photo by Sangga Rima Roman Selia on Unsplash