Validate Input
- 10 minutes to read
Use standard Blazor EditForm to validate data input. Inside the form, you can display a DevExpress Form Layout component or any DevExpress standalone data editor. The EditForm reads data annotation attributes defined in a model and indicates any errors.
The following table lists data editors and their properties you can validate in the EditForm
:
Data Editor | Property |
---|---|
Important
You should not rely on form validation alone to secure your Blazor-powered app. Form validation is designed to improve usability. A threat actor can bypass validation and send malicious data to the server. To minimize security related threats/risks, you must validate user input using multiple strategies. Refer to the following topic for more information: Validate User Input.
Standard Validation Mechanism
The EditForm
validates input values based on the edit context once a user attempts to submit this form. DevExpress Blazor Editors use the standard Blazor technique for validation and become marked with colored outlines: green indicates valid values, red - invalid values.
Valid Value | Invalid Value |
---|---|
For information on how Blazor validation works, refer to Microsoft documentation: Forms and validation.
The sections below describe how to organize validation for DevExpress Blazor Editors.
Set Up a Validation Model
Create a model and apply data annotation attributes to model fields.
using System.ComponentModel.DataAnnotations;
public class MyModel {
[Required]
[StringLength(10, ErrorMessage = "Name is too long.")]
public string Name { get; set; }
// ...
}
Declare EditForm markup and assign a model object to the EditForm’s Model property. The edit context is constructed based on this model. To supply the edit context explicitly, assign it to the EditContext property and do not specify the Model
property.
<EditForm Model="@model">
</EditForm>
@code {
private MyModel model = new MyModel();
// ...
}
Add a DataAnnotationsValidator component to enable validation based on annotation attributes.
<EditForm Model="@model">
<DataAnnotationsValidator />
</EditForm>
Set Up Components for Validation
Add standalone data editors or a Form Layout with data editors to the EditForm
. Use the @bind attribute to implement two-way binding between editor properties and model fields with data annotations. New input triggers edit context updates.
<EditForm Model="@model" Context="EditFormContext">
<DataAnnotationsValidator />
<DxFormLayout Context="FormLayoutContext">
<DxFormLayoutItem Caption="Name:" ColSpanMd="6" >
<DxTextBox @bind-Text="@model.Name" />
</DxFormLayoutItem >
</DxFormLayout>
@*...*@
</EditForm>
Display Validation Results
Notify a user whether the input is valid. To do this, you can do one of the following or both:
Use the ValidationMessage component to display messages for individual data editors or the ValidationSummary component to summarize validation messages.
<DxFormLayout Context="FormLayoutContext"> @*...*@ <DxFormLayoutItem ColSpanMd="12"> <ValidationSummary /> </DxFormLayoutItem> </DxFormLayout>
Use the ShowValidationIcon global option or an editor’s ShowValidationIcon property to specify whether editors should show validation icons - error or success .
<DxTextBox @bind-Text="@model.Name" ShowValidationIcon="true" />
Submit a Form
Add a DxButton or DxToolbarItem object to the markup and set the object’s SubmitFormOnClick
property to true
. The form is submitted when a user clicks this button or item.
<DxFormLayout Context="FormLayoutContext">
@*...*@
<DxFormLayoutItem>
<DxButton SubmitFormOnClick="true" Text="Submit" RenderStyle="@ButtonRenderStyle.Secondary" />
</DxFormLayoutItem>
</DxFormLayout>
If you want to respond to form submission, specify the EditForm
‘s OnValidSubmit and OnInvalidSubmit callbacks. They are triggered when the form passed and failed validation, respectively. For instance, you can post valid values to an underlying data source. Alternatively, you can specify the OnSubmit callback to check field values and trigger validation manually.
<EditForm Model="@model"
Context="EditFormContext"
OnValidSubmit="@HandleValidSubmit"
OnInvalidSubmit="@HandleInvalidSubmit">
@*...*@
</EditForm>
Custom Validation
You can implement custom validation logic if the standard validation methods do not meet your requirements.
Validation Attribute
Follow the steps below to create a custom validation attribute:
- Create a ValidationAttribute class descendant.
- Override the IsValid method to implement custom validation logic. This method has two overloads: one overload accepts only the object that should be validated; the other also accepts a ValidationContext object that stores additional information about the validation operation.
- Apply the AttributeUsageAttribute to the class. Specify where and how your new custom attribute can be used.
- Apply the custom attribute to a model field.
using System.ComponentModel.DataAnnotations;
public class Starship {
// ...
[DateInPastAttribute(ErrorMessage = "The Production Date value cannot be later than today.")]
public DateTime ProductionDate { get; set; }
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
AllowMultiple = false)]
public class DateInPastAttribute: ValidationAttribute {
public override bool IsValid(object value) {
return (DateTime)value <= DateTime.Today;
}
}
For more information, refer to Microsoft documentation: Custom attributes.
Validator Component
Follow the steps below to create a custom validation component:
- Create a ComponentBase descendant (
CustomValidation
in the example below). - Implement a property that reflects the Edit Form’s
EditContext
. Decorate it with the CascadingParameter attribute. - In the
OnInitialized
method, create a ValidationMessageStore instance that stores a list of form errors. - Implement the
DisplayErrors
method to receive validation errors and save them to a dictionary. - Implement the
ClearErrors
method to clear the list of error messages. - Place the
CustomValidation
component to the Edit Form.
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
namespace DxBlazorApplication1.Data {
public class CustomValidation : ComponentBase {
[CascadingParameter]
private EditContext? CurrentEditContext { get; set; }
private ValidationMessageStore? messageStore;
protected override void OnInitialized() {
messageStore = new(CurrentEditContext);
CurrentEditContext.OnValidationRequested += (s, e) => messageStore?.Clear();
CurrentEditContext.OnFieldChanged += (s, e) => messageStore?.Clear(e.FieldIdentifier);
}
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();
}
}
public void ClearErrors() {
messageStore?.Clear();
CurrentEditContext?.NotifyValidationStateChanged();
}
}
}
For more information, refer to Microsoft documentation: Validator components.
Examples
See sections below for different editor validation examples.
Standalone Data Editors
The following example validates user input in a standalone Text Box, Combo Box, Spin Edit, and Date Edit. The ValidationMessage component displays error messages for each editor.
<EditForm Model="@starship" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="@HandleInvalidSubmit">
<DataAnnotationsValidator />
<div class="row">
<div class="col-md-6" style="padding-bottom: 12px">
<label for="identifier">Identifier: </label>
<DxTextBox Id="identifier" @bind-Text="@starship.Identifier" />
<ValidationMessage For="@(() => starship.Identifier)" />
</div>
<div class="col-md-6" style="padding-bottom: 12px">
<label for="classification">Primary Classification: </label>
<DxComboBox Id="classification" NullText="Select classification ..."
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto"
Data="@(new List<Classification>() { new Classification(1, "Defense"),
new Classification(2, "Exploration"),
new Classification(3, "Diplomacy") })"
TextFieldName="Value"
ValueFieldName="Id"
@bind-Value="@starship.Classification" />
<ValidationMessage For="@(() => starship.Classification)" />
</div>
<div class="col-md-6" style="padding-bottom: 12px">
<label for="accommodation">Maximum Accommodation: </label>
<DxSpinEdit Id="accommodation" @bind-Value="@starship.MaximumAccommodation" />
<ValidationMessage For="@(() => starship.MaximumAccommodation)" />
</div>
<div class="col-md-6" style="padding-bottom: 12px">
<label for="productionDate">Production Date: </label>
<DxDateEdit @bind-Date="@starship.ProductionDate" />
<ValidationMessage For="@(() => starship.ProductionDate)" />
</div>
<div class="col-md-12">
<DxButton SubmitFormOnClick="true" Text="Submit" RenderStyle="@ButtonRenderStyle.Secondary" />
</div>
</div>
</EditForm>
@code {
private Starship starship = new Starship();
private void HandleValidSubmit() {
Console.WriteLine("OnValidSubmit");
}
private void HandleInvalidSubmit() {
Console.WriteLine("OnInvalidSubmit");
}
}
Form Layout
The following example validates user input in the Form Layout component with four layout items. These items contain the following data editors: Text Box, Combo Box, Spin Edit, and Date Edit.
One layout item contains the ValidationSummary component that displays all the error messages. The last layout item contains the Button that submits the form.
@using System.ComponentModel.DataAnnotations
<div class="cw-880">
<EditForm Model="@starship"
OnValidSubmit="@HandleValidSubmit"
OnInvalidSubmit="@HandleInvalidSubmit"
Context="EditFormContext">
<DataAnnotationsValidator/>
<DxFormLayout>
<DxFormLayoutItem Caption="Identifier:" ColSpanMd="6">
<DxTextBox @bind-Text="@starship.Identifier" ShowValidationIcon="true"/>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Primary Classification:" ColSpanMd="6">
<DxComboBox NullText="Select classification ..."
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto"
Data="classifications"
@bind-Value="@starship.Classification"
ShowValidationIcon="true"/>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Maximum Accommodation:"
ColSpanMd="6">
<DxSpinEdit Id="accommodation" ShowValidationIcon="true"
@bind-Value="@starship.MaximumAccommodation"/>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Production Date:"
ColSpanMd="6">
<DxDateEdit @bind-Date="@starship.ProductionDate" ShowValidationIcon="true"/>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Description:"
ColSpanMd="12">
<DxMemo @bind-Text="@starship.Description" ShowValidationIcon="true"/>
</DxFormLayoutItem>
<DxFormLayoutItem ColSpanMd="12">
<DxButton SubmitFormOnClick="true"
Text="Submit"
RenderStyle="ButtonRenderStyle.Secondary"/>
</DxFormLayoutItem>
<DxFormLayoutItem ColSpanMd="12">
<ValidationSummary/>
</DxFormLayoutItem>
</DxFormLayout>
<div class="row w-100 mx-0">
<p class="demo-text col-12 mt-2">
Form Validation State: <b>@FormValidationState</b>
</p>
</div>
</EditForm>
</div>
@code {
string FormValidationState = @"Press the ""Submit"" button to validate the form.";
Starship starship = new Starship() { ProductionDate = DateTime.Now + TimeSpan.FromDays(1) };
List<string> classifications = new List<string>() { "Defense", "Exploration", "Diplomacy" };
void HandleValidSubmit() {
FormValidationState = @"Form data is valid";
}
void HandleInvalidSubmit() {
FormValidationState = @"Form data is invalid";
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class DateInPastAttribute : ValidationAttribute {
public override bool IsValid(object value) {
return (DateTime)value <= DateTime.Today;
}
}
public class Starship {
[Required(ErrorMessage = "The Identifier value should be specified.")]
[StringLength(16, ErrorMessage = "The Identifier exceeds 16 characters.")]
public string Identifier { get; set; }
[Required(ErrorMessage = "The Primary Classification value should be specified.")]
public string Classification { get; set; }
[Range(1, 100000, ErrorMessage = "The Maximum Accommodation value should be a number between 1 and 100,000.")]
public int MaximumAccommodation { get; set; }
[Required]
[DateInPastAttribute(ErrorMessage = "The Production Date value cannot be later than today.")]
public DateTime ProductionDate { get; set; }
[Required(ErrorMessage = "The Description should be specified.")]
public string Description { get; set; }
}
}
Data Editors Inside Another Component
To validate user input in a data editor that is placed in another Blazor component, follow the steps below:
Create a custom Blazor component and add a data editor. Define parameters that are passed to the editor’s
<PropertyName>
and<PropertyName>Expression
properties and handle the<PropertyName>Changed
event as shown below. The following code snippet createsMyComponent
with a Text Box:<DxTextBox Text="@MyValue" TextChanged="@((v) => MyValue = v)" TextExpression="@MyValueExpression" /> @code { private string _value; [Parameter] public string MyValue { get => _value; set { if (_value == value) return; _value = value; MyValueChanged.InvokeAsync(value); } } [Parameter] public EventCallback<string> MyValueChanged { get; set; } [Parameter] public Expression<Func<string>> MyValueExpression { get; set; } }
Register the System.Linq.Expressions namespace in the
_Imports.razor
file to use the Expression class.@using System.Linq.Expressions
Apply data annotation attributes to model fields. Add
MyComponent
to EditForm.<EditForm Model="@customer" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="@HandleInvalidSubmit" Context="EditFormContext"> <DataAnnotationsValidator /> <div class="container"> <div class="col"> <div class="row"><h5>@nameof(customer.FirstName)</h5></div> <div class="row"> <div class="form-group"> <MyComponent @bind-MyValue="@customer.FirstName" /> <ValidationMessage For="@(() => customer.FirstName)" /> </div> </div> </div> <div class="col"> <div class="row"><h5>@nameof(customer.LastName)</h5></div> <div class="row"> <div class="form-group"> <MyComponent @bind-MyValue="@customer.LastName" /> <ValidationMessage For="@(() => customer.LastName)" /> </div> </div> </div> <div class="row"> <div class="col"> <div class="form-group "> <DxButton SubmitFormOnClick="true" Text="Submit" RenderStyle="@ButtonRenderStyle.Primary" /> </div> </div> </div> </div> </EditForm>