Проверка модальной формы Blazor: дважды нажмите кнопку отмены, чтобы закрыть модальное окно при удалении поля формы - PullRequest
0 голосов
/ 17 января 2020

В Blazor я использую модальные диалоги для CRUD. Когда я открываю модальное окно для редактирования записи и удаляю (например) имя пользователя, а затем непосредственно нажимаю кнопку «Отмена», проверка формы все еще начинается. Модальное не закрывается.

Blazor Cancel Validation Мне нужно снова нажать кнопку «Отмена», чтобы закрыть модальное окно.

Я знаю, что могу поставить кнопку отмены за пределами EditForm, но перед диалоговым окном вы увидите мигающее сообщение проверки. закрывается. И я хочу, чтобы кнопка отмены была рядом с кнопкой отправки в модальном нижнем колонтитуле.

Есть ли способ отменить проверку формы при нажатии кнопки Отмена? И я бы предпочел не использовать JavaScript Interop, просто блейзор.

Пример рабочего кода:

@page "/cancel"
@using System.ComponentModel.DataAnnotations;

<h3>Cancel Validation</h3>

<button type="submit" class="btn btn-primary" @onclick="Login">Login</button>
<hr />
<p>Status: @status</p>

@if (showModal)
{
    <div class="modal" tabindex="-1" role="dialog" style="display:block" id="taskModal">
        <div class="modal-dialog shadow-lg bg-white rounded" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">Login</h5>
                </div>
                <div class="modal-body">
                    <EditForm Model="user" OnValidSubmit="HandleValidSubmit">
                        <DataAnnotationsValidator />
                        <ValidationSummary />
                        <div class="form-group row">
                            <label class="col-3 col-form-label">Email: </label>
                            <InputText class="col-8 form-control" @bind-Value="user.Email" />
                        </div>
                        <div class="form-group row">
                            <label class="col-3 col-form-label">Password: </label>
                            <InputText type="password" class="col-8 form-control" @bind-Value="user.Password" />
                        </div>
                        <div class="modal-footer">
                            <button type="submit" class="btn btn-primary">Submit</button>
                            <button type="button" class="btn btn-secondary" @onclick="CancelSubmit">Cancel</button>
                        </div>
                    </EditForm>
                </div>
            </div>
        </div>
    </div>
}

@code {

    public class UserLogin
    {
        [Required(ErrorMessage = "Email is required")]
        public string Email { get; set; }

        [Required(ErrorMessage = "Password is required")]
        public string Password { get; set; }
    }

    UserLogin user = new UserLogin();

    bool showModal;
    string status;

    protected override void OnInitialized()
    {
        showModal = false;
        status = "Init";
        // for demo purposes, if you delete user.Email in the login dialog, you'll need to press 'Cancel' 2 times.
        user.Email = "user@example.com";
        user.Password = "12345";
    }

    private void Login()
    {
        status = "Show Login Modal";
        showModal = true;
    }

    private void HandleValidSubmit()
    {
        status = "Valid Submit";
        showModal = false;
    }

    private void CancelSubmit()
    {
        status = "Cancelled"; 
        showModal = false;
    }
}

Ответы [ 2 ]

1 голос
/ 18 января 2020

@ Jaap, вот решение, основанное на проверке внутренних форм. Я надеюсь, что это удовлетворит вас, пока не будет предложено лучшее решение.

Поддержка проверки форм в Blazor, добавленная в объект EditContext, выполняется на двух уровнях: уровне объекта и уровне поля. Когда вы нажимаете кнопку «Отправить», вся модель проверяется. Мы уже видели, что кнопка «Отправить» работает очень хорошо и не позволяет вам отправлять, если значения полей модели не действительны. Когда вы нажимаете кнопку «Отмена» и значения полей модели действительны, диалоговое окно закрывается без проблем. Но когда одно или несколько полей имеют недопустимые значения (например, после очистки поля электронной почты), и вы нажимаете кнопку «Отмена», проверка уровня поля начинается немедленно, прежде чем код в обработчике события кнопки «Отмена» будет иметь малейшее значение. шанс что-то сделать. Это поведение разработано и повторяется, даже когда я использовал Fluent Validation вместо DataAnnotations validation. Вывод: это наши ограничения, а не система. Нам нужно больше времени уделять изучению Blazor. Решение, которое я предлагаю, состоит в том, чтобы отключить проверку на уровне поля, когда мы нажимаем кнопку «Отмена», и, таким образом, немедленно закрывать диалоговое окно без проверки вообще.

