SkillAgentSearch skills...

Blazilla

A library for using FluentValidation with Blazor EditForm components. Provides seamless integration between FluentValidation and Blazor forms with support for real-time validation, nested objects, async validation, and rule sets.

Install / Use

/learn @loresoft/Blazilla
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Blazilla

A library for using FluentValidation with Blazor

Nuget version Build Status Coverage Status License

Overview

Blazilla provides seamless integration between FluentValidation and Blazor's EditForm component. This library enables you to use FluentValidation's powerful and flexible validation rules with Blazor forms, supporting both Blazor Server and Blazor WebAssembly applications.

Features

  • Real-time validation - Validate fields as users type or change values
  • Form-level validation - Full model validation on form submission
  • Nested object validation - Support for complex object hierarchies
  • Asynchronous validation - Built-in support for async validation rules
  • Rule sets - Execute specific groups of validation rules
  • Custom validator selectors - Fine-grained control over which rules to execute
  • Dependency injection integration - Automatic validator resolution from DI container
  • Performance optimized - Compiled expression trees for fast validation context creation

Installation

Install the package via NuGet:

dotnet add package Blazilla

Or via Package Manager Console:

Install-Package Blazilla

Quick Start

1. Create a Model

public class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public int Age { get; set; }
    public string? EmailAddress { get; set; }
}

2. Create a FluentValidation Validator

using FluentValidation;

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        RuleFor(p => p.FirstName)
            .NotEmpty().WithMessage("First name is required")
            .MaximumLength(50).WithMessage("First name cannot exceed 50 characters");

        RuleFor(p => p.LastName)
            .NotEmpty().WithMessage("Last name is required")
            .MaximumLength(50).WithMessage("Last name cannot exceed 50 characters");

        RuleFor(p => p.Age)
            .GreaterThanOrEqualTo(0).WithMessage("Age must be greater than or equal to 0")
            .LessThan(150).WithMessage("Age must be less than 150");

        RuleFor(p => p.EmailAddress)
            .NotEmpty().WithMessage("Email address is required")
            .EmailAddress().WithMessage("Please provide a valid email address");
    }
}

3. Register the Validator

Blazor Server (Program.cs)

using FluentValidation;

var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

// Register FluentValidation validators as singletons for better performance
builder.Services.AddSingleton<IValidator<Person>, PersonValidator>();

var app = builder.Build();
// ... rest of configuration

Blazor WebAssembly (Program.cs)

using FluentValidation;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

// Register FluentValidation validators as singletons for better performance
builder.Services.AddSingleton<IValidator<Person>, PersonValidator>();

await builder.Build().RunAsync();

4. Use in Blazor Component

@page "/person-form"

<h3>Person Form</h3>

<EditForm Model="@person" OnValidSubmit="@HandleValidSubmit">
    <FluentValidator />
    <ValidationSummary />

    <div class="mb-3">
        <label for="firstName" class="form-label">First Name</label>
        <InputText id="firstName" class="form-control" @bind-Value="person.FirstName" />
        <ValidationMessage For="@(() => person.FirstName)" />
    </div>

    <div class="mb-3">
        <label for="lastName" class="form-label">Last Name</label>
        <InputText id="lastName" class="form-control" @bind-Value="person.LastName" />
        <ValidationMessage For="@(() => person.LastName)" />
    </div>

    <div class="mb-3">
        <label for="age" class="form-label">Age</label>
        <InputNumber id="age" class="form-control" @bind-Value="person.Age" />
        <ValidationMessage For="@(() => person.Age)" />
    </div>

    <div class="mb-3">
        <label for="email" class="form-label">Email</label>
        <InputText id="email" class="form-control" @bind-Value="person.EmailAddress" />
        <ValidationMessage For="@(() => person.EmailAddress)" />
    </div>

    <button type="submit" class="btn btn-primary">Submit</button>
</EditForm>

@code {
    private Person person = new();

    private async Task HandleValidSubmit()
    {
        // Handle successful form submission
        Console.WriteLine("Form submitted successfully!");
    }
}

