Skip to main content

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

DxCalendar

SelectedDate

SelectedDates

DxCheckBox

Checked

DxComboBox

Text

Value

DxDateEdit

Date

DxListBox

Values

DxMaskedInput

Value

DxMemo

Text

DxRadio

GroupValue

DxRadioGroup

Value

DxSpinEdit

Value

DxTagBox

Tags

Values

DxTextBox

Text

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

Valid Input

Invalid Input

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 Error or success 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:

  1. Create a ValidationAttribute class descendant.
  2. 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.
  3. Apply the AttributeUsage attribute to the class. Specify where and how your new custom attribute can be used.
  4. 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:

  1. Create a ComponentBase descendant (CustomValidation in the example below).
  2. Implement a property that reflects the Edit Form’s EditContext. Decorate it with the CascadingParameter attribute.
  3. In the OnInitialized method, create a ValidationMessageStore instance that stores a list of form errors.
  4. Implement the DisplayErrors method to receive validation errors and save them to a dictionary.
  5. Implement the ClearErrors method to clear the list of error messages.
  6. 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.

Validate Input in Data Editors

<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");
    }
}

Run Demo: Form Validation - Custom Form

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.

Validate Input in Form Layout

@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; }
    }
}

Run Demo: Form Validation - Form Layout

Data Editors Inside Another Component

To validate user input in a data editor that is placed in another Blazor component, follow the steps below:

  1. 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 creates MyComponent 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; }
    }
    
  2. Register the System.Linq.Expressions namespace in the _Imports.razor file to use the Expression class.

    @using System.Linq.Expressions
    
  3. 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>
    

Validate Editors in Another Component

See Also