Примечание. Мой код использует Fluent Validation, так как я экспериментировал с Fluent Validation, но то же самое можно сделать и с DataAnnotations Validation. Код в обоих случаях практически идентичен и не имеет ничего общего с Fluent Validation. Обратите внимание, что я адаптировал пример кода Fluent Validation от Chris Sainty

UserLogin.cs

public class UserLogin
{
    public string Email { get; set; }
    public string Password { get; set; }
}

UserLoginValidator.cs

public class UserLoginValidator : AbstractValidator<UserLogin>
    {
       public UserLoginValidator()
     {

         RuleFor(user => user.Email).NotEmpty().WithMessage("You must enter an email address");
         RuleFor(user => user.Email).EmailAddress().WithMessage("You must provide a valid email address");
         RuleFor(user => user.Password).NotEmpty().WithMessage("You must enter a password");
         RuleFor(user => user.Password).MaximumLength(50).WithMessage("Password cannot be longer than 50 characters");
    }
 }

FluentValidationValidator .cs

public class FluentValidationValidator : ComponentBase
{
    [CascadingParameter] EditContext CurrentEditContext { get; set; }
    [Parameter] public bool ShouldValidate { 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(ShouldValidate);
    }
}

EditContextFluentValidationExtensions.cs

 public static class EditContextFluentValidationExtensions
{
    public static EditContext AddFluentValidation(this EditContext editContext, bool shouldValidate)
    {
        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, shouldValidate);

        return editContext;
    }

    private static void ValidateModel(EditContext editContext, ValidationMessageStore messages)
    {
        var validator = GetValidatorForModel(editContext.Model);
        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, bool shouldValidate)
    {
        Console.WriteLine(fieldIdentifier.FieldName.ToString());

        if (shouldValidate)
        {
            var properties = new[] { fieldIdentifier.FieldName };
            var context = new FluentValidation.ValidationContext(fieldIdentifier.Model, new PropertyChain(), new MemberNameValidatorSelector(properties));

            var validator = GetValidatorForModel(fieldIdentifier.Model);
            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));
        var modelValidatorInstance = (IValidator)Activator.CreateInstance(modelValidatorType);

        return modelValidatorInstance;
    }
}

Cancel.razor

@page "/cancel"
@using System.ComponentModel.DataAnnotations;

<h3>Cancel Validation</h3>

<button type="submit" class="btn btn-primary" @onclick="Login">Login</button>
<hr />
<p>Status: @status</p>

@if (showModal)
{
    <div class="modal" tabindex="-1" role="dialog" style="display:block" id="taskModal">
        <div class="modal-dialog shadow-lg bg-white rounded" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">Login</h5>
                </div>
                <div class="modal-body">
                    <EditForm Model="user" OnValidSubmit="HandleValidSubmit">
                        @*<DataAnnotationsValidator />*@
                        <FluentValidationValidator ShouldValidate="false" />
                        @*<ValidationSummary />*@
                        <div class="form-group row">
                            <label class="col-3 col-form-label">Email: </label>
                            <InputText class="col-8 form-control" @bind-Value="user.Email" />
                            <ValidationMessage For="@(() => user.Email)" />
                        </div>
                        <div class="form-group row">
                            <label class="col-3 col-form-label">Password: </label>
                            <InputText type="password" class="col-8 form-control" @bind-Value="user.Password" />
                            <ValidationMessage For="@(() => user.Password)" />
                        </div>
                        <div class="modal-footer">
                            <button type="submit" class="btn btn-primary">Submit</button>
                            <button type="button" class="btn btn-secondary" @onclick="CancelSubmit">Cancel</button>
                        </div>
                    </EditForm>
                </div>
            </div>
        </div>
    </div>
}

