A battle tested library for using FluentValidation with Blazor. Supports async validation, validation severity, custom rulesets and is open for extensibility. Contributors are welcome!
$ dotnet add package vNext.BlazorComponents.FluentValidationA battle tested library for using FluentValidation with Blazor that supports async validation, severity levels and more.
For introduction, see this blog post
Live demo can be found here: https://blazorrepl.telerik.com/GQYpbvbP37ENDltY58
You can install from Nuget using the following command:
Install-Package vNext.BlazorComponents.FluentValidation
Or via the Visual Studio package manger.
Start by add the following using statement to your root _Imports.razor.
@using vNext.BlazorComponents.FluentValidation
You can then use it as follows within a EditForm component.
<EditForm Model="@Person" OnValidSubmit="@SubmitValidForm">
<FluentValidationValidator />
<ValidationSummary />
<p>
<label>Name: </label>
<InputText @bind-Value="@Person.Name" />
</p>
<p>
<label>Age: </label>
<InputNumber @bind-Value="@Person.Age" />
</p>
<p>
<label>Email Address: </label>
<InputText @bind-Value="@Person.EmailAddress" />
</p>
<button type="submit">Save</button>
</EditForm>
@code {
Person Person { get; set; } = new Person();
async Task SubmitValidForm(EditContext editContext)
{
var validationResult = await editContext.GetValidationResultAsync(); //make sure async valiation completes
if (validationResult.IsValid)
{
await JS.InvokeVoidAsync("alert", "Form Submitted Successfully!");
}
}
}
The component locates validators using IValidatorFactory optional service.
The DefaultValidatorFactory implementation check for validators registered with DI first.
If it finds multiple validators, validators in the same assembly and namespace are takes precedence. If it can't find any, it will then try scanning the applications assemblies
You can override default behaviour on by registering IValidatorFactory service:
services.AddSingleton<IValidatorFactory>(new DefaultValidatorFactory { DisableAssemblyScanning = false })
or per FluentValidationValidator component
<FluentValidationValidator ValidatorFactory="customValidatorFactory" />
@code {
IValidatorFactory customValidatorFactory = new MyCustomValidatorFactory();
}
See DefaultValidatorFactory.cs for more info.
class PersonValidator : AbstractValidator<Person>
{
public PersonValidator() {
RuleFor(x => x.Name).NotEmpty();
RuleFor(x => x.Address).SetValidator(new AddressValidator()); //must be set explicitelly
}
}
class AddressValidator: AbstractValidator<Address> //should be separate class
{
public AddressValidator() {
RuleFor(x => x.Street).NotEmpty();
}
}
Blazor performs two kinds of validation:
EditContext.Validate() which is called usually on form submitEditContext.NotifyValidationStateChanged() which is called automatically, when user edits inputs.When Field validation is triggered, FluentValidator will create validator based on FieldIdentifier.Model, which might be different from EditContext.Model in case of complex models.
Consider following example:
<EditContext Model="Person" OnValidSubmit="ValidSubmitted">
<InputText @bind-Value="Person.Name" />
<InputText @bind-Value="Person.Address.Street" />
<button type="submit" />
</EditContext>
When user edits Person.Name, FluentValidator validates the property using IValidator<Person>
When user edits Person.Address.Street, FluentValidator validates the property using IValidator<Address>
When user clicks submit button, FluentValidator validates the Person class using IValidator<Person>.
However, IValidator<Address> will not be automatically used, unless it is explicitelly defined for Address property in IValidator<Person>.
Address street is validated only when user edits the input, but not on submit:
class PersonValidator : AbstractValidator<Person>
{
public PersonValidator() {
RuleFor(x => x.Name).NotEmpty();
}
}
class AddressValidator: AbstractValidator<Person>
{
public AddressValidator() {
RuleFor(x => x.Street).NotEmpty();
}
}
Street is validated only on submit, but not when user edits the input:
class PersonValidator : AbstractValidator<Person>
{
public PersonValidator() {
RuleFor(x => x.Name).NotEmpty();
RuleFor(x => x.Address.Street).NotEmpty();
}
}