dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.36k stars 9.99k forks source link

Add async support to form validation with EditContext on submit #51501

Closed AntMaster7 closed 1 year ago

AntMaster7 commented 1 year ago

Is there an existing issue for this?

Is your feature request related to a problem? Please describe the problem.

The EditContext for form validation is missing support for an asynchronous custom validator. If I want to implement form validation that does asynchronous operations like server calls or using the async FluentValidation library, I have to implement some hacks instead of using a custom validator component. The EditForm or custom form implementations have no way of invoking the Validate method on the EditContext asynchronosly to in turn have the EditContext invoke the OnValidationRequested event asynchronously. It appears though that this is something that's planned. But I'm not sure if this will happen or when? It seems this would be a simple feature to implement.

Describe the solution you'd like

I can implement an asynchronous event handler for the OnValidationRequested event of the EditContext. The Validate method of the EditContext and in turn the EditForm await the execution of this event handler.

Additional context

I'm also happy to contribute to this feature with a pull request. I was thinking of something like this inside the EditContext (just a draft):

/// <summary>
/// Validates this <see cref="EditContext"/>.
/// </summary>
/// <returns>True if there are no validation messages after validation; otherwise false.</returns>
public bool Validate() // Existing method
{
    OnValidationRequested?.Invoke(this, ValidationRequestedEventArgs.Empty);
    return !GetValidationMessages().Any();
}

public event Func<object?, EventArgs, Task> OnValidationRequestedAsync; // New

public async Task<bool> ValidateAsync() // New
{
    OnValidationRequested?.Invoke(this, ValidationRequestedEventArgs.Empty);

    await Task.WhenAll(OnValidationRequestedAsync
        .GetInvocationList()
        .Cast<Func<object?, EventArgs, Task>>()
        .Select(invocation => invocation(this, EventArgs.Empty)));

    return !GetValidationMessages().Any();
}

The EditForm already has a HandleSubmitAsync method which instead of calling the sync Validate method could call the async ValidateAsync method. This way the change would be non breaking.

private async Task HandleSubmitAsync()
{
    Debug.Assert(_editContext != null);

    if (OnSubmit.HasDelegate)
    {
        // When using OnSubmit, the developer takes control of the validation lifecycle
        await OnSubmit.InvokeAsync(_editContext);
    }
    else
    {
        // Otherwise, the system implicitly runs validation on form submission
        var isValid = _editContext.Validate(); // This will likely become ValidateAsync later

        if (isValid && OnValidSubmit.HasDelegate)
        {
            await OnValidSubmit.InvokeAsync(_editContext);
        }

        if (!isValid && OnInvalidSubmit.HasDelegate)
        {
            await OnInvalidSubmit.InvokeAsync(_editContext);
        }
    }
}

Edit: Apparently async form validation has already been discussed years ago and then been abandoned. However, I think the approach I'm thinking of is different. I don't plan on doing an async validation on each field change. Rather the async validation is only run once the form is being submitted which I think is legitimate.

javiercn commented 1 year ago

Dupe of https://github.com/dotnet/aspnetcore/issues/7680.

We recommend upvoting that instead