Create Edit Forms with FluentValidation and Syncfusion Blazor Components
Tags: csharp, dotnet, blazor, webdev
Published: 2022-06-08 11:51:55

---
title: Create Edit Forms with FluentValidation and Syncfusion Blazor Components
published: true
date: 2022-06-08 11:27:04 UTC
tags: csharp, dotnet, blazor, webdev
canonical_url: https://www.syncfusion.com/blogs/post/create-edit-forms-with-fluentvalidation-and-syncfusion-blazor-components.aspx
cover_image: https://www.syncfusion.com/blogs/wp-content/uploads/2022/06/Create-Edit-Forms-with-FluentValidation-and-Syncfusion-Blazor-Components-672x372.png
---

Validation is very important for any application to get reliable data from the user on any data entry form. The Blazor framework has built-in form validation support which uses data annotations. It also supports the very popular third-party [FluentValidation library](https://docs.fluentvalidation.net/en/latest/ "FluentValidation documentation") to solve complex validation requirements.

The Syncfusion [Blazor component](https://www.syncfusion.com/blazor-components "Syncfusion Blazor UI components") library offers over 70 responsive and lightweight UI controls for building modern web apps.

In this blog post, we will see how to create an edit form to get employee details with Syncfusion Blazor UI components and apply complex validation using the **FluentValidation** library.

## Prerequisites

- [Visual Studio](https://visualstudio.microsoft.com/downloads/ "Visual Studio")
- [.NET Core 3.0 or above](https://dotnet.microsoft.com/en-us/download/dotnet/3.0 ".NET Core 3.0")
- [FluentValidation library](https://docs.fluentvalidation.net/en/latest/ "FluentValidation")

## Setup Blazor WebAssembly application

First, create a [Blazor WebAssembly app](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-visual-studio "Getting Started with Blazor WebAssembly App in Visual Studio documentation") and install the following required packages:

- **FluentValidation** : Install it using the Visual Studio package manager or use the following dotnet CLI command.  

```
[CLI]
dotnet add package FluentValidation
```
- **Syncfusion Blazor packages** : Install them using the Visual Studio package manager or use the following dotnet CLI command.  

```
[CLI]
dotnet add package Syncfusion.Blazor
```

Then, [register the Syncfusion Blazor Service](https://blazor.syncfusion.com/documentation/getting-started/blazor-webassembly-visual-studio#register-syncfusion-blazor-service "Register Syncfusion Blazor Service documentation") and reference the scripts and stylesheets.

## Creating a model

Let’s create a new model class file inside the **Model** folder with the name  **Employee**.

In this class file, add the class definitions for the  **Countries**  and  **Cities**  classes with the required properties and methods to generate the appropriate data for the dropdown list.

Refer to the following code example.

```csharp
namespace SyncFluentValidation.Model
{
    public class Employee
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime? DateOfBirth { get; set; }
        public string Gender { get; set; }
        public decimal? Experience { get; set; }
        public string EmailAddress { get; set; }
        public PermanentAddress PermanentAddress { get; } = new PermanentAddress();
    }
    public class PermanentAddress
    {
        public string AddressLine1 { get; set; }
        public string Country { get; set; }
        public string City { get; set; }
        public string Postcode { get; set; }
    }

    public class Countries
    {
        public string Name { get; set; }
        public string Code { get; set; }

        public List<Countries> GetCountries()
        {
            List<Countries> Country = new List<Countries>
            {
                new Countries() { Name = "Australia", Code = "AU" },
                new Countries() { Name = "United Kingdom", Code = "UK" },
                new Countries() { Name = "United States", Code = "US" },
            };
            return Country;
        }
    }
    public class Cities
    {
        public string Name { get; set; }
        public string Code { get; set; }
        public string CountryCode { get; set; }
        public List<Cities> GetCities()
        {
            List<Cities> CityName = new List<Cities>
            {
                new Cities() { Name = "New York", CountryCode = "US", Code="US-101" },
                new Cities() { Name = "Virginia", CountryCode = "US", Code="US-102" },
                new Cities() { Name = "Washington", CountryCode = "US", Code="US-103" },
                new Cities() { Name = "Victoria", CountryCode = "AU", Code="AU-101" },
                new Cities() { Name = "Tasmania", CountryCode = "AU", Code="AU-102" },
                new Cities() { Name = "Queensland", CountryCode = "AU", Code="AU-103" },
                new Cities() { Name = "London", CountryCode = "UK", Code="UK-101" },
                new Cities() { Name = "Manchester", CountryCode = "UK", Code="UK-102" },
                new Cities() { Name = "Ashford", CountryCode = "UK", Code="UK-103" }
            };
            return CityName;
        }
    }
}
```

## Binding the form validation component

Now, bind the FluentValidator component to replace the default data annotation validation in the Blazor framework. To do so, create the extension methods for **EditContext** to call the validation logic for **FluentValidation**.

Validate the edit form UI components using the EditContext events, namely **OnValidationRequested**  for full model validation on form submission and  **OnFieldChanged** for individual field validation on value changes.

Refer to the following code example.

```csharp
public class FluentValidator<TValidator> : ComponentBase where TValidator : IValidator, new()
{
   private readonly static char[] separators = new[] { '.', '[' };
   private TValidator validator;

   [CascadingParameter] private EditContext EditContext { get; set; }

   protected override void OnInitialized()
   {
       validator = new TValidator();
       var messages = new ValidationMessageStore(EditContext);

       // Revalidate when any field changes, or if the entire form requests validation
       // (e.g., on submit)

       EditContext.OnFieldChanged += (sender, eventArgs)
          => ValidateModel((EditContext)sender, messages);

       EditContext.OnValidationRequested += (sender, eventArgs)
          => ValidateModel((EditContext)sender, messages);
   }

   private void ValidateModel(EditContext editContext, ValidationMessageStore messages)
   {
       var context = new ValidationContext<object>(editContext.Model);
       var validationResult = validator.Validate(context);
       messages.Clear();
       foreach (var error in validationResult.Errors)
       {
           var fieldIdentifier = ToFieldIdentifier(editContext, error.PropertyName);
           messages.Add(fieldIdentifier, error.ErrorMessage);
       }
       editContext.NotifyValidationStateChanged();
   }

   private static FieldIdentifier ToFieldIdentifier(EditContext editContext, string propertyPath)
   {
       // This method parses property paths like 'SomeProp.MyCollection[123].ChildProp'
       // and returns a FieldIdentifier which is an (instance, propName) pair. For example,
       // it would return the pair (SomeProp.MyCollection[123], "ChildProp"). It traverses
       // as far into the propertyPath as it can go until it finds any null instance.

       var obj = editContext.Model;

       while (true)
       {
          var nextTokenEnd = propertyPath.IndexOfAny(separators);
          if (nextTokenEnd < 0)
          {
              return new FieldIdentifier(obj, propertyPath);
          }

          var nextToken = propertyPath.Substring(0, nextTokenEnd);
          propertyPath = propertyPath.Substring(nextTokenEnd + 1);

          object newObj;
          if (nextToken.EndsWith("]"))
          {
              // It's an indexer
              // This code assumes C# conventions (one indexer named Item with one param)
              nextToken = nextToken.Substring(0, nextToken.Length - 1);
              var prop = obj.GetType().GetProperty("Item");
              var indexerType = prop.GetIndexParameters()[0].ParameterType;
              var indexerValue = Convert.ChangeType(nextToken, indexerType);
              newObj = prop.GetValue(obj, new object[] { indexerValue });
          }
          else
          {
              // It's a regular property
              var prop = obj.GetType().GetProperty(nextToken);
              if (prop == null)
              {
                 throw new InvalidOperationException($"Could not find property named {nextToken} on object of type {obj.GetType().FullName}.");
              }
              newObj = prop.GetValue(obj);
          }

          if (newObj == null)
          {
              // This is as far as we can go
              return new FieldIdentifier(obj, nextToken);
          }

          obj = newObj;
        }
    }
 }
```

## Simple validation

FluentValidation works the same as data annotations in Blazor to validate each object. It supports built-in validator rules like NotEmpty, EmailAddress, NotNull, and [more](https://docs.fluentvalidation.net/en/latest/built-in-validators.html "Built-in Validators in FluentValidation").

In the validator, we will create the validation rules for each field of the model object like in the following code example.

```csharp
public class EmployeeValidator: AbstractValidator<Employee>
{
    public EmployeeValidator()
    {
       RuleFor(e => e.FirstName).NotEmpty();
       RuleFor(e => e.EmailAddress).EmailAddress().NotEmpty();
       RuleFor(e => e.DateOfBirth).NotEmpty();
    }
}
```

Now, create the EditForm with the [Syncfusion Blazor components](https://www.syncfusion.com/blazor-components "Syncfusion Blazor UI components") to design the UI for acquiring employee details like first name, last name, date of birth, and email address.

Refer to the following razor code.

```html
<EditForm Model="EmployeeDetails" OnValidSubmit="SubmitValidForm">
    <div class="form-row">
        <div class="form-group col-md-6">
            <SfTextBox FloatLabelType="FloatLabelType.Auto" Placeholder="First Name" @bind-Value="EmployeeDetails.FirstName"></SfTextBox>
        </div>
        <div class="form-group col-md-6">
            <SfTextBox FloatLabelType="FloatLabelType.Auto" Placeholder="Last Name" @bind-Value="EmployeeDetails.LastName"></SfTextBox>
        </div>
    </div>
    <div class="form-row">
        <div class="form-group col-md-6">
            <SfTextBox FloatLabelType="FloatLabelType.Auto" Placeholder="Email Address" @bind-Value="EmployeeDetails.EmailAddress"></SfTextBox>
        </div>
        <div class="form-group col-md-6">
            <SfDatePicker TValue="DateTime?" FloatLabelType="FloatLabelType.Auto" Placeholder="Date of Birth" @bind-Value="EmployeeDetails.DateOfBirth"> </SfDatePicker>
        </div>
    </div>
    <p>
        <SfButton>Submit</SfButton>
    </p>
</EditForm>
```

## Complex data validation

**FluentValidation** works well with complex data type validation (child object).

Refer to the following example. Here, the employee permanent address field has a complex validation type.

```csharp
public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime? DateOfBirth { get; set; }
    public string Gender { get; set; }
    public decimal? Experience { get; set; }
    public string EmailAddress { get; set; }
    public PermanentAddress PermanentAddress { get; } = new PermanentAddress();
}
public class PermanentAddress
{
    public string AddressLine1 { get; set; }
    public string Country { get; set; }
    public string City { get; set; }
    public string Postcode { get; set; }
}
```

Next, create the validation rule for the complex objects. **FluentValidation** has a  **SetValidator** API to reference the parent class to the child class.

```csharp
public class EmployeeValidator: AbstractValidator<Employee>
{
   public EmployeeValidator()
   {
      RuleFor(e => e.FirstName).NotEmpty();
      RuleFor(e => e.EmailAddress).EmailAddress().NotEmpty();
      RuleFor(e => e.DateOfBirth).NotEmpty();
      RuleFor(e => e.PermanentAddress).SetValidator(new AddressValidator());
   }
}
public class AddressValidator : AbstractValidator<PermanentAddress>
{
   public AddressValidator()
   {
      RuleFor(address => address.AddressLine1).NotEmpty();
      RuleFor(address => address.Country).NotEmpty();
      RuleFor(address => address.Postcode).NotEmpty();
   }
}
```

Now, map the complex object model to the **EditForm.** Refer to the following code example.

```html
<div class="form-row">
 <div class="form-group col-md-3">
    <SfTextBox Multiline="true" FloatLabelType="FloatLabelType.Auto" Placeholder="Address Line" @bind-Value="EmployeeDetails.PermanentAddress.AddressLine1">
    </SfTextBox>
    <SfComboBox TValue="string" TItem="Countries" FloatLabelType="FloatLabelType.Auto" Placeholder="Select a Country" @bind-Value="EmployeeDetails.PermanentAddress.Country" DataSource="@countries">
        <ComboBoxFieldSettings Text="Name" Value="Code"></ComboBoxFieldSettings>
        <ComboBoxEvents TValue="string" TItem="Countries" ValueChange="ChangeCountry"></ComboBoxEvents>
    </SfComboBox>
    <SfDropDownList Enabled="@enableCityDropDown" TValue="string" TItem="Cities" FloatLabelType="FloatLabelType.Auto" Placeholder="Select a City" @bind-Value="EmployeeDetails.PermanentAddress.City" Query="@CityQuery" DataSource="@cities">
        <DropDownListFieldSettings Text="Name" Value="Code"></DropDownListFieldSettings>
    </SfDropDownList>
    <SfTextBox FloatLabelType="FloatLabelType.Auto" Placeholder="Postal Code" @bind-Value="EmployeeDetails.PermanentAddress.Postcode"></SfTextBox>
 </div>
</div>
```

![Completed Form with Complex Validation](https://www.syncfusion.com/blogs/wp-content/uploads/2022/06/Applying-Complex-Validation-Using-FluentValidation-in-Blazor-App.png)
<figcaption>Completed Form with Complex Validation</figcaption>

## Conditional validation

Also, you can apply [conditional](https://docs.fluentvalidation.net/en/latest/conditions.html "Conditional Validators in FluentValidation") validation using the **FluentValidation** library. A rule may define multiple fields with **When** and **Unless** methods to specify the conditions that decide when the rule should be executed.

In the following code example, check the condition based on the country code. If the country field has the value **US** , then set the postal code minimum length as 5 and maximum length as 10. Otherwise, set the maximum length as 7 for all other countries.

```csharp
public AddressValidator()
{
    RuleFor(address => address.AddressLine1).NotEmpty();
    RuleFor(address => address.Country).NotEmpty();
    RuleFor(address => address.Postcode).NotEmpty();
    When(address => address.Country == "US", () => { RuleFor(address => address.Postcode).MinimumLength(5).MaximumLength(10); })
       .Otherwise(()=> {
           RuleFor(address => address.Postcode).MaximumLength(7);
       });
}
```

##  GitHub reference

Also, you can download the complete source code for this [Blazor FluentValidation example](https://github.com/SyncfusionExamples/blazor_fluentvalidation "Blazor FluentValidation demo on GitHub") on GitHub.

## Conclusion

Thanks for reading! In this blog, we have seen how to create an edit form with Syncfusion Blazor UI components and easily apply complex validation with the FluentValidation library. Try out the steps in this blog post and leave your feedback or questions in the comments section below!

Try our Blazor components by downloading a [free 30-day trial](https://www.syncfusion.com/account/manage-trials/downloads "Free evaluation of the Syncfusion Essential Studio") or downloading our [NuGet package](https://www.nuget.org/packages/Syncfusion.Blazor "Syncfusion.Blazor NuGet package"). Feel free to have a look at our [online examples](https://blazor.syncfusion.com/ "Syncfusion Blazor controls online demos") and [documentation](https://blazor.syncfusion.com/documentation/introduction/ "Syncfusion Blazor components documentation") to explore other available features.

You can also contact us through our [support forum](https://www.syncfusion.com/forums/blazor-components "Syncfusion support forum"), [support portal](https://support.syncfusion.com "Syncfusion support portal"), and [feedback portal](https://www.syncfusion.com/feedback "Syncfusion Feedback Portal"). We are always happy to assist you!