In a vanilla .NET 6 Blazor Server project, I am using Blazored.FluentValidation 2.1.0. When trying to validate a single property, as documented in the FluentValidation docs, it is triggering the validation for the whole form.
I've tried to create a .NET 6 MVC project and used FluentValidation 11.5.2 directly. As expected, only the provided property is being validated when I call Validate()
by specifying the property in the IncludeProperties()
method. Is there something wrong that I'm doing or missing from my side on Blazor Server?
Reproduction steps:
FormModel.cs
classpublic class FormModel
{
public string? Name { get; set; }
public string? Email { get; set; }
public string? Comment { get; set; }
}
FormModelValidator.cs
classpublic class FormModelValidator : AbstractValidator<FormModel>
{
public FormModelValidator()
{
RuleFor(x => x.Name).NotEmpty().WithMessage("Name is mandatory");
RuleFor(x => x.Email).NotEmpty().EmailAddress().WithMessage("Email is mandatory and must be valid");
RuleFor(x => x.Comment).NotEmpty().WithMessage("Comment is mandatory");
}
}
Program.cs
builder.Services.AddValidatorsFromAssemblyContaining<FormModelValidator>();
<EditForm Model="@_model">
<FluentValidationValidator @ref="_fluentValidationValidator" DisableAssemblyScanning="@true" />
<div class="form-group">
<label for="name">Name:</label>
<InputText id="name" class="form-control" @bind-Value="@_model.Name" />
<ValidationMessage For="@(() => _model.Name)" />
</div>
<div class="form-group">
<label for="email">Email:</label>
<InputText id="email" class="form-control" @bind-Value="@_model.Email" />
<ValidationMessage For="@(() => _model.Email)" />
</div>
<div class="form-group">
<label for="comment">Comment:</label>
<InputTextArea id="comment" class="form-control" @bind-Value="@_model.Comment" />
<ValidationMessage For="@(() => _model.Comment)" />
</div>
<button type="submit" class="btn btn-primary" @onclick="@HandleSubmit">Submit</button>
</EditForm>
@if (_formSubmitted)
{
<p class="text-success">Form submitted successfully!</p>
}
@if (_formError)
{
<p class="text-danger">Form is incorrect!</p>
}
@code {
private FormModel _model = new FormModel();
private bool _formSubmitted, _formError;
private FluentValidationValidator? _fluentValidationValidator;
private async Task HandleSubmit()
{
var isValid = await _fluentValidationValidator!.ValidateAsync(options => options.IncludeProperties("Comment"));
if (isValid)
{
_formSubmitted = true;
}
else
{
_formError = true;
}
}
}
The reason for your problem is that with the button type as submit
, the form is submitted and HandleSubmitAsync
is called.
This is the EditForm
code.
private async Task HandleSubmitAsync()
{
Debug.Assert(_editContext != null);
if (OnSubmit.HasDelegate)
await OnSubmit.InvokeAsync(_editContext);
else
{
var isValid = _editContext.Validate();
if (isValid && OnValidSubmit.HasDelegate)
await OnValidSubmit.InvokeAsync(_editContext);
if (!isValid && OnInvalidSubmit.HasDelegate)
await OnInvalidSubmit.InvokeAsync(_editContext);
}
}
You don't have a registered OnSubmit
delegate, so var isValid = _editContext.Validate();
is run which validates the whole object.
There are two solutions to the problem.
<button type="submit" class="btn btn-primary" @onclick="@HandleSubmit">Submit</button>
To:
<button type="button" class="btn btn-primary" @onclick="@HandleSubmit">Submit</button>
HandleSubmit
handler on the EditForm
and remove it from the button.<EditForm Model="@_model" OnSubmit="HandleSubmit">
//....
<button type="submit" class="btn btn-primary">Submit</button>
BTW - excellent Minimal Reproducible Example.