Validating Dynamic Forms

Validating Dynamic Forms

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