Хорошо, я решил использовать FluentValidation, так как по какой-то причине я не мог заставить настраиваемые атрибуты (или встроенный атрибут Compare
) работать вообще
PinComponent.Razor
<input class="pinBox" type="password" maxlength="1" size="1" onkeypress='return event.charCode >= 48 && event.charCode <= 57' required @bind="@_pinOne"/>
<input class="pinBox" type="password" maxlength="1" size="1" onkeypress='return event.charCode >= 48 && event.charCode <= 57' required @bind="@_pinTwo"/>
<input class="pinBox" type="password" maxlength="1" size="1" onkeypress='return event.charCode >= 48 && event.charCode <= 57' required @bind="@_pinThree"/>
<input class="pinBox" type="password" maxlength="1" size="1" onkeypress='return event.charCode >= 48 && event.charCode <= 57' required @bind="@_pinFour" @oninput="Completion"/>
@code{
private string _value;
[Parameter]
public string Value
{
get { return Value; }
set
{
if (string.IsNullOrWhiteSpace(value))
{
_pinOne = null;
_pinTwo = null;
_pinThree = null;
_pinFour = null;
}
_value = value;
}
}
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
private string _pinOne;
private string _pinTwo;
private string _pinThree;
private string _pinFour;
private void Completion(ChangeEventArgs e)
{
_pinFour = e.Value.ToString();
ValueChanged.InvokeAsync(_pinOne + _pinTwo + _pinThree + _pinFour);
}
}
PinConfirmationComponent.Razor
@using Application.Validation
<EditForm Model="@_model" OnValidSubmit="@OnValidSubmit" OnInvalidSubmit="@OnInvalidSubmit">
<div class="pinContainer">
<PinComponent @bind-Value="_model.Pin"></PinComponent>
<PinComponent @bind-Value="_model.PinConfirmation"></PinComponent>
</div>
<FluentValidationValidator />
<ValidationSummary />
<input id="btnSubmit" class="btn btnFont" type="submit" value="Register PIN" style="margin-top: 5px;" />
</EditForm>
@code {
private PinModel _model = new PinModel();
void OnValidSubmit()
{
}
void OnInvalidSubmit()
{
_model.Pin = null;
_model.PinConfirmation = null;
StateHasChanged();
}
}
PinModel.cs
public class PinModel
{
public string Pin { get; set; }
public string PinConfirmation { get; set; }
}
После этого примера репо Я использовал FluentValidation
EditContextFluentValidationExtensions. cs
public static class EditContextFluentValidationExtensions
{
public static EditContext AddFluentValidation(this EditContext editContext)
{
if (editContext == null)
{
throw new ArgumentNullException(nameof(editContext));
}
var messages = new ValidationMessageStore(editContext);
editContext.OnValidationRequested +=
(sender, eventArgs) => ValidateModel((EditContext)sender, messages);
editContext.OnFieldChanged +=
(sender, eventArgs) => ValidateField(editContext, messages, eventArgs.FieldIdentifier);
return editContext;
}
private static void ValidateModel(EditContext editContext, ValidationMessageStore messages)
{
var validator = GetValidatorForModel(editContext.Model);
if (validator == null)
return;
var validationResults = validator.Validate(editContext.Model);
messages.Clear();
foreach (var validationResult in validationResults.Errors)
{
messages.Add(editContext.Field(validationResult.PropertyName), validationResult.ErrorMessage);
}
editContext.NotifyValidationStateChanged();
}
private static void ValidateField(EditContext editContext, ValidationMessageStore messages, in FieldIdentifier fieldIdentifier)
{
var properties = new[] { fieldIdentifier.FieldName };
var context = new ValidationContext(fieldIdentifier.Model, new PropertyChain(), new MemberNameValidatorSelector(properties));
var validator = GetValidatorForModel(fieldIdentifier.Model);
if (validator == null)
return;
var validationResults = validator.Validate(context);
messages.Clear(fieldIdentifier);
foreach (var validationResult in validationResults.Errors)
{
messages.Add(editContext.Field(validationResult.PropertyName), validationResult.ErrorMessage);
}
editContext.NotifyValidationStateChanged();
}
private static IValidator GetValidatorForModel(object model)
{
var abstractValidatorType = typeof(AbstractValidator<>).MakeGenericType(model.GetType());
var modelValidatorType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.IsSubclassOf(abstractValidatorType));
if (modelValidatorType == null)
return null;
var modelValidatorInstance = (IValidator)Activator.CreateInstance(modelValidatorType);
return modelValidatorInstance;
}
}
FluentValidationValidator.cs
public class FluentValidationValidator : ComponentBase
{
[CascadingParameter]
EditContext CurrentEditContext { get; set; }
protected override void OnInitialized()
{
if (CurrentEditContext == null)
{
throw new InvalidOperationException($"{nameof(FluentValidationValidator)} requires a cascading " +
$"parameter of type {nameof(EditContext)}. For example, you can use {nameof(FluentValidationValidator)} " +
$"inside an {nameof(EditForm)}.");
}
CurrentEditContext.AddFluentValidation();
}
}
PinValidator.cs
public class PinValidator : AbstractValidator<PinModel>
{
public PinValidator()
{
RuleFor(p => p.Pin).NotNull().Matches("^[0-9]{4}$");
RuleFor(p => p).Must(PinsAreSame)
.WithMessage("PINs must be the same");
}
private bool PinsAreSame(PinModel pinModel)
{
return (pinModel.Pin.Equals(pinModel.PinConfirmation));
}
}