ASP.NET Core Blazor forms and validation
By Daniel Roth and Luke Latham
Forms and validation are supported in Blazor using data annotations.
The following ExampleModel
type defines validation logic using data annotations:
using System.ComponentModel.DataAnnotations;
public class ExampleModel
{
[Required]
[StringLength(10, ErrorMessage = "Name is too long.")]
public string Name { get; set; }
}
A form is defined using the EditForm
component. The following form demonstrates typical elements, components, and Razor code:
<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 ExampleModel();
private void HandleValidSubmit()
{
Console.WriteLine("OnValidSubmit");
}
}
- The form validates user input in the
name
field using the validation defined in theExampleModel
type. The model is created in the component's@code
block and held in a private field (exampleModel
). The field is assigned to theModel
attribute of the<EditForm>
element. - The
DataAnnotationsValidator
component attaches validation support using data annotations. - The
ValidationSummary
component summarizes validation messages. HandleValidSubmit
is triggered when the form successfully submits (passes validation).
A set of built-in input components are available to receive and validate user input. Inputs are validated when they're changed and when a form is submitted. Available input components are shown in the following table.
Input component | Rendered as… |
---|---|
InputText |
<input> |
InputTextArea |
<textarea> |
InputSelect |
<select> |
InputNumber |
<input type="number"> |
InputCheckbox |
<input type="checkbox"> |
InputDate |
<input type="date"> |
All of the input components, including EditForm
, support arbitrary attributes. Any attribute that doesn't match a component parameter is added to the rendered HTML element.
Input components provide default behavior for validating on edit and changing their CSS class to reflect the field state. Some components include useful parsing logic. For example, InputDate
and InputNumber
handle unparseable values gracefully by registering them as validation errors. Types that can accept null values also support nullability of the target field (for example, int?
).
The following Starship
type defines validation logic using a larger set of properties and data annotations than the earlier ExampleModel
:
using System;
using System.ComponentModel.DataAnnotations;
public class Starship
{
[Required]
[StringLength(16, ErrorMessage = "Identifier too long (16 character limit).")]
public string Identifier { get; set; }
public string Description { get; set; }
[Required]
public string Classification { get; set; }
[Range(1, 100000, ErrorMessage = "Accommodation invalid (1-100000).")]
public int MaximumAccommodation { get; set; }
[Required]
[Range(typeof(bool), "true", "true",
ErrorMessage = "This form disallows unapproved ships.")]
public bool IsValidatedDesign { get; set; }
[Required]
public DateTime ProductionDate { get; set; }
}
In the preceding example, Description
is optional because no data annotations are present.
The following form validates user input using the validation defined in the Starship
model:
@page "/FormsValidation"
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="@starship" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<p>
<label for="identifier">Identifier: </label>
<InputText id="identifier" @bind-Value="starship.Identifier" />
</p>
<p>
<label for="description">Description (optional): </label>
<InputTextArea id="description" @bind-Value="starship.Description" />
</p>
<p>
<label for="classification">Primary Classification: </label>
<InputSelect id="classification" @bind-Value="starship.Classification">
<option value="">Select classification ...</option>
<option value="Exploration">Exploration</option>
<option value="Diplomacy">Diplomacy</option>
<option value="Defense">Defense</option>
</InputSelect>
</p>
<p>
<label for="accommodation">Maximum Accommodation: </label>
<InputNumber id="accommodation"
@bind-Value="starship.MaximumAccommodation" />
</p>
<p>
<label for="valid">Engineering Approval: </label>
<InputCheckbox id="valid" @bind-Value="starship.IsValidatedDesign" />
</p>
<p>
<label for="productionDate">Production Date: </label>
<InputDate id="productionDate" @bind-Value="starship.ProductionDate" />
</p>
<button type="submit">Submit</button>
<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 {
private Starship starship = new Starship();
private void HandleValidSubmit()
{
Console.WriteLine("OnValidSubmit");
}
}
The EditForm
creates an EditContext
as a cascading value that tracks metadata about the edit process, including which fields have been modified and the current validation messages. The EditForm
also provides convenient events for valid and invalid submits (OnValidSubmit
, OnInvalidSubmit
). Alternatively, use OnSubmit
to trigger the validation and check field values with custom validation code.
InputText based on the input event
Use the InputText
component to create a custom component that uses the input
event instead of the change
event.
Create a component with the following markup, and use the component just as InputText
is used:
@inherits InputText
<input
@attributes="AdditionalAttributes"
class="@CssClass"
value="@CurrentValue"
@oninput="EventCallback.Factory.CreateBinder<string>(
this, __value => CurrentValueAsString = __value, CurrentValueAsString)" />
Validation support
The DataAnnotationsValidator
component attaches validation support using data annotations to the cascaded EditContext
. Enabling support for validation using data annotations requires this explicit gesture. To use a different validation system than data annotations, replace the DataAnnotationsValidator
with a custom implementation. The ASP.NET Core implementation is available for inspection in the reference source: DataAnnotationsValidator/AddDataAnnotationsValidation.
Blazor performs two types of validation:
- Field validation is performed when the user tabs out of a field. During field validation, the
DataAnnotationsValidator
component associates all reported validation results with the field. - Model validation is performed when the user submits the form. During model validation, the
DataAnnotationsValidator
component attempts to determine the field based on the member name that the validation result reports. Validation results that aren't associated with an individual member are associated with the model rather than a field.
Validation Summary and Validation Message components
The ValidationSummary
component summarizes all validation messages, which is similar to the Validation Summary Tag Helper:
<ValidationSummary />
Output validation messages for a specific model with the Model
parameter:
<ValidationSummary Model="@starship" />
The ValidationMessage
component displays validation messages for a specific field, which is similar to the Validation Message Tag Helper. Specify the field for validation with the For
attribute and a lambda expression naming the model property:
<ValidationMessage For="@(() => starship.MaximumAccommodation)" />
The ValidationMessage
and ValidationSummary
components support arbitrary attributes. Any attribute that doesn't match a component parameter is added to the generated <div>
or <ul>
element.
Custom validation attributes
To ensure that a validation result is correctly associated with a field when using a custom validation attribute, pass the validation context's MemberName when creating the ValidationResult:
using System;
using System.ComponentModel.DataAnnotations;
private class MyCustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
...
return new ValidationResult("Validation message to user.",
new[] { validationContext.MemberName });
}
}
Blazor data annotations validation package
The Microsoft.AspNetCore.Blazor.DataAnnotations.Validation is a package that fills validation experience gaps using the DataAnnotationsValidator
component. The package is currently experimental.
[CompareProperty] attribute
The CompareAttribute doesn't work well with the DataAnnotationsValidator
component. The Microsoft.AspNetCore.Blazor.DataAnnotations.Validation experimental package introduces an additional validation attribute, ComparePropertyAttribute
, that works around these limitations. In a Blazor app, [CompareProperty]
is a direct replacement for the [Compare]
attribute. For more information, see CompareAttribute ignored with OnValidSubmit EditForm (aspnet/AspNetCore #10643).
Nested models, collection types, and complex types
Blazor provides support for validating form input using data annotations with the built-in DataAnnotationsValidator
. However, the DataAnnotationsValidator
only validates top-level properties of the model bound to the form that aren't collection- or complex-type properties.
To validate the bound model's entire object graph, including collection- and complex-type properties, use the ObjectGraphDataAnnotationsValidator
provided by the experimental Microsoft.AspNetCore.Blazor.DataAnnotations.Validation package:
<EditForm Model="@model" OnValidSubmit="@HandleValidSubmit">
<ObjectGraphDataAnnotationsValidator />
...
</EditForm>
Annotate model properties with [ValidateComplexType]
. In the following model classes, the ShipDescription
class contains additional data annotations to validate when the model is bound to the form:
Starship.cs:
using System;
using System.ComponentModel.DataAnnotations;
public class Starship
{
...
[ValidateComplexType]
public ShipDescription ShipDescription { get; set; }
...
}
ShipDescription.cs:
using System;
using System.ComponentModel.DataAnnotations;
public class ShipDescription
{
[Required]
[StringLength(40, ErrorMessage = "Description too long (40 char).")]
public string ShortDescription { get; set; }
[Required]
[StringLength(240, ErrorMessage = "Description too long (240 char).")]
public string LongDescription { get; set; }
}
Validation of complex or collection type properties
Validation attributes applied to the properties of a model validate when the form is submitted. However, the properties of collections or complex data types of a model aren't validated on form submission by the DataAnnotationsValidator
component. To honor the nested validation attributes in this scenario, use a custom validation component. For an example, see the Blazor Validation sample (aspnet/samples).
Feedback
Loading feedback...