Advanced Usage

Asynchronous Validation

Blazor's built-in validation system doesn't natively support asynchronous validation. When using async validation rules with FluentValidation, you need to handle form submission manually to ensure async validation completes before the form is submitted.

Enable asynchronous validation mode and use OnSubmit instead of OnValidSubmit to properly handle async validation:

<EditForm Model="@person" OnSubmit="@HandleSubmit">
    <FluentValidator AsyncMode="true" />
    <ValidationSummary />
    
    <div class="mb-3">
        <label for="email" class="form-label">Email</label>
        <InputText id="email" class="form-control" @bind-Value="person.EmailAddress" />
        <ValidationMessage For="@(() => person.EmailAddress)" />
    </div>
    
    <button type="submit" class="btn btn-primary" disabled="@isSubmitting">
        @(isSubmitting ? "Validating..." : "Submit")
    </button>
</EditForm>

@code {
    private Person person = new();
    private bool isSubmitting = false;

    private async Task HandleSubmit(EditContext editContext)
    {
        isSubmitting = true;
        StateHasChanged();
        
        try
        {
            // Use ValidateAsync to ensure all async validation completes
            var isValid = await editContext.ValidateAsync();
            
            if (isValid)
            {
                // Form is valid, proceed with submission
                await ProcessValidForm();
            }
            // If invalid, validation messages will be displayed automatically
        }
        finally
        {
            isSubmitting = false;
            StateHasChanged();
        }
    }
    
    private async Task ProcessValidForm()
    {
        // Handle successful form submission
        Console.WriteLine("Form submitted successfully!");
        await Task.Delay(1000); // Simulate form processing
    }
}

Create a validator with async rules:

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        RuleFor(p => p.EmailAddress)
            .NotEmpty().WithMessage("Email is required")
            .EmailAddress().WithMessage("Please provide a valid email address")
            .MustAsync(async (email, cancellation) => 
            {
                // Simulate async validation (e.g., database check)
                await Task.Delay(500, cancellation);
                return !email?.Equals("admin@example.com", StringComparison.OrdinalIgnoreCase) ?? true;
            }).WithMessage("This email address is not available");
    }
}

Why This Approach is Necessary

Blazor's OnValidSubmit event fires immediately after synchronous validation passes, without waiting for async validation to complete. This can result in forms being submitted with incomplete validation results.

The EditContextExtensions.ValidateAsync() method:

  1. Triggers synchronous validation first (which may initiate async validation tasks)
  2. Waits for any pending async validation tasks to complete
  3. Returns true only when all validation (sync and async) has passed

This ensures that form submission is properly prevented when async validation rules fail.

Important: Always use OnSubmit instead of OnValidSubmit when working with async validation rules. The OnValidSubmit event doesn't wait for async validation to complete.

Rule Sets

Use rule sets to execute specific groups of validation rules:

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        // Default rules (always executed)
        RuleFor(p => p.FirstName).NotEmpty();

        // Rules in specific rule sets
        RuleSet("Create", () =>
        {
            RuleFor(p => p.EmailAddress)
                .NotEmpty()
                .EmailAddress();
        });

        RuleSet("Update", () =>
        {
            RuleFor(p => p.LastName).NotEmpty();
        });
    }
}
<!-- Execute only the "Create" rule set -->
<FluentValidator RuleSets="@(new[] { "Create" })" />

<!-- Execute all rules including those in rule sets -->
<FluentValidator AllRules="true" />

Nested Object Validation

Validate complex objects with nested properties:

public class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public Address? Address { get; set; }
}

public class Address
{
    public string? Street { get; set; }
    public string? City { get; set; }
    public string? PostalCode { get; set; }
}

public class AddressValidator : AbstractValidator<Address>
{
    public AddressValidator()
    {
        RuleFor(a => a.Street).N

Related Skills

View on GitHub
GitHub Stars91
CategoryCustomer
Updated16h ago
Forks9

Languages

C#

Security Score

100/100

Audited on Mar 31, 2026

No findings