@ 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 ... Удачи ...