blazored / fluentvalidation Goto Github PK
View Code? Open in Web Editor NEWA library for using FluentValidation with Blazor
Home Page: https://blazored.github.io/FluentValidation/
License: MIT License
A library for using FluentValidation with Blazor
Home Page: https://blazored.github.io/FluentValidation/
License: MIT License
@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?
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.
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.
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.
I have a string in which I need to check if it does have a substring declared in another property of the same class, is there any way to achieve this?
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:
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.
Hosting Model (is this issue happening with a certain hosting model?):
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
<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?):
Additional context
.NET 5 RC2
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
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
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?):
Additional context
Working with version 9.0.0 preview 4
Doc: https://docs.fluentvalidation.net/en/latest/collections.html
Thanks in advance
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?
Please see FluentValidation/FluentValidation#1522
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.
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!
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(" ").
Hosting Model (is this issue happening with a certain hosting model?):
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.
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.
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.
However, go into Index.razor and comment out line 8, then uncomment line 7. Things become a bit odd.
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.
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.
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 ?
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:
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)
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.
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:
Expected behavior
The form shouldn't be submitted with wrong data - it should wait until validation finishes and show the error.
Hosting Model (is this issue happening with a certain hosting model?):
Additional context
It seems that https://github.com/ryanelian/FluentValidation.Blazor has a possible working approach for this issue.
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
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();
}
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.
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.
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
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
Will you please add an MIT license so that our team can use this library?
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
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
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?
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));
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?):
I am unable to make it to work if I move Person to a class library
I added "@using SharedModels.Models" in root _Imports.razor
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.
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:
Expected behavior
Probably shouldn't lag at all.
Hosting Model (is this issue happening with a certain hosting model?):
Any idea where is the bottleneck ??
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:
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
I am finding that if
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
I have this form where I'm building up the list of addresses:
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
As per #20 , I have:
EditForm
and corresponding FluentValidationValidator
tagThe 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:
EditContext
will be used by your component to validate my form.Reality:
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?
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?):
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).
I would like to show the user error as soon as they exit the field. Somewhere I think I say that being done with edit contact, but that may have been with data annotations.
Is there a recommended way to do this?
thanks
Darryl
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:
Hosting Model (is this issue happening with a certain hosting model?):
Additional context
Probably caused by: Pull request 1742 Support Internal Types In AssemblyScanner
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?
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?):
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 ๐
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.
Maybe an idea to add credits + a link to the original article (https://chrissainty.com/using-fluentvalidation-for-forms-validation-in-razor-components/) in the readme.md file?
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)
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.