From 77f928eb9d36cfe270a98b3e2c09f4c7ef811886 Mon Sep 17 00:00:00 2001 From: yfranz Date: Mon, 6 Nov 2023 12:09:53 -0800 Subject: [PATCH] Implemented a new method `AddValidationResult` in FluentValidationValidator that allows manual addition of FluentValidation's ValidationResult errors to the Blazored FluentValidation EditContext. This enables explicit control over the validation state and message display within Blazor forms, particularly useful when integrating custom validation scenarios that are not automatically managed by Blazor's data annotation validators. --- samples/BlazorServer/Pages/Index.razor | 11 ++++++++ samples/BlazorWebAssembly/Pages/Index.razor | 11 ++++++++ .../EditContextFluentValidationExtensions.cs | 11 +++----- .../FluentValidationsValidator.cs | 26 +++++++++++++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/samples/BlazorServer/Pages/Index.razor b/samples/BlazorServer/Pages/Index.razor index f227a9f..4cc349e 100644 --- a/samples/BlazorServer/Pages/Index.razor +++ b/samples/BlazorServer/Pages/Index.razor @@ -68,6 +68,7 @@
+ @code { private readonly Person _person = new(); @@ -78,4 +79,14 @@ private void PartialValidate() => Console.WriteLine($"Partial validation result : {_fluentValidationValidator?.Validate(options => options.IncludeRuleSets("Names"))}"); + + private void AddValidationResult() + { + _fluentValidationValidator!.AddValidationResult(_person, new FluentValidation.Results.ValidationResult + { + Errors = new List { + new FluentValidation.Results.ValidationFailure { PropertyName = "EmailAddress", ErrorMessage = "This email address is taken already"} + } + }); + } } \ No newline at end of file diff --git a/samples/BlazorWebAssembly/Pages/Index.razor b/samples/BlazorWebAssembly/Pages/Index.razor index 400e083..367024f 100644 --- a/samples/BlazorWebAssembly/Pages/Index.razor +++ b/samples/BlazorWebAssembly/Pages/Index.razor @@ -68,6 +68,7 @@
+ @code { private readonly Person _person = new(); @@ -83,4 +84,14 @@ private void PartialValidate() => Console.WriteLine($"Partial validation result : {_fluentValidationValidator?.Validate(options => options.IncludeRuleSets("Names"))}"); + + private void AddValidationResult() + { + _fluentValidationValidator!.AddValidationResult(_person, new FluentValidation.Results.ValidationResult + { + Errors = new List { + new FluentValidation.Results.ValidationFailure { PropertyName = "EmailAddress", ErrorMessage = "This email address is taken already"} + } + }); + } } \ No newline at end of file diff --git a/src/Blazored.FluentValidation/EditContextFluentValidationExtensions.cs b/src/Blazored.FluentValidation/EditContextFluentValidationExtensions.cs index c06c9f4..5477e08 100644 --- a/src/Blazored.FluentValidation/EditContextFluentValidationExtensions.cs +++ b/src/Blazored.FluentValidation/EditContextFluentValidationExtensions.cs @@ -17,17 +17,14 @@ public static void AddFluentValidation(this EditContext editContext, IServicePro { ArgumentNullException.ThrowIfNull(editContext, nameof(editContext)); - var messages = new ValidationMessageStore(editContext); - editContext.OnValidationRequested += - async (sender, _) => await ValidateModel((EditContext)sender!, messages, serviceProvider, disableAssemblyScanning, fluentValidationValidator, validator); + async (sender, _) => await ValidateModel((EditContext)sender!, serviceProvider, disableAssemblyScanning, fluentValidationValidator, validator); editContext.OnFieldChanged += - async (_, eventArgs) => await ValidateField(editContext, messages, eventArgs.FieldIdentifier, serviceProvider, disableAssemblyScanning, validator); + async (_, eventArgs) => await ValidateField(editContext, fluentValidationValidator.Messages, eventArgs.FieldIdentifier, serviceProvider, disableAssemblyScanning, validator); } private static async Task ValidateModel(EditContext editContext, - ValidationMessageStore messages, IServiceProvider serviceProvider, bool disableAssemblyScanning, FluentValidationValidator fluentValidationValidator, @@ -56,11 +53,11 @@ private static async Task ValidateModel(EditContext editContext, editContext.Properties[PendingAsyncValidation] = asyncValidationTask; var validationResults = await asyncValidationTask; - messages.Clear(); + fluentValidationValidator.Messages.Clear(); foreach (var validationResult in validationResults.Errors) { var fieldIdentifier = ToFieldIdentifier(editContext, validationResult.PropertyName); - messages.Add(fieldIdentifier, validationResult.ErrorMessage); + fluentValidationValidator.Messages.Add(fieldIdentifier, validationResult.ErrorMessage); } editContext.NotifyValidationStateChanged(); diff --git a/src/Blazored.FluentValidation/FluentValidationsValidator.cs b/src/Blazored.FluentValidation/FluentValidationsValidator.cs index ae8e6f9..117d300 100644 --- a/src/Blazored.FluentValidation/FluentValidationsValidator.cs +++ b/src/Blazored.FluentValidation/FluentValidationsValidator.cs @@ -18,6 +18,8 @@ public class FluentValidationValidator : ComponentBase [Parameter] public Action>? Options { get; set; } internal Action>? ValidateOptions { get; set; } + internal ValidationMessageStore Messages { get; private set; } + public bool Validate(Action>? options = null) { if (CurrentEditContext is null) @@ -79,6 +81,30 @@ protected override void OnInitialized() $"inside an {nameof(EditForm)}."); } + Messages = new ValidationMessageStore(CurrentEditContext); CurrentEditContext.AddFluentValidation(ServiceProvider, DisableAssemblyScanning, Validator, this); } + + /// + /// Adds the validation errors contained in a FluentValidation ValidationResult object to the + /// current EditContext's validation messages. This allows for manual validation scenarios where + /// the ValidationResult may not be automatically integrated into Blazor's validation system. + /// Calling this method will update the validation state and the associated UI elements. + /// + /// The object instance that the validation applies to. This is used to + /// identify the form model within the EditContext. + /// The ValidationResult obtained from FluentValidation that contains + /// the details of any validation errors. + public void AddValidationResult(object model, ValidationResult validationResult) + { + if (CurrentEditContext != null && Messages != null) + { + foreach (ValidationFailure error in validationResult.Errors) + { + var fieldIdentifier = new FieldIdentifier(model, error.PropertyName); + Messages.Add(fieldIdentifier, error.ErrorMessage); + } + CurrentEditContext.NotifyValidationStateChanged(); + } + } } \ No newline at end of file