@code {
    UserLogin user = new UserLogin();

    bool showModal;
    string status;

    protected override void OnInitialized()
    {
        showModal = false;
        status = "Init";
        // for demo purposes, if you delete user.Email in the login dialog, you'll need to press 'Cancel' 2 times.
        user.Email = "user@example.com";
        user.Password = "12345";
    }

    private void Login()
    {
        status = "Show Login Modal";

        showModal = true;
    }

    private void HandleValidSubmit()
    {
        status = "Valid Submit";
        showModal = false;
    }

    private void CancelSubmit()
    {
        Console.WriteLine("CancelSubmit");

        status = "Cancelled";
        showModal = false;
    }
}

Обратите внимание, что компонент FluentValidationValidator имеет свойство с именем ShouldValidate, для которого мы устанавливаем значение false чтобы удалить проверку на уровне поля. Пожалуйста, следите за ходом исполнения, это очень просто. Я почти ничего не делаю, чтобы решить проблему, что заставляет меня думать, что, возможно, есть более короткий и лучший способ сделать это. Возможно, вам потребуется установить пакет Fluent Validation ... Удачи ...

1 голос
/ 18 января 2020

Итак, я нашел что-то новое здесь: ASP. NET Формы и проверка Core Blazor

<ValidationSummary style="@displaySummary" /> отключен по умолчанию, и я включаю его, когда есть является InvalidSubmit ..

Это не идеальное решение, потому что, когда пользователь (1) удаляет содержимое поля, тогда (2) нажимает «Отправить» проверка активируется, но затем, если пользователь вводит что-то действительное в том же поле (3) , пользователь должен дважды нажать «Отправить». Но сейчас я могу с этим смириться, потому что это случается не так часто.

Новый рабочий код:

@page "/cancel"
@using System.ComponentModel.DataAnnotations;

<h3>Cancel Validation</h3>

<button type="submit" class="btn btn-primary" @onclick="OpenDialog">Open Dialog</button>
<hr />
<div>@((MarkupString)status)</div>

@if (showModal)
{
    <div class="modal" tabindex="-1" role="dialog" style="display:block" id="taskModal">
        <div class="modal-dialog shadow-lg bg-white rounded" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">Cancel Test</h5>
                    <button type="button" class="close" @onclick="CancelSubmit"><span aria-hidden="true">&times;</span></button>
                </div>
                <div class="modal-body">
                    <div>
                        <EditForm Model="@user" OnValidSubmit="HandleValidSubmit" OnInvalidSubmit="HandleInValidSubmit">
                            <DataAnnotationsValidator />
                            <ValidationSummary style="@displaySummary" />
                            <div class="form-group row">
                                <label class="col-3 col-form-label">Email</label>
                                <InputText class="col-8 form-control" @bind-Value="user.Email" />
                            </div>
                            <div class="form-group row">
                                <label class="col-3 col-form-label">Password</label>
                                <InputText type="password" class="col-8 form-control" @bind-Value="user.Password" />
                            </div>
                            <div class="modal-footer">
                                <button type="submit" class="btn btn-primary">Submit</button>
                                <button type="button" class="btn btn-secondary" @onclick="CancelSubmit">Cancel</button>
                            </div>
                        </EditForm>
                    </div>
                </div>
            </div>
        </div>
    </div>
}

@code {

    public class UserLogin
    {
        [Required(ErrorMessage = "Email is required")]
        public string Email { get; set; }

        [Required(ErrorMessage = "Password is required")]
        public string Password { get; set; }
    }

    private UserLogin user = new UserLogin();

    bool showModal = false;
    private string displaySummary = "display:none";
    string status = "";

    protected override void OnInitialized()
    {
        InitUser();
    }

    private void InitUser()
    {
        // for demo purposes, think of it as an 'edit dialog' in CRUD operation.
        status += "Init User";
        user.Email = "user@example.com";
        user.Password = "12345";
        displaySummary = "display:none";
    }

    private void OpenDialog()
    {
        status += "<br />Open User Dialog";
        showModal = true;
    }

    private void HandleValidSubmit()
    {
        status += "<br />Valid Submit";
        displaySummary = "display:none";
        showModal = false;
    }

    private void HandleInValidSubmit()
    {
        displaySummary = "display:block";
        status += "<br />Invalid Submit";
    }

    private void CancelSubmit()
    {
        status += "<br />Cancelled<br /><br />";
        InitUser(); // for demo purposes so you can test it multiple times
        showModal = false;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...