dotnet / AspNetCore.Docs

Documentation for ASP.NET Core
https://docs.microsoft.com/aspnet/core
Creative Commons Attribution 4.0 International
12.6k stars 25.29k forks source link

Add an example of performing validation directly in the component to "Business logic validation" section of "Blazor forms and validation" #22545

Closed DamianEdwards closed 3 years ago

DamianEdwards commented 3 years ago

I was working on a simple Blazor app recently (two single-form pages) and I needed to add some fairly simple custom validation to a form (ensure at least one of a set of checkboxes was checked). I was surprised that the documentation guided me to create a custom validator component for such a basic scenario.

I suggest we update the "Business logic validation" section to include content that shows how to add basic custom validation to a form so that it correctly interacts with Blazor's built-in form validation logic, without having to encapsulate that logic in a separate component or validation attribute. This would be desirable in cases where the form's model is defined within the component hosting the form (either as members directly on the component or in a sub-class), rather than a model class that is separate for the purposes of reuse, etc.

The approach involves configuring an EditForm to use a declared EditContext and ValidationMessageStore instance, and then setting up a handler for the OnValidationRequested event of the EditContext, inside of which the custom validation logic is performed and the result of which is stored in the ValidationMessageStore instance.

Example:

@page "/example"

<h1>Form with custom validation</h1>

<EditForm EditContext="editContext" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />

    <div class="form-group">
        <label for="name">Name</label>
        <InputText class="form-control" @bind-Value="model.Name" />
    </div>

    <div class="form-group">
        <div class="form-row">
            <div class="form-group col mb-0">
                <div class="form-check form-check-inline">
                    <InputCheckbox id="option1" class="form-check-input" @bind-Value="model.Option1" />
                    <label class="form-check-label" for="option1">
                        Option #1
                    </label>
                </div>
                <div class="form-check form-check-inline">
                    <InputCheckbox id="option2" class="form-check-input" @bind-Value="model.Option2" />
                    <label class="form-check-label" for="option2">
                        Option #2
                    </label>
                </div>
            </div>
        </div>
        <div class="form-row">
            <div class="col">
                <ValidationMessage For="() => model.Options" />
            </div>
        </div>
    </div>

    <button class="btn btn-primary" type="submit">Save</button>
</EditForm>

@code {
    Model model;
    EditContext editContext;
    ValidationMessageStore validationMessageStore;

    protected override void OnInitialized()
    {
        model = new Model();
        editContext = new EditContext(model);
        editContext.OnValidationRequested += editContext_OnValidationRequested;
        validationMessageStore = new ValidationMessageStore(editContext);
    }

    void editContext_OnValidationRequested(object sender, ValidationRequestedEventArgs args)
    {
        validationMessageStore.Clear();

        // Put custom validation logic here
        if (!model.Options)
        {
            validationMessageStore.Add(() => model.Options, "At least one option must be selected");
        }
    }

    void HandleValidSubmit()
    {
        // This will only be called when all validation has passed

    }

    class Model
    {
        [Required]
        public string Name { get; set; }

        public bool Option1 { get; set; }
        public bool Option2 { get; set; }
        public bool Options => Option1 || Option2;
    }
}

Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

guardrex commented 3 years ago

cc: @pranavkm ... any additional guidance? ... any sneaky 😈 or hidden 🐉 for this scenario that I'll need to call out?

..... e.g., this probably can't be mixed (easily anyway) with existing validation approaches in the topic with a happy ending.