【问题标题】:Validate 2 components have the same value验证 2 个组件具有相同的值
【发布时间】:2019-10-18 14:39:09
【问题描述】:

我编写了一个由 4 个输入字段组成的 PIN 组件(因为它将在一些地方重复使用)

<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{
    [Parameter]
    public EventCallback<string> Completed { get; set; }

    private string _pinOne;
    private string _pinTwo;
    private string _pinThree;
    private string _pinFour;

    private void Completion(ChangeEventArgs e)
    {
        _pinFour = e.Value.ToString();

        Completed.InvokeAsync(_pinOne + _pinTwo + _pinThree + _pinFour);
    }
}

然后我创建了另一个组件,它使用了其中 2 个 PIN 输入组件

<PinComponent Completed="@PinCompleted"></PinComponent>
<PinComponent Completed="@ConfirmationPinCompleted"></PinComponent>
@code {
    private string _pin;
    private string _confirmationPin;

    private bool _valid = false;

    private void PinCompleted(string pin)
    {
        _pin = pin;
    }

    private void ConfirmationPinCompleted(string pin)
    {
        _confirmationPin = pin;

        if (_pin.Equals(_confirmationPin))
        {
            _valid = true;
        }
    }
}

是否可以使用 Blazor 的 ValidationMessage 来确保这两个组件共享相同的值?

【问题讨论】:

    标签: c# blazor blazor-server-side


    【解决方案1】:

    将验证的值和结果传递给您的PinComponent,并使该组件显示验证错误。

    <PinComponent Completed="@PinCompleted"></PinComponent>
    <PinComponent Completed="@ConfirmationPinCompleted" ValidationMessage="@validationMessage"></PinComponent>
    
    @code {
        private string _pin;
        private string _confirmationPin;
    
        private bool _valid = false;
    
        private string ValidationMessage => _valid ? string.Empty : "PIN does not match";
    
        private void PinCompleted(string pin)
        {
            _pin = pin;
        }
    
        private void ConfirmationPinCompleted(string pin)
        {
            _confirmationPin = pin;
    
            if (_pin.Equals(_confirmationPin))
            {
                _valid = true;
            }
        }
    }
    

    如果您想使用 Blazor 表单验证

    class PinModel
    {
        [Required]
        public string Pin {get;set;}
    
        [Required]
        [PinTheSame]
        public string PinConfirmation {get;set;}
    }
    
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public class PinTheSameAttirbute: ValidationAttribute
    {
    
            protected override ValidationResult IsValid(object value, ValidationContext validationContext)
            {
            if (value == null) return new ValidationResult("A pin is required.");
    
            // Make sure you change PinModel to whatever  the actual name is
            if ((validationContext.ObjectType.Name != "PinModel") 
                 return new ValidationResult("This attribute is being used incorrectly.");
            if (((PinModel)validationContext.ObjectInstance).ConfirmPin != value.ToString())
                return new ValidationResult("Pins must match.");
    
            return ValidationResult.Success;
            }
    
    }
    

    并将值作为模型传递

    <EditForm Model="@Model">
        <PinComponent Value="@Pin"></PinComponent>
        <PinComponent Value="@ConfirmationPin"></PinComponent>
    </EditForm>
    

    最后一种方法尚未完全完成,但应该让您对方向有所了解。

    【讨论】:

    • 谢谢,你能帮助/详细说明一下使用模型吗?我已经尝试过这种方法,但似乎无法让它工作(我刚刚将 PinModel 作为组件中的私有字段,将 EditForm 的 Model 设置为它,然后更新我的 PinCompleted 和 ConfirmationPinCompleted 中的属性方法)
    【解决方案2】:

    好的,我决定使用 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; }
    }
    

    this example repo 之后,我使用了 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));
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2020-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-19
      • 2017-06-15
      • 2019-11-15
      • 2021-10-02
      相关资源
      最近更新 更多