Skip to content

ryanelian/FluentValidation.Blazor

Repository files navigation

FluentValidation.Blazor

Fluent Validation-powered Blazor component for validating standard <EditForm> πŸŒ€ βœ…

GitHub Actions NuGet

Install

dotnet add package FluentValidation
dotnet add package Accelist.FluentValidation.Blazor

Getting Started

This library is a direct replacement to the default Blazor <DataAnnotationValidator> with zero configuration required ⚑ in the application code base:

<EditForm Model="Form">


    <FluentValidator></FluentValidator>


    <div class="form-group">
        <label for="email">Email</label>
        <InputText id="email" type="email" class="form-control" @bind-Value="Form.Email"></InputText>
        <ValidationMessage For="() => Form.Email"></ValidationMessage>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">
            <i class="fas fa-chevron-up"></i>
            Submit
        </button>
    </div>
</EditForm>
@code {
    FormModel Form = new FormModel();
}
public class FormModel
{
    public string Email { set; get; }
}

public class FormModelValidator : AbstractValidator<FormModel>
{
    public FormModelValidator()
    {
        RuleFor(Q => Q.Email).NotEmpty().EmailAddress().MaximumLength(255);
    }
}

The <FluentValidator> component automatically detects the Model data type used by the parent <EditForm> then attempts to acquire the corresponding FluentValidation.IValidator<T> for that model data type.

For this reason, in addition to coding the usual FluentValidation.AbstractValidator<T> Fluent Validation implementation, you are required to register the FluentValidation.IValidator<T> implementation in the Startup.cs Service Provider (Dependency Injection):

services.AddTransient<IValidator<CreateAccountFormModel>, CreateAccountFormModelValidator>();
// Alternatively, use FluentValidation.DependencyInjectionExtensions package (read further down below...)

This effectively allows you, dear programmer, to inject required services to your validation implementations for writing amazing custom validation methods! πŸ”₯

public class FormModelValidator : AbstractValidator<FormModel>
{
    readonly AppDbContext DB;
    readonly IServiceProvider SP;

    public FormModelValidator(AppDbContext db, IServiceProvider sp)
    {
        this.DB = db;
        this.SP = sp;

        RuleFor(Q => Q.Email).NotEmpty().EmailAddress().MaximumLength(255)
            .Must(BeUniqueEmail).WithMessage("Email address is already registered.");
    }

    bool BeUniqueEmail(string email)
    {
        var exist = DB.Account.Where(Q => Q.Email == email).Any();
        return (exist == false);
    }
}

Inlined Validator

Validator parameter may also be passed directly to the component to inline the AbstractValidator implementation instead of relying on .NET Core DI:

<FluentValidator Validator="Validator"></FluentValidator>
@code {
    FormModelValidator Validator = new FormModelValidator();
}

FluentValidation.DependencyInjectionExtensions

dotnet add package FluentValidation.DependencyInjectionExtensions
services.AddValidatorsFromAssemblyContaining<Program>();

Can be used to auto-populate all validators from current application / other project automatically!

Nested Objects & Arrays Validation

FluentValidation offers SetValidator method for validating nested objects and arrays. Combined with Dependency Injection capability of this component library, a complex form can be validated easily:

public class RootValidator : AbstractValidator<Root>
{
    public RootValidator(IValidator<Child> childValidator, IValidator<Item> itemValidator)
    {
        RuleFor(Q => Q.Child).SetValidator(childValidator);
        RuleForEach(Q => Q.ArrayOfItems).SetValidator(itemValidator); // Array, List, IList, ...
    }
}

The validators used MUST be registered in the ASP.NET Core service provider!

If for some reason Dependency Injection is not possible, the parameter ChildValidators (inlined Dictionary<Type, IValidator> defining validators used for each children model types) MUST be passed into the component due to technical reasons.

Repeated Remote Call Warning ⚠️

By default, Fluent Validation does NOT short-circuit the validation chain on first error.

This may cause performance issues when a validation logic hits the database / remote service multiple times in short burst due to validation triggers on field change!

To reduce the impact of repeated custom validation calls, use:

  • .Cascade(CascadeMode.Stop) after RuleFor() chain,

  • Or set ValidatorOptions.CascadeMode = CascadeMode.Stop; on Program.cs application entry point.

Why Not Just Use <DataAnnotationValidator>?

  1. <DataAnnotationValidator> cannot use DI services at the moment...

  2. ... but <DataAnnotationValidator> also cannot do AddModelError() like Razor Pages. Which rules out validation AFTER form submission!

  3. [ValidationAttribute] top-level IsValid() method cannot be async!

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy