Basic validation
To add basic validation to a form, add the DataAnnotationsValidator
Component. This enables validation using data annotations applied to the model class.
Blazor performs two types of validation:
- Field validation—performed when the user tabs out of a field.
- Model validation—performed when the user submits the form.
To display validation errors at the top of the page, add the ValidationSummary
Component.
Pages/FormExample2.razor
@page "/form-example-1"
@using Microsoft.Extensions.Logging
@inject ILogger<FormExample1> Logger
<!-- OnValidSubmit only invokes the assigned event handler if the form is valid when submitted:-->
<EditForm Model="@exampleModel" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText id="name" @bind-Value="exampleModel.Name" />
<button type="submit">Submit</button>
</EditForm>
@code {
private ExampleModel exampleModel = new();
private void HandleValidSubmit()
{
Logger.LogInformation("HandleValidSubmit called");
// Process the valid form
}
}
Custom validation logic
An EditForm
instance uses EditContext
and ValidationMessageStore
instances to validate form fields. To apply custom validation:
- Declare a
ValidationMessageStore
instance and anEditForm
instance - Create an event handler method
- Assign the handler to
EditContext
’sOnValidationRequested
event - Perform custom validation logic in the event handler
- Update the
ValidationMessageStore
with the validation error message
Example
ExampleForm.razor
@page "/form-example-4"
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<FormExample4> Logger
<h2>Ship Holodecks</h2>
<EditForm EditContext="editContext" OnValidSubmit="@HandleValidSubmit">
<label>
Type 1:
<InputCheckbox @bind-Value="holodeck.Type1" />
</label>
<label>
Type 2:
<InputCheckbox @bind-Value="holodeck.Type2" />
</label>
<button type="submit">Update</button>
<ValidationMessage For="() => holodeck.Options" />
<p>
<a href="http://www.startrek.com/">Star Trek</a>,
©1966-2019 CBS Studios, Inc. and
<a href="https://www.paramount.com">Paramount Pictures</a>
</p>
</EditForm>
@code {
// STEP #1
private EditContext? editContext;
private Holodeck holodeck = new();
// STEP #1
private ValidationMessageStore? messageStore;
protected override void OnInitialized()
{
editContext = new(holodeck);
// STEP 3
editContext.OnValidationRequested += HandleValidationRequested;
messageStore = new(editContext);
}
// STEP #2
private void HandleValidationRequested(object? sender,
ValidationRequestedEventArgs args)
{
messageStore?.Clear();
// STEP 4
if (!holodeck.Options)
{
// STEP 5
messageStore?.Add(() => holodeck.Options, "Select at least one.");
}
}
private void HandleValidSubmit()
{
Logger.LogInformation("HandleValidSubmit called: Processing the form");
// Process the form
}
public class Holodeck
{
public bool Type1 { get; set; }
public bool Type2 { get; set; }
public bool Options => Type1 || Type2;
}
public void Dispose()
{
if (editContext is not null)
{
editContext.OnValidationRequested -= HandleValidationRequested;
}
}
}
Custom Validator components
Use a custom validator component in cases where an independent model class is used across several components.
You can create custom validator components to process validation messages for different forms on the same page or the same form at different steps of form processing (for example, client-side validation followed by server-side validation):
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
namespace BlazorSample;
// 1. Derive your validator component from ComponentBase:
public class CustomValidation : ComponentBase
{
// 2. Create a ValidationMessageStore to hold the current list of form errors:
private ValidationMessageStore? messageStore;
// 3. Create the form's EditContext as a CascadingParameter:
[CascadingParameter]
private EditContext? CurrentEditContext { get; set; }
protected override void OnInitialized()
{
if (CurrentEditContext is null)
{
throw new InvalidOperationException(
$"{nameof(CustomValidation)} requires a cascading " +
$"parameter of type {nameof(EditContext)}. " +
$"For example, you can use {nameof(CustomValidation)} " +
$"inside an {nameof(EditForm)}.");
}
messageStore = new(CurrentEditContext);
CurrentEditContext.OnValidationRequested += (s, e) =>
messageStore?.Clear();
CurrentEditContext.OnFieldChanged += (s, e) =>
messageStore?.Clear(e.FieldIdentifier);
}
// 4. This method is called by the form's component. Errors are passed in via a Dictionary;
// the key is the name of the form field with the error, the value is the error list:
public void DisplayErrors(Dictionary<string, List<string>> errors)
{
if (CurrentEditContext is not null)
{
foreach (var err in errors)
{
messageStore?.Add(CurrentEditContext.Field(err.Key), err.Value);
}
CurrentEditContext.NotifyValidationStateChanged();
}
}
// 5. This clears the ValidationMessageStore and is called:
// - Validation is requested (OnValidRequested event is raised)
// - A field changes (the OnFieldChanges event is raised)
// - Manually be developer code
public void ClearErrors()
{
messageStore?.Clear();
CurrentEditContext?.NotifyValidationStateChanged();
}
}
The custom validator component is used like this:
<EditForm Model="@starship" OnValidSubmit="@HandleValidSubmit">
<CustomValidation @ref="customValidation" />
<ValidationSummary />
Server-side validation with a custom validator component
ValidationSummary and ValidationMessage components
ValidationSummary
summaries all validation message. Optionally, output only validation message for a specific model with the Model
parameter:
<ValidationSummary Model="@starship" />
ValidationMessage<TValue>
displays validation messages for specific field with the For
attribute and a lambda expression for the model property:
<ValidationMessage For="@(() => starship.MaximumAccommodation)" />
Both ValidationSummary
and ValidationMessage
support arbitrary attributes which are added to the generated <div>
or <ul>
elements.
ValidationSummary
and ValidationMessage
can be styled via the validation-message CSS class:
.validation-message {
color: red;
}