Git Product home page Git Product logo

fluentvalidation's People

Contributors

adamradocz avatar andrewgriffithsfg avatar arielbeje avatar chrissainty avatar ddjerqq avatar gioviq avatar icnocop avatar jeremyskinner avatar kuraiandras avatar luetm avatar mahmar avatar oliverback avatar p6laris avatar pwelter34 avatar stefh avatar tothalexlaszlo avatar vinod-vetrivel avatar youugotssponged avatar zetterstrom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fluentvalidation's Issues

[Bug] Why is this failing?

@chrissainty
Hello,
I'm trying to use telerik to bind to my model of type T with reflection instead of model.firstname. This example below partially works, but as soon as I give the textbox any value, I'm getting weird errors in the console. What does it mean?
image
image

Addding reference via nuget package

When I add Blazored.FluentValidation via Nuget then it throws exception
Unhandled exception rendering component: Method not found: System.Threading.Tasks.Task`1<FluentValidation.Results.ValidationResult> FluentValidation.IValidator.ValidateAsync(object,System.Threading.CancellationToken)

However when I add Blazored.FluentValidation from your sample then it works fine.

[Bug] Getting validator class from assembly doesn't gracefully handle ReflectionTypeLoadExceptions.

Describe the bug
GetValidatorForModel doesn't gracefully handle scenarios where the assembly reference throws any ReflectionTypeLoadExceptions. Resulting in a hard error.

To Reproduce
In my example, I have reference to Microsfot.JSInterop.WebAssembly.dll from a project dependency. I have a data model tied to an form which doesn't have a backing IValidator<> for that model type. This only matters because if I did have the a backing IValidator<> the order in which assemblies evaluate means a successful Validator is found prior to hitting the assembly that throws the error.

When I run my project and start interacting with the form it will hit a hard error when it Microsfot.JSInterop.WebAssembly.dll is attempted to be loaded as a part of GetValidatorForModel. Here is a detailed look at the exception thrown.

image

Expected behavior
I would instead expect the error to be caught gracefully and then continue evaluating other assemblies. In my scenario it would eventually return null since I don't have a backing IValidator<>.

Hosting Model
Currently running Blazor Server, but I don't actually hit this issues when just running the site. I hit this issue when running Selenium Tests in a different project that hosts and interact with Blazor site. This is why I mention above that the Microsfot.JSInterop.WebAssembly.dll reference comes from a project dependency (my blazor project). I'm not super familiar with assembly referencing in code so wasn't sure if this was worth mentioning or not.

[Bug] Dto Validator Rule not working as expected

Describe the bug
I am using the code below as a rule for a Dto Validator and it is not respecting the NotNull() condition and it is displaying the message even after the DVToolAnswer question has been answered (after it is no longer null).

RuleFor(x => x.DvToolAnswer).NotNull().WithMessage("At least 1 DV Tool option must be selected").When(x => x.WellViewName == "INTCSG1" && x.ActivityOptionId == ActivityOptionId.AvgOfLastPad);

To Reproduce
Steps to reproduce the behavior:

  1. Go to modal in app (modal has a question with two radial buttons)
  2. Try to submit the form/modal without selecting an option to display the message (validator works as expected)
  3. After confirming that validator is working correctly, proceed to choose one of the radial buttons expecting the message goes away.
  4. See error = Message from validator is still showing even though an option has been selected. It does let me submit the form.

Expected behavior
The message from the DtoValidator should no longer appear once an option has been selected

Screenshots
If applicable, add screenshots to help explain your problem.
image

Hosting Model (is this issue happening with a certain hosting model?):

  • Blazor WebAssembly

[Bug] System.InvalidOperationException: Could not find property at Blazored.FluentValidation.EditContextFluentValidationExtensions.ToFieldIdentifier

Describe the bug
Calling StateHasChanged on the component causes InvalidOperatorException when there is RuleForEach validation rule in the Validator. Additionally the validation error disappears and input is marked as Valid.

To Reproduce

  1. Run following code
  2. Removed text from a cell in Name column to cause validation error
  3. Click the button
<EditForm Model="this">    
    <Blazored.FluentValidation.FluentValidationValidator />
    <table>
        <tbody>
            @foreach (var item in DataSource)
            {
                <tr @key="item">
                    <td><InputNumber @bind-Value="item.Id" /></td>
                    <td>
                        <InputText @bind-Value="item.Name" />
                        <ValidationMessage For="() => item.Name" />
                    </td>
                </tr>
            }
        </tbody>
    </table>
    <button @onclick="this.StateHasChanged">Click</button>
</EditForm>

@code {
    public List<Item> DataSource = Enumerable.Range(1, 10).Select(i => new Item { Id = i, Name = $"Item {i}" }).ToList();

    public class Item
    {
        public int Id { get; set; }

        [Required]
        [StringLength(10, MinimumLength = 2)]
        public string Name { get; set; }
    }



    public class MyValidator : AbstractValidator<Index>
    {
        public MyValidator()
        {
            //RuleFor(m => m.TestProp).NotEmpty().MinimumLength(2);
            RuleForEach(m => m.DataSource).SetValidator(new ItemValidator());
        }
    }

    public class ItemValidator : AbstractValidator<Item>
    {
        public ItemValidator()
        {
            RuleFor(m => m.Name).NotEmpty().MinimumLength(2);
        }
    }
} 

Expected behavior
After clicking the button, all invalid Inputs should be marked as invalid and validation messages should be shown.

Hosting Model (is this issue happening with a certain hosting model?):

  • Blazor Server

Additional context
.NET 5 RC2

[Feature Request] Add Instructions to the README

Is your feature request related to a problem? Please describe.
Blazored.FluentValidation v 1.4.1, .Net 5.0
Validation messages were not displayed until I connected the service to the Startup.cs [...]

Describe the solution you'd like
add this item to the README

Question: project contributor

Hello Chris,

I really like you Blazored projects. And I would like to contribute to a new project to render a label for model property like:

<LabelText For="@(() => Model.AccountNumber)" />

And this small component will also be using the IStringLocalizer to support localization.

Is it possible that I can be added as a contributor to create a new project in the Blazored organization?

Kind regards,
Stef

[Bug] use of {CollectionIndex} in RulesForEach

Hello

Describe the bug
Using "CollectionIndex" not working, it is writing {CollectionIndex} instead of the actual index when updating to version 9.0.0 of FluentValidation. I don't kwow if it's your side problem or original project problem.

To Reproduce

public class PersonValidator : AbstractValidator {
public PersonValidator() {
RuleForEach(x => x.AddressLines).NotNull().WithMessage("Address {CollectionIndex} is required.");
}
}

Hosting Model (is this issue happening with a certain hosting model?):

  • Blazor WebAssembly

Additional context
Working with version 9.0.0 preview 4

Doc: https://docs.fluentvalidation.net/en/latest/collections.html

Thanks in advance

[Question] RuleFor with Reclection?

How can I write write a rule for a generic type?
Example: RuleFor(p => p.GetType().GetProperty("firstname")).NotEmpty();

p "can" be a contact, but this is not known at runtime.
How can I do this?

Arrays missing Item indexer will result in NullReferenceException

If the model contains arrays without Item indexer it will be a NullReferenceException at the ToFieldIdentifier method in EditContextFluentValidationExtensions.cs.
The following line will be null in such a case:

var prop = obj.GetType().GetProperty("Item");

Here is a simplified model that I use.

eAvverka = new uppgifterTypeEAvverka
{                        
	AnmalanAnsokan = new uppgifterTypeEAvverkaAnmalanAnsokan
	{
		Allmant = new uppgifterTypeEAvverkaAnmalanAnsokanAllmant
		{                                
			Fastighet = new uppgifterTypeEAvverkaAnmalanAnsokanAllmantFastighet
			{
				FastNamn = "XXX",                                    
				Block = "19",
				Enhet = "1",
				KommunNamn = "YYY" //,                                    
			}                                
		},
		Avverkning = new uppgifterTypeEAvverkaAnmalanAnsokanAvverkning
		{			
			Andamal = 3,                                
			AndamalSpecified = true,
			OmlaggningAr = 2020,
			OmlaggningArSpecified = true,                                
			Foryngringmetoder = new uppgifterTypeEAvverkaAnmalanAnsokanAvverkningForyngringMetod[]
			{
				new uppgifterTypeEAvverkaAnmalanAnsokanAvverkningForyngringMetod
				{
					AntalTrad = 50,
					AntalTradSpecified = true,
					AntalHa = 10.4f,
					Metod = 1,
					TradslagSpecified = true,
					Tradslag = 0					
				}
			}
		}                            
	}                        
}

If there is a validation error in uppgifterTypeEAvverkaAnmalanAnsokanAvverkningForyngringMetod the exception will be thrown.

blazor.server.js:19 [2020-08-12T08:38:23.027Z] Error: System.NullReferenceException: Object reference not set to an instance of an object.
   at Blazored.FluentValidation.EditContextFluentValidationExtensions.ToFieldIdentifier(EditContext editContext, String propertyPath)
   at Blazored.FluentValidation.EditContextFluentValidationExtensions.ValidateModel(EditContext editContext, ValidationMessageStore messages, IServiceProvider serviceProvider, Boolean disableAssemblyScanning, IValidator validator)
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_0(Object state)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteSynchronously(TaskCompletionSource`1 completion, SendOrPostCallback d, Object state)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c.<.cctor>b__23_0(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteBackground(WorkItem item)
e.log @ blazor.server.js:19
C @ blazor.server.js:8
(anonymous) @ blazor.server.js:8
(anonymous) @ blazor.server.js:1
e.invokeClientMethod @ blazor.server.js:1
e.processIncomingData @ blazor.server.js:1
connection.onreceive @ blazor.server.js:1
i.onmessage @ blazor.server.js:1

I had a hard time to find the bug until I downloaded the source code and made some debugging.

I fixed it with the following code, probably needs to be modified to be bulletproof but now the validation works for arrays.

object newObj = null;
                if (nextToken.EndsWith("]"))
                {
                    // It's an indexer
                    // This code assumes C# conventions (one indexer named Item with one param)
                    nextToken = nextToken.Substring(0, nextToken.Length - 1);
                    
                    var prop = obj.GetType().GetProperty("Item");
                    
                    if(null != prop)
                    {
                        var indexerType = prop.GetIndexParameters()[0].ParameterType;
                        var indexerValue = Convert.ChangeType(nextToken, indexerType);
                        newObj = prop.GetValue(obj, new object[] { indexerValue });                        
                    }
                    else
                    {
                        // If there is no Item property
                        // Try to cast the object to array
                        object[] array = obj as object[];
                        if (array != null)
                        {
                            int indexerValue = Convert.ToInt32(nextToken);
                            newObj = array[indexerValue];
                        }
                        else
                        {
                            throw new InvalidOperationException($"Could not find indexer on object of type {obj.GetType().FullName}.");
                        }
                    }                                        
                }

Hosting model is Blazor Server.

Make FluentValidation responsiveness based on mouse/key-up rather then input change for better UX?

Hi,
First of all - great work!
The current solution is great, however I find it annoying in cases where text inputs are constrained to have min/max length (this is just one example).

Use case:
Suppose we have a field named "Name" and the constraints is that the length is at least 2 letters.
Say we have pre-validated the form so the user sees that "Minimum length of 2 is required".
When a user types 2 letters, the validation message does not disappear until he clicks somewhere - usually the next click is on the "Submit" button, but if this button is being activated by the form being valid or not - we have a problem - since it is not valid.

I really hope I made myself clear...
Maybe this is the intended behavior though, I don't know...

Cheers!

Using SetValidator with a validator inheriting from AbstractValidator<string> doesn't seem to come through

Describe the bug
SetValidator doesn't seem to set for vlidators inheriting from AbstractValidator<string>.

I could be because of the primitive type nature
(see FluentValidation/FluentValidation#184)

However I have code (I can't share that sorry!) where in the backend API where the validator behaviour is "as expected".
So, a validator with a rule against a string type property is assigning a validator which inherits from AbstractValidator<string> - which is why I came here to see if it's possible to get that behaviour working in the blazor front end too!

To Reproduce
Grab the sample code from repo.

Extend the SharedModels/Person.cs class the following:
(change the string type Person.EmailAddress validation to a dedicated validator, and try to use SetValidator to achieve it)


using FluentValidation;
using System.Threading.Tasks;

namespace SharedModels
{
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int? Age { get; set; }
        public string EmailAddress { get; set; }
        public Address Address { get; set; } = new Address();
    }

    public class PersonValidator : AbstractValidator<Person>
    {
        public PersonValidator()
        {
            RuleSet("Names", () =>
            {
                RuleFor(p => p.FirstName)
                .NotEmpty().WithMessage("You must enter your first name")
                .MaximumLength(50).WithMessage("First name cannot be longer than 50 characters");

                RuleFor(p => p.LastName)
                .NotEmpty().WithMessage("You must enter your last name")
                .MaximumLength(50).WithMessage("Last name cannot be longer than 50 characters");
            });

            RuleFor(p => p.Age)
                .NotNull().WithMessage("You must enter your age")
                .GreaterThanOrEqualTo(0).WithMessage("Age must be greater than 0")
                .LessThan(150).WithMessage("Age cannot be greater than 150");

            RuleFor(p => p.Address).SetValidator(new AddressValidator());

            RuleFor(p => p.EmailAddress).SetValidator(new EmailValidator()); // this doesn't work (i.e. does no validation - just passes any input given)

            //RuleFor(p => p.EmailAddress).EmailValidation(); // extension strategy also doesn't work

            //RuleFor(p => p.EmailAddress) // This works - because it's the original code :)
            //    .NotEmpty().WithMessage("You must enter a email address")
            //    .EmailAddress().WithMessage("You must provide a valid email address")
            //    .MustAsync(async (email, cancellationToken) => await IsUniqueAsync(email)).WithMessage("Email address must be unique").When(p => !string.IsNullOrEmpty(p.EmailAddress));

        }

        private async Task<bool> IsUniqueAsync(string email)
        {
            await Task.Delay(300);
            return email.ToLower() != "[email protected]";
        }
    }

    public class EmailValidator : AbstractValidator<string>
    {
        public EmailValidator()
        {
            RuleFor(v => v).EmailAddress().WithMessage("must be good email");
        }
    }

    public static class Extensions
    {
        /// <summary>
        /// Inspired by https://github.com/FluentValidation/FluentValidation/issues/184#issuecomment-197952324
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="rule"></param>
        /// <returns></returns>
        public static IRuleBuilderOptions<T, string> EmailValidation<T>(this IRuleBuilder<T, string> rule)
        {
            return rule.EmailAddress().WithMessage("must be valid email");
        }
    }
}

Run the app, and click on "save".

Notice no validation was applied to Email field.

Expected behavior
Without having to configure anything, SetValidator(new EmailValidator()); to "work" in the sense that, in the front end when the save button is clicked the validation rules are executed, and the user is confronted with the error messages passed in the method WithMessage(" ").

Screenshots
image

Hosting Model (is this issue happening with a certain hosting model?):

  • Blazor WebAssembly

Additional context
The reason why I want this to work is because for some string properties it's not appropriate to create a complex type of its own, and the validation rules are to be shared across multiple validators - for example, passwords and phone number input - they should stay as string for me, and I have multiple use cases needing these validated, hence the need to share the validation logic against a string. I'm not too precious as to using the AbstractValidator strategy v.s. extension method v.s. another way I haven't learned about -- but either way I do want to be able to use FluentValidation component for UI validation, with a shared collection of rules used against a string property!

The example in this post is a little trivial since the built in validator has that, but I hope it illustrates the challenge.

CurrentEditContext.AddFluentValidation needs to be moved to the OnParametersSet()

Is your feature request related to a problem? Please describe.
in class FluentValidationValidator:

protected override void OnInitialized()
{
     CurrentEditContext.AddFluentValidation(ServiceProvider, DisableAssemblyScanning, Validator);
}

If you change the EditContext in EditForm, the changes will not be applied

Describe the solution you'd like
You need to do the same as in ValidationFieldMessage.razor

        protected override void OnParametersSet()
	{
		if (CurrentEditContext == _previousEditContext)
			return;
		DetachValidationStateChangedListener();
		CurrentEditContext.OnValidationStateChanged += HandleValidationStateChanged;
		_previousEditContext = CurrentEditContext;
	}

Yes, this scenario is very rarely used, but sometimes it comes across.

Additional context
All components (EditForm, ValidationSummary, ValidationFieldMessage) support EditContext changes, only the DataAnnotationsValidator does not support context changes. I don't understand why this is happening.

Odd InputDate behavior

Minimal repro is here:

https://github.com/lxman/InputDateValidationTest

I am not even sure if this falls under the purview of this project, so please pardon me if this belongs somewhere else. That having been said...

If I run the project in its initial state and select a date in the future, things appear to work as I have come to expect.
image
However, go into Index.razor and comment out line 8, then uncomment line 7. Things become a bit odd.
image
Notice I still receive my error message but now the outline around the input shows a border that would seem to indicate successful validation.
Now, for some more fun, go into DateModelValidator.cs and uncomment lines 12-16.
image
I don't even get my message at this point. It looks like that is the auto-generated message from FluentValidation.
You may note that what I am trying to do here is "subclass" the InputDate component so that I can have some styling and other options built in for use at various places in my site. I have used a similar method with InputText and InputSelect, and they are working well. This issue so far only seems to happen with InputDate.

What is needed to get validation work for nested objects in nested components ?

My project is complex, so let me abstract the issue I have:

My main form has an EditContext for a Person which has many addresses and each address is rendered in a loop by a sub component.
The issue is that error messages and red border shows up ONLY for failing validations of main form properties, but not for the sub (address) form. However when I use the validation summary, main and sub from error shows correct. So validation is working, just not the graphics for each item on the sub form. ( example

I added to both main and sub form and I pass the context via [CascadingParameter] to the child form.

I am missing anything or is this a bug ?

Expected behaviour for interrelated properties

I've got some validation rules that span multiple properties and what I'm finding is that if there's a validation rule on C that says "A + B must equal C" then when A or B are changed they do not cause the validation message to be updated for C.

By way of a basic example, in the gif below Amount Paid must equal Net Total + Tax Due:

cPDjGtPaLZ

The code for the invoice and its validator looks like this:

    public class InvoiceHeader
    {
        public int? NetTotal { get; set; }
        public int? TaxTotal { get; set; }
        public int? AmountPaid { get; set; }
    }

    public class InvoiceHeaderValidator : AbstractValidator<InvoiceHeader>
    {
        public InvoiceHeaderValidator()
        {
            RuleFor(x => x.AmountPaid).Equal(x => x.NetTotal + x.TaxTotal);
        }
    }

And the code for the Invoice component looks like this

@page "/invoice"
@using ServerSideBlazor.Models
@using Blazored.FluentValidation

<h1>Invoice</h1>

<EditForm Model="_invoiceHeader" OnValidSubmit="HandleValidSubmit" OnInvalidSubmit="HandleInvalidSubmit">
  <FluentValidationValidator />
  <ValidationSummary />

  <div class="form-group">
    <label for="NetTotal" class="control-label  col-md-4">Net Total: </label>
    <div class="col-md-8">

      <InputNumber class="form-control text-right" @bind-Value="_invoiceHeader.NetTotal" />
      <ValidationMessage For="@(() => _invoiceHeader.NetTotal)" />
    </div>
  </div>

  <div class="form-group">
    <label for="TaxTotal" class="control-label  col-md-4">Tax Due: </label>
    <div class="col-md-8">

      <InputNumber class="form-control text-right" @bind-Value="_invoiceHeader.TaxTotal" />
      <ValidationMessage For="@(() => _invoiceHeader.TaxTotal)" />
    </div>
  </div>

  <div class="form-group">
    <label for="AmountPaid" class="control-label  col-md-4">Amount Paid: </label>
    <div class="col-md-8">

      <InputNumber class="form-control text-right" @bind-Value="_invoiceHeader.AmountPaid" />
      <ValidationMessage For="@(() => _invoiceHeader.AmountPaid)" />
    </div>
  </div>

  <button type="submit">Save</button>

</EditForm>
@code {
  private InvoiceHeader _invoiceHeader = new InvoiceHeader();

  private void HandleValidSubmit()
  {
  }

  private void HandleInvalidSubmit()
  {
  }
}

Is the behaviour I'm seeing the expected behaviour, and if so, is there any way to tell the validation to update the validation messages for all the components, not just the one that changed?

(Apologies, this may well be how Blazor validation is expected to work, I'm trying to work out if I've set my rules up badly, need to do something to change Blazor's in-built validation behaviour or if this behaviour comes from the Blazored.FluentValidation library)

How to disable submit button if form is invalid (with async validators)?

I have some fields that need to be validated by making async calls to the server webApi. On edit/focus change of the corresponding form controls, those calls should be made and the submit button only enabled once the whole form is valid.

Is this possible at the current stage of Blazor?

Please provide a code example.

[Bug] Async Validator not working correctly

Describe the bug
When using an async validator it's still possible to submit the form with invalid data.

To Reproduce
I modified the Blazor Server sample code to reduce the steps to show the behavior.
Steps to reproduce the behavior:

  1. Slightly modify the sample code for Blazor Server to only have the name field.
  2. Set Task.Delay to a much higher value, i.e. 3000 to imitate a API call
  3. Enter "test" for name and submit form.
  4. Form is submitted successfully, validation occurs much later when form is already submitted with wrong data.

Expected behavior
The form shouldn't be submitted with wrong data - it should wait until validation finishes and show the error.

Screenshots
image

Hosting Model (is this issue happening with a certain hosting model?):

  • Blazor Server

Additional context
It seems that https://github.com/ryanelian/FluentValidation.Blazor has a possible working approach for this issue.

[Bug] When you enter a wrong value multiple times in a field, the error message repeats.

When you enter a wrong value multiple times in a field, the error message repeats every time you try to submit the form.

When you then enter a new wrong value in another field and press submit, only that error message is displayed, but when you submit the form again, without changing anything so pressing submit twice, the others become visible again.

WASM Client Side 3.2.0 Preview 2
Visual Studio 16.6.0. Preview 1
.NET SDK 5.0 Preview 1
Edge Dev (Chromium) Browser

Form code:

<EditForm EditContext="@editContext" OnValidSubmit="HandleValidSubmit" OnInvalidSubmit="HandleInValidSubmit">
    <FluentValidationValidator />
    <ValidationSummary style="@displaySummary" />

Screenshot

Annotation 2020-03-20 141027

Is it possible to validate child components?

I've this Claim submit razor page:

<EditForm Model="@Model" OnValidSubmit="@Submit">
    <FluentValidationValidator />

    <div class="row">
        <AddressPartial Model="@Model" />
    </div>
</EditForm>

And this Address child page:

@inject ISharedResources Localizer

<LabelText For="@(() => Model.Address.Street)" />
<div class="input-group mb-3">
    <InputText @bind-Value="@Model.Address.Street" />
    <ValidationMessage For="@(() => Model.Address.Street)" />
</div>

@code {
    [Parameter]
    public ClaimBase Model { get; set; }
}

And when I clear the street (which should result in an required error message), this does not work and I get this exception in the browser:

[2019-09-26T18:09:51.529Z] Error: System.InvalidCastException: Unable to cast object of type 'Models.Address' to type 'Models.Claim'.
   at FluentValidation.ValidationContext.ToGeneric[T]() in /home/jskinner/code/FluentValidation/src/FluentValidation/ValidationContext.cs:line 211
   at FluentValidation.AbstractValidator`1.FluentValidation.IValidator.ValidateAsync(ValidationContext context, CancellationToken cancellation) in /home/jskinner/code/FluentValidation/src/FluentValidation/AbstractValidator.cs:line 74
   at Blazored.FluentValidation.EditContextFluentValidationExtensions.ValidateField(EditContext editContext, ValidationMessageStore messages, FieldIdentifier fieldIdentifier, IServiceProvider serviceProvider, IValidator validator) in C:\Users\Web.Blazor\Validation\EditContextFluentValidationExtensions.cs:line 64

Is this supposed to work, or should the below code be changed into:

 validator = GetValidatorForModel(serviceProvider, fieldIdentifier.Model); // fieldIdentifier.Model = the Address and editContext.Model is the Claim
private static async void ValidateField(EditContext editContext, ValidationMessageStore messages, FieldIdentifier fieldIdentifier, IServiceProvider serviceProvider, IValidator validator = null)
        {
            var properties = new[] { fieldIdentifier.FieldName };
            var context = new ValidationContext(fieldIdentifier.Model, new PropertyChain(), new MemberNameValidatorSelector(properties));

            if (validator == null)
            {
                validator = GetValidatorForModel(serviceProvider, editContext.Model);
            }

            var validationResults = await validator.ValidateAsync(context);

            messages.Clear(fieldIdentifier);
            messages.Add(fieldIdentifier, validationResults.Errors.Select(error => error.ErrorMessage));

            editContext.NotifyValidationStateChanged();
        }

Add a ShouldValidate Parameter to the FluentValidationValidator

I've run into a situation where I only want my forms validated when the form is submitted, and to disable any fieldchanged events that would apply validation style changes or messages as the user progresses through the form.

I found a solution based on the Blazored FluentValidation components here:
https://stackoverflow.com/questions/59787064/blazor-modal-form-validation-youve-to-click-the-cancel-button-twice-to-close-t

I'm wondering if the ShouldValidate parameter could be incorporated into your solution; seems like a lot of people might have a use for what appears to be a pretty simple feature to add.

When clause does not seem to be working correctly

Given my following rule:

public class SomeClassValidator : AbstractValidator<SomeClass>
{
    public SomeClassValidator()
    {
        RuleFor(s => s.Id).NotEmpty().When(s => s.Active).WithMessage("Cannot be empty");
    }
}

Now the above is part of a list in my Blazor form (stripped HTML):

@foreach (var someClassInstance in ViewModel.SomeClassList)
{
  <tr>
    <td>
      <InputText class="form-control" @bind-Value=someClassInstance.Id />
      <ValidationMessage For="() => someClassInstance.Id" />
    </td>
    <td>
      @if (someClassInstance.Active)
      {
        <button type="button" @onclick="@(() => { Delete(someClassInstance); })"><delete</button>
      }
      else
      {
        <button type="button" @onclick="@(() => { Restore(someClassInstance); })">restore</button>
      }
    </td>
  </tr>
}

No if I click on my delete button the validator shows a validation error which is wrong. Code delete action:

private void Delete(SomeClass someClassInstance)
{
    someClassInstance.Active = false;
}

Maybe it is something I do wrong or maybe the fluent validation is not working correctly.

[Bug] n FluentValidationValidators gives n validation errors for same error

Describe the bug
When using big forms sometimes it can be valuable to show validation errors on multiple places on the page.

Example:

--top of page & save button--
<FluentValidationValidator /> <-- 1st summary
--lots of form stuff--
--bottom of page & save button--
<FluentValidationValidator /> <-- 2nd summary

This results in the errors being repeated twice in both summarys.

Expected behavior
Errors should only be repeated once in each summary for 1-n summaries used.

Hosting Model
Only tested with Blazor WebAssembly

Version
2.0.0

[Question] Is it possible to load a ValidationResult from JSON?

Hello,

Is it possible to load a ValidationResult object into the validator so that the messages get applied as appropriate? For example, some of my validations for a property may require database lookups so they aren't performed until we attempt to persist the object.

Thanks

System.TypeLoadException: Unable to locate a validator

I'm just having a few problems with this library and nested types.

I have the following page

@page "/courses/create"
@namespace ContosoUniversity.Blazor.Courses.Pages

<h2>Create</h2>

<ContosoSpinLoader IsLoading="@(Departments == null)">
    <ContentTemplate>
        <h4>Course</h4>
        <hr />
        <div class="row">
            <div class="col-md-4">

                <EditForm Model="@Data" OnValidSubmit="HandleValidSubmit">
                    <FluentValidationValidator />
                    <ValidationSummary />

                    <div class="form-group">
                        <Label For="@(() => Data.Number)"></Label>
                        <InputNumber class="form-control" @bind-Value="@Data.Number" />
                    </div>

                    <div class="form-group">
                        <Label For="@(() => Data.Title)"></Label>
                        <InputText class="form-control" @bind-Value="@Data.Title" />
                    </div>

                    <div class="form-group">
                        <Label For="@(() => Data.Credits)"></Label>
                        <InputNumber class="form-control" @bind-Value="@Data.Credits" />
                    </div>

                    <div class="form-group">
                        <Label For="@(() => Data.Department)"></Label>
                        <ContosoInputSelect class="form-control" @bind-Value="@Data.Department.Id">
                            @foreach (var department in Departments)
                            {
                                <option value="@department.Id">@department.Name</option>
                            }
                        </ContosoInputSelect>
                    </div>

                    <div class="form-group">
                        <button type="submit" class="btn btn-primary">Create</button>
                    </div>
                </EditForm>
            </div>
        </div>

    </ContentTemplate>
</ContosoSpinLoader>

<div>
    <a href="courses">Back to List</a>
</div>

However, when i change the drop down list for the departments, i get the following exception (taken from the browser console)

blazor.server.js:15 [2020-02-06T17:39:01.855Z] Error: System.TypeLoadException: Unable to locate a validator of type FluentValidation.IValidator`1[[ContosoUniversity.Domain.UniversityAggregate.Department, ContosoUniversity.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] or FluentValidation.AbstractValidator`1[[ContosoUniversity.Domain.UniversityAggregate.Department, ContosoUniversity.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
   at Blazored.FluentValidation.EditContextFluentValidationExtensions.GetValidatorForModel(IServiceProvider serviceProvider, Object model)
   at Blazored.FluentValidation.EditContextFluentValidationExtensions.ValidateField(EditContext editContext, ValidationMessageStore messages, FieldIdentifier fieldIdentifier, IServiceProvider serviceProvider, IValidator validator)
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_0(Object state)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteSynchronously(TaskCompletionSource`1 completion, SendOrPostCallback d, Object state)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c.<.cctor>b__23_0(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteBackground(WorkItem item)

And i can't seem to work out how to fix this issue. The validator for the form looks like this

        public class Validator : AbstractValidator<Command>
        {
            public Validator()
            {
                RuleFor(p => p.Number).NotEmpty();

                RuleFor(p => p.Title)
                    .SetValidator(new TitleValidator());

                RuleFor(p => p.Credits)
                    .SetValidator(new CreditsValidator());
            }
        }

Which means i don't want/need to validate on the department field. If i add a blank validator in for the department, its seems to work ok.

        public class Validator : AbstractValidator<Command>
        {
            public Validator()
            {
                RuleFor(p => p.Number).NotEmpty();

                RuleFor(p => p.Title)
                    .SetValidator(new TitleValidator());

                RuleFor(p => p.Credits)
                    .SetValidator(new CreditsValidator());

                RuleFor(p => p.Department)
                    .SetValidator(new DepartmentValidator());
            }
        }

        public class DepartmentValidator : AbstractValidator<Department>
        {
            public DepartmentValidator()
            {

            }
        }

Any ideas why this would be happening? I've tried debugging it but the debug tools for blazor are lacking a bit at the moment.

Cheers
Tom

DotNet5 Compability

After migrate the project to DotNet5 the validations is not working anymore.

To Reproduce
Steps to reproduce the behavior:
After submit the errors show up in the console tab:

Expected behavior
Validate the form.

Screenshots
image

Hosting Model:

  • Blazor Server

[Feature Request] Severity

Is your feature request related to a problem? Please describe.
FluentValidator supports Severity levels for validation messages.

I only want to prevent form submit, when there are ValidationFailures with severity Error and ignore Warnings and Infos.
Currently, the FluentValidator generates errors for all severity levels. I would like to set minimum level to Error and ignore Warning and Info.

Describe the solution you'd like

<FluentValidator ValidationFailureFilter="@(error => error.Severity >= Severity.Error)" >

Describe alternatives you've considered

<FluentValidator Severity="Severity.Error" >

Additional context

Even when I want to enable submit when there are warnings, I still want to be able to show the warnings in the ValidationSummary.

That might be tricky, but in worst case the FluentValidator might expose Validate, resp ValidateAsync method.

FluentValidator _fluentValidatorRef;

async Task OnSubmit()
{
  var result = await _fluentValidatorRef.ValidateAsync();
  bool isValid = !result.Errors.Any(e => e.Severity >= Severity.Error);
  if (isValid) 
  {
      .....
  }
}

Alternatively, FluentValidator might expose ValidationResult, but it will propagate to EditContext only those errors, that matches the ValidationFailureFilter. We will be able to catch the Warnings and show them somewhere, but I prefer the custom OnSubmit logic

Target NET 5?

This library currently targets NET Standard 2.1 and includes the following dependencies:

  • Microsoft.AspNetCore.Components (3.1.14)
  • Microsoft.AspNetCore.Components.Web (3.1.14)

The 3.1.x versions of those two packages do not target NET 5, but the 5.0.x versions of those packages do. FluentValidation itself targets NET 5. Can this library target NET 5 with the appropriate 5.0.x Microsoft package dependencies?

ValidateField doesn't use RuleSet

Is your feature request related to a problem? Please describe.
The ValidateField method doesn't consider RuleSets.
It generates errors for all RuleSets. Instead, I only like to have errors for one particalur RuleSet.

Describe the solution you'd like
I think you could combine functionality of RulesetValidatorSelector and MemberNameValidatorSelector to achieve this. Currently, there is only MemberNameValidatorSelector when creating the context:
var context = new ValidationContext<object>(fieldIdentifier.Model, new PropertyChain(), new MemberNameValidatorSelector(properties));

[Bug] Validation stops working when using services.AddValidatorsFromAssembly in Blazor Webassembly startup Program.cs

Describe the bug
In my Blazor Wasm project, I wanted to turn off assembly scanning on the FluentValidator, and I assumed that I will have to add validators in the startup (i.e. Program.cs). However, when adding this line of code to load validators from the relevant assembly, the FluentValidator simply stopped working, with no exceptions (i.e. all forms were considered valid). The offending line in Program.cs that caused this issue is:

services.AddValidatorsFromAssembly(typeof(UpdateRigSectionsCommand).GetTypeInfo().Assembly);

Expected behavior
We should be able to load the validators during startup and disable assembly scanning in FluentValidator

Hosting Model (is this issue happening with a certain hosting model?):

  • Blazor WebAssembly

Set Options from outside / have a default RuleSet that always applies

Is your feature request related to a problem? Please describe.
Currently, it's not possible to have a default RuleSet. It's set internally to IncludeAllRuleSets. However, this is not desired behavior in some cases.

Describe the solution you'd like
Please allow setting a default RuleSet for the Validator, i.e. directly in razor markup.
<FluentValidationValidator @ref="_fluentValidationValidator" RuleSet="Name" DisableAssemblyScanning="true" />

Describe alternatives you've considered
Alternatively, it should be possible to set the RuleSet by code through the options:

protected override void OnInitialized()
{
     _fluentValidationValidator.Validator.Options.RuleSet = "Name";
}

Currently, the options field is only internally and cannot be modified. This is really a limitation.

[Bug?] Text input is slow when using @oninput and fluent validation

Describe the bug
I am validating my input as I type, so validation performs with every letter. Basically using this component. It works fine with [DataAnnotations], but with fluent validation, it slows down the input and lags noticeably.

To Reproduce
Steps to reproduce the behavior:

  1. Go to this Blazor Repl, where is copy of the sample WA app (with person and address)
  2. Try typing into First Name - it lags noticeably

Expected behavior
Probably shouldn't lag at all.

Hosting Model (is this issue happening with a certain hosting model?):

  • Blazor WebAssembly
  • Didn't tested on server

Any idea where is the bottleneck ??

[Feature Request] Validator Customization

Is your feature request related to a problem? Please describe.
I would like to have a possibility to specify which ruleset of injected validator will be executed against current model

Describe the solution you'd like
A similar to the solution in FluentValidation.AspNetCore library:

  • You are able to specify rulesets by [CustomizeValidator(RuleSet = "common,create")] attribute
  • Probably the best option is to define rulesets via FluentValidationValidator parameters

Describe alternatives you've considered
Alternatively it's still possible to have different model types amd different validators for each, which cause a lot of redundant classes created in project

Form's OnValidSubmit fires before validation is complete (for Async validation rules)

I am finding that if

  1. I have async validation rules
  2. The submit button is immediately clicked on the form
    that the OnValidSubmit handler is fired - even though the form is invalid. Only after it has fired, does the ValidationSummary component show the errors.

My Form:

        <EditForm Model="@contactModel" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="@HandleInvalidSubmit" Context="EditFormContext">
            <FluentValidationValidator />
            <ValidationSummary />

            <p>
                <label>First Name: </label>
                <InputText @bind-Value="@contactModel.FirstName" />
            </p>

            <p>
                <label>Last Name: </label>
                <InputText @bind-Value="@contactModel.LastName" />
            </p>

            <p>
                <label>Tel Number: </label>
                <InputText @bind-Value="@contactModel.TelephoneNumber" />
            </p>

            <p>
                <label>Cell Number: </label>
                <InputText @bind-Value="@contactModel.CellNumber" />
            </p>

            <p>
                <label>Email Address: </label>
                <InputText @bind-Value="@contactModel.EmailAddress" />
            </p>

            <button type="submit">Save</button>
        </EditForm>

My validator:

    public class CreateContactCommandValidator : AbstractValidator<CreateContactCommand>
    {
        private readonly IContactsAppContext contactsAppContext;

        public CreateContactCommandValidator(IContactsAppContext contactsAppContext)
        {
            RuleFor(x => x.FirstName).NotEmpty();
            RuleFor(x => x.LastName).NotEmpty();
            RuleFor(x => x.EmailAddress).NotEmpty().EmailAddress().MustAsync(BeUnique).WithMessage("Email already registered");

            this.contactsAppContext = contactsAppContext ?? throw new ArgumentNullException(nameof(contactsAppContext));
        }

        async Task<bool> BeUnique(string email, CancellationToken cancellationToke)
        {
            return await contactsAppContext.Contacts.CountAsync(p => p.EmailAddress == email) == 0 ? true : false;
        }
    }

In this example, if I click the submit button on an empty form, the OnValidSubmit event is fired, even though the Email Address is empty (and therefore invalid). Only after the OnValidSubmit event has fired, does the ValidationSummary component show the validation errors.
This only seems to happen if I have async validation rules. If I remove the 'MustAsync' validation rule from the email address, everything works just fine.

Please advise if I am doing something wrong.
Thanks
Andrew

Nested validator

I have this form where I'm building up the list of addresses:

image

Here is the example:
https://github.com/kedzior-io/blazor-webassmebly-aspnetcore-hosted-fluent-validation/blob/master/BlazorFluentValidation/Client/Pages/Index.razor

<EditForm Model="@Person">
    <FluentValidationValidator />

    <div class="form-group">
        <label>First Name: </label>
        <InputText @bind-Value="@Person.FirstName" class="form-control" />
        <ValidationMessage For="@(() => Person.FirstName)" />
    </div>

    <div class="form-group">
        <label>Last Name: </label>
        <InputText @bind-Value="@Person.LastName" class="form-control" />
        <ValidationMessage For="@(() => Person.LastName)" />
    </div>

    <div class="form-group">
        <label>Address List: </label>
        @foreach (var address in Person.Adresses)
        {
            <p>- @address.City, @address.Country</p>
        }
    </div>

    <hr />

    <p>Add Address</p>
    <FluentValidationValidator Validator="AddressValidator" />
    <div class="form-group">
        <label>City: </label>
        <InputText @bind-Value="@NewAddress.City" class="form-control" />
        <ValidationMessage For="@(() => NewAddress.City)" />
    </div>
    <div class="form-group">
        <label>Country: </label>
        <InputText @bind-Value="@NewAddress.Country" class="form-control" />
        <ValidationMessage For="@(() => NewAddress.Country)" />
    </div>

    <button @onclick="AddAddress">Add Address</button>

    <hr />

    <button type="submit">Save</button>

    @if (isValid)
    {
        <div class="alert alert-success" role="alert">
            Oh wow! I'm valid!
        </div>
    }
</EditForm>

@code {
    Person Person { get; set; } = new Person();
    Address NewAddress { get; set; } = new Address();
    AddressValidator AddressValidator = new AddressValidator();

    bool isValid { get; set; } = false;

    public void AddAddress()
    {
        var result = AddressValidator.Validate(NewAddress);

        if (!result.IsValid)
        {
            isValid = false;
            return;
        }

        Person.Adresses.Add(NewAddress);
        NewAddress = new Address();
        isValid = true;
    }
}

When I hit "Add Address" validation fires for first name and last name and completely ignores city and country.

When I remove:

<FluentValidationValidator Validator="AddressValidator" />

and instantiate validator manually:

    public void AddAddress()
    {
        var addressValidator = new AddressValidator();
        var result = addressValidator.Validate(NewAddress);
     //  ...
    }

it catches errors on address instance but it doesn't show it on the form below corresponding inputs.

Any idea why?

The only way I managed to get it working is splitting it into two forms (which is not ideal):
https://github.com/kedzior-io/blazor-webassmebly-aspnetcore-hosted-fluent-validation/blob/master/BlazorFluentValidation/Client/Pages/Working.razor

[Feature Request] Explicit validator specification

As per #20 , I have:

  • A parent component containing an EditForm and corresponding FluentValidationValidator tag
  • A handful of child components in the form
  • A single DI-registered validator which validates all fields on this form

The DI-registered validator also new's up a child validator containing a non-DI constructor argument, as I have non-trivial validation rules:

RuleForEach(i => i.FinalProducts).SetValidator(i => new ProductValidator(i.Nature));

Expectation:

  • My DI-registered validator for the model used in my form's EditContext will be used by your component to validate my form.

Reality:

  • My DI-registered validator is used, but additionally:
  • An assembly scan and DI lookup is performed to look up validators for models used in my child components.
  • The assembly scan fails because of a dependency issue in a third-party component.
  • The DI lookup fails if it finds a validator never intended to be directly invoked nor registered in DI (my no-arg ProductValidator from the example above).

Is the design of your component that for each bound value, a validator is located for the associated model? This works for self-contained components, but isn't so great for a situation like mine where the models for my child components are just fields in the parent EditContext model, and where we have a single parent validator for the lot.

Given the desired behaviour appears to be a significant departure from how your component is designed, perhaps we can pass in the target validator model type as an argument for FluentValidationValidator? Or just a flag to say to use the EditContext model's validator?

[Bug]

Describe the bug
Unhandled exception in browser.

To Reproduce
Steps to reproduce the behavior:
While following the concepts in Chapter 5 of Blazor in action, I am getting this error:

[2021-03-29T21:44:03.980Z] Error: System.MissingMethodException: Method not found: 'System.Collections.Generic.IList`1<FluentValidation.Results.ValidationFailure> FluentValidation.Results.ValidationResult.get_Errors()'.
   at Blazored.FluentValidation.EditContextFluentValidationExtensions.ValidateModel(EditContext editContext, ValidationMessageStore messages, IServiceProvider serviceProvider, Boolean disableAssemblyScanning, FluentValidationValidator fluentValidationValidator, IValidator validator)
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Blazored.FluentValidation.EditContextFluentValidationExtensions.ValidateModel(EditContext editContext, ValidationMessageStore messages, IServiceProvider serviceProvider, Boolean disableAssemblyScanning, FluentValidationValidator fluentValidationValidator, IValidator validator)
   at Blazored.FluentValidation.EditContextFluentValidationExtensions.<>c__DisplayClass3_0.<AddFluentValidation>b__0(Object sender, ValidationRequestedEventArgs eventArgs)
   at Microsoft.AspNetCore.Components.Forms.EditContext.Validate()
   at Microsoft.AspNetCore.Components.Forms.EditForm.HandleSubmitAsync()
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle)

Frankly, I don't know where to look to find out why a method would be missing. :(

Here's the beginning of the Edit Form.

  <EditForm Model="@objNewPerson" OnValidSubmit="@HandleValidSubmit">
        <FluentValidationValidator />
        <FormSection Title="Personal Info">

Using <DataAnnotationsValidator /> works fine (except I thought it would be easier to use fluent for the child records).

Expected behavior
No errors in JS console.
Validation error in markup ("Surname is required")

Hosting Model (is this issue happening with a certain hosting model?):

  • Blazor Server

Additional context
Following the steps from Blazor in Action, with my own model, that includes a "child" record (Person and PhoneNumbers, rather than Trails and Steps).

FluentValidation 10.2.0 causes MissingMethodException in Blazored.FluentValidation

Describe the bug
FluentValidation 10.2.0 causes the following MissingMethodException in Blazored.FluentValidation:

System.MissingMethodException: Method not found: 'FluentValidation.AssemblyScanner FluentValidation.AssemblyScanner.FindValidatorsInAssembly(System.Reflection.Assembly)'.
at Blazored.FluentValidation.EditContextFluentValidationExtensions.GetValidatorForModel(IServiceProvider serviceProvider, Object model, Boolean disableAssemblyScanning)...

To Reproduce
Steps to reproduce the behavior:

  1. Install Blazored.FluentValidation 2.0.0 and FluentValidation 10.1.0.
  2. Setup a Blazor form using fluent validation. The samples provided with this repo will work.
  3. Launch the app and submit the form, no issue.
  4. Upgrade to FluentValidation 10.2.0.
  5. Launch the app again and submit the form. The exception occurs.

Hosting Model (is this issue happening with a certain hosting model?):

  • Tested using Blazor Server

Additional context
Probably caused by: Pull request 1742 Support Internal Types In AssemblyScanner

[Question]Support for validator with constructor argument

Please be sure to check the readme file before raising an issue.
Fluent validation supports validators with a constructor parameter using an overload of the SetValidator method. See CollectionValidatorWithParentTests for sample.

		var validator = new TestValidator {
			v => v.RuleFor(x => x.Surname).NotNull(),
			v => v.RuleForEach(x => x.Orders).SetValidator(y => new OrderValidator(y))
		};

I tried the same with Blazored.FluentValidation and gets an exception. This is because it is trying to get the constructor parameter from DI instead of using the already provided instance.

Is there any way to get it working?

[Bug] Nested Complex Type Validation Fails

Describe the bug

Although FluentValidation supports nested complex types in the model classes (see here), the FluentValidationValidator throws an exception when editing the nested complex type:

System.InvalidOperationException: Cannot validate instances of type 'Address'. This validator can only validate instances of type 'Customer'

Originating from:

Blazored.FluentValidation.EditContextFluentValidationExtensions.ValidateField(EditContext editContext, ValidationMessageStore messages, FieldIdentifier fieldIdentifier, IServiceProvider serviceProvider, Boolean disableAssemblyScanning, IValidator validator)

To Reproduce

Please have a look at the minimal source project I created to reproduce the issue. Please download the repo, build, and execute the sample.

Expected behavior

Nested complex types should be editable and the validation rules should work accordingly.

Hosting Model (is this issue happening with a certain hosting model?):

  • Blazor WebAssembly (.Net 5)

Additional context

I guess if the EditContext.Model is passed to the ValidationContext instead of fieldIdentifier.Model, validation should execute successfully. Perhaps such a change in editContext.OnFieldChanged can fix the problem.

Many thanks for the awesome library ๐Ÿ‘

Enable interception of model type used to fetch validators (for proxy type scenarios)

EditContextFluentValidationExtensions.GetValidatorForModel uses model.GetType() to determine what validator to fetch from the service provider. The majority of my view models are proxied at runtime and so model.GetType() will return the runtime-generated proxy type, not the underlying compile-time type being proxied. The result is that validators will sometimes not be found.

My proposal is to add a new parameter to EditContextFluentValidationExtensions.AddFluentValidation (in a non-breaking fashion).

public delegate Type InterceptEditContextModelTypeDelegate(object model);

public static EditContext AddFluentValidation(this EditContext editContext, IServiceProvider serviceProvider, bool disableAssemblyScanning, IValidator validator, FluentValidationValidator fluentValidationValidator)
	=> editContext.AddFluentValidation(serviceProvider, disableAssemblyScanning, validator, fluentValidationValidator, model => model.GetType());

public static EditContext AddFluentValidation(this EditContext editContext, IServiceProvider serviceProvider, bool disableAssemblyScanning, IValidator validator, FluentValidationValidator fluentValidationValidator, InterceptEditContextModelTypeDelegate modelTypeInterceptor)
{
	...
}

private static IValidator GetValidatorForModel(IServiceProvider serviceProvider, object model, bool disableAssemblyScanning, InterceptEditContextModelTypeDelegate modelTypeInterceptor)
{
	var modelType = modelTypeInterceptor.Invoke(model);
	...
}

I can submit a PR for this later.

Exception when searching for validators

I haven't been able to replicate this locally, but have identified this exception from our test envrionment. This appears to happen if a validator isn't in DI and Blazored FluentValidation scans for it.

I am assuming that this scan is just identifying an existing issue in my solution, but is it possible to handle these errors more gracefully?

System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types.
Could not load file or assembly 'System.DirectoryServices.Protocols, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
   at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
   at System.Reflection.RuntimeModule.GetTypes()
   at Blazored.FluentValidation.EditContextFluentValidationExtensions.GetValidatorForModel(IServiceProvider serviceProvider, Object model)
   at Blazored.FluentValidation.EditContextFluentValidationExtensions.ValidateField(EditContext editContext, ValidationMessageStore messages, FieldIdentifier fieldIdentifier, IServiceProvider serviceProvider, IValidator validator)
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_0(Object state)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteSynchronously(TaskCompletionSource`1 completion, SendOrPostCallback d, Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)

Validation occurs before binding completes when using a custom Razor component whose custom binding needs to call JSInterop

I encountered this issuing when using this IntlTelInput component, which is a Blazor wrapper for intl-tel-input, a popular phone number validator js library.

The IntlTelInput Razor component wraps a basic input component like this:

<input @onchange="OnChange" type="tel" @ref="_telInput"/>

private async void OnChange (ChangeEventArgs e)
{
    CurrentValue = await _intlTelInputJsInterop.GetData(_inputIndex);
    if (CurrentValue is not null)
    {
        await _intlTelInputJsInterop.SetNumber(_inputIndex, CurrentValue.Number);
     }
}

And I use the IntlTelInput component itself in an EditForm like this:

<EditForm EditContext="_editContext" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit">
    
    <FluentValidationValidator DisableAssemblyScanning="@true" />
    <ValidationSummary/>
        
    <IntlTelInput @bind-Value="_model.IntTelNumber"/>
        
    <button class="btn-primary">Submit</button>

</EditForm>

Note that the bound property model.IntTelNumber is not a string , but a custom type IntlTel. It is constructed by the JSInterop call as shown earlier. The object then exposes a Number property (for the phone number) and a IsValid property which represents the result of the number validation by intl-tel-input. My FluentValidation code in turn uses this IsValid property like this:

 RuleFor(x => x.IntTelNumber).Cascade(CascadeMode.Stop)
                .NotNull()
                .WithMessage("Please enter a phone number")
                .Must( x => x.IsValid)
                .WithMessage("Invalid phone number")

So here's the problem:

Whenever a user enters a number, and immediately click the submit button, the OnChange event handler in the Razor component triggers, which is supposed to call JSInterop and assign the generated object to model.IntTelNumber. But before this completes, the FluentValidation code already triggers, and at this time, it sees IntTelNumber as null, and therefore this validation fails.

After the JSInterop call completes, the FluentValidation code appears to trigger again, this time validating the property property.

The more bizarre part is, on a desktop browser, the first "failed validation" occurs so fast (can be only seen while debugging) that in the actual EditForm, OnValidSubmit is actually triggered. So everything turns out fine and the form validates normally.

But on any mobile browser, OnInvalidSubmit is triggered instead. The result is that the first time user enters the phone number and hits the submit button, the form does not submit. But if the user simply hits the button again, it validates property and submits the form.

I wonder if there is anything that can be done to mitigate this issue. Any advice would be greatly appreciated.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.