Skip to content

Instantly share code, notes, and snippets.

@ahancock1
Created April 13, 2020 21:21
Show Gist options
  • Save ahancock1/b89cacc6230a9329ef281bc8e8a70bc9 to your computer and use it in GitHub Desktop.
Save ahancock1/b89cacc6230a9329ef281bc8e8a70bc9 to your computer and use it in GitHub Desktop.
Simple Blazor Fluent Validation Validator Component
public class FluentValidator : ComponentBase
{
private readonly IDictionary<Type, IValidator> _validators =
new Dictionary<Type, IValidator>();
[CascadingParameter]
public EditContext EditContext { get; set; }
[Inject]
private IServiceProvider ServiceProvider { get; set; }
protected override void OnInitialized()
{
if (EditContext == null)
{
throw new InvalidOperationException(
$"{nameof(FluentValidator)} requires a cascading parameter.");
}
GetOrAddValidator(EditContext.Model.GetType());
var messages = new ValidationMessageStore(EditContext);
EditContext.OnValidationRequested +=
(s, _) => ValidateModel((EditContext) s, messages);
EditContext.OnFieldChanged +=
(_, e) => ValidateField(EditContext, messages, e.FieldIdentifier);
}
private IValidator GetOrAddValidator(Type type)
{
if (!_validators.ContainsKey(type))
{
_validators[type] = CreateValidator(type);
}
return _validators[type];
}
private IValidator CreateValidator(Type type)
{
return (IValidator) ServiceProvider
.GetService(typeof(IValidator<>).MakeGenericType(type));
}
public FieldIdentifier FindFieldIdentifier(object model, string property)
{
var split = property.Split(new[] {"[", "].", "."}, StringSplitOptions.None);
var walker = model;
foreach (var item in split.Take(split.Length - 1))
{
if (walker == null)
{
break;
}
switch (item)
{
case { } when int.TryParse(item, out var index):
{
if (walker is IList array)
{
walker = array[index];
}
break;
}
default:
{
walker = walker.GetType().GetProperty(item)?.GetValue(walker);
break;
}
}
}
return new FieldIdentifier(walker, split.Last());
}
private void ValidateModel(EditContext context, ValidationMessageStore messages)
{
var validator = GetOrAddValidator(context.Model.GetType());
var result = validator.Validate(context.Model);
messages.Clear();
foreach (var error in result.Errors)
{
var identifier = FindFieldIdentifier(context.Model, error.PropertyName);
messages.Add(identifier, error.ErrorMessage);
}
context.NotifyValidationStateChanged();
}
private void ValidateField(EditContext context, ValidationMessageStore messages,
in FieldIdentifier identifier)
{
var validator = GetOrAddValidator(identifier.Model.GetType());
if (validator == null)
{
return;
}
var result = validator.Validate(
new ValidationContext(
identifier.Model,
new PropertyChain(),
new MemberNameValidatorSelector(
new[]
{
identifier.FieldName
})));
messages.Clear(identifier);
foreach (var error in result.Errors)
{
messages.Add(identifier, error.ErrorMessage);
}
context.NotifyValidationStateChanged();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment