NWard
NWard

Reputation: 2086

Server-side Blazor Validation Not Updating UI

I have a Blazor component representing a form, that needs to perform some expensive validation before submitting, to ensure the uniqueness of a data point on a server. I tried to use this documentation as inspiration: https://learn.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-3.1

It seems like better documentation for this is on the Microsoft documentation roadmap: https://github.com/dotnet/AspNetCore.Docs/issues/17377

My component looks like this:

<EditForm Model="@form" OnValidSubmit="@Submit">
    <InputText @bind-Value="form.DataPoint" />
    <ValidationMessage For="() => form.DataPoint" />

    <button type="submit">Go!</button>
</EditForm>

@code {
    private Form form = new Form();
    private EditContext editContext;

    protected override void OnInitialized() 
    {
        editContext = new EditContext(form);
    }

    private async Task Submit()
    {
        var isValid = editContext.Validate() && await ServerValidate(editContext);
        if (isValid) 
        { 
            // do stuff 
        }
    }

    private async Task<bool> ServerValidate(EditContext editContext)
    {
        var form = (Form)editContext.Model;
        var validationErrors = new ValidationMessageStore(editContext);
        var isDataPointCollision = await SomeService.CheckUniqueness(form.DataPoint);
        if (isDataPointCollision)
        {
            var field = new FieldIdentifier(form, nameof(Form.DataPoint));
            validationErrors.Add(field, "This data point already exists, please type a different one");
            editContext.NotifyValidationStateChanged();
            return false;
        }
        return true;
    }
}

My validation code works properly, detects the collision, and prevents submission of the form. However, the UI does not update as expected with the <ValidationMessage [...] /> component. No UI update occurs at all, and no validation messages are displayed. I've also tried this component:

<ValidationSummary Model="@form" />

to no avail.

There are no errors either on the server, or in the client-side JS.

Am I barking up the wrong tree with this approach, or have I missed a connection somewhere? Is there a better way to accomplish what I'm looking to do?

Upvotes: 5

Views: 2295

Answers (3)

BDNTS
BDNTS

Reputation: 63

Henk's answer is correct. A couple of additions

  1. Add <DataAnnotationsValidator /> and <ValidationSummary /> below <EditForm> for individual field validation driven by Model Annotations, and Form summary error messages (above form in addition to on each field) if so desired.
  2. Once a message is added to a field (validationErrors.Add(field, "This data point already exists, please type a different one");) The message will persist, and future Submits will be ignored as Invalid.
  3. Only OnInvalidSubmit() will fire at this point because of the validation error above and its pairing with OnValidSubmit(). The error needs to be cleared for normal processing to proceed.
  4. I've only found a brute force way to clear the errors, and that is with a ClearError() method to reinitialize EditContext:
    public void ClearErrors()
    {
        _editContext = new EditContext(form);
        isSubmitButtonDisabled = false;
        showClearErrors = false;
    }

I then wire it into a a button that is displayed when showClearErrors=true and disable the Submit button. If there is a better way to clear the errors, I'm all ears.

Upvotes: 1

Henk Holterman
Henk Holterman

Reputation: 273244

You create (and update) an EditContext that is not attached to the UI Form.

Change the first line to (note: no Model):

 <EditForm EditContext="editContext"  OnValidSubmit="Submit">

The rest of your code can stay as-is, no need for StateHasChanged() or anything.

On the first docs page you link to, search for "_editContext"

Upvotes: 2

Dmitry Pavlov
Dmitry Pavlov

Reputation: 28290

Call StateHasChanged after validation:

    private async Task Submit()
    {
        var isValid = editContext.Validate() && await ServerValidate(editContext);
        if (isValid) 
        { 
            // do stuff 
        }
        StateHasChanged();
    }

Note: or call it inside editContext.NotifyValidationStateChanged() if you have access to component there.

Upvotes: 2

Related Questions