Your search did not match any results.

Form - Validation

This demo shows how to validate Form editors. To apply validation rules to an editor, declare them in the validationRules array. Specify type and other properties for each rule.

Backend API
@using DevExtreme.NETCore.Demos.ViewModels @using(Html.BeginForm()) { using(Html.DevExtreme().ValidationGroup()) { @Html.AntiForgeryToken() @(Html.DevExtreme().Form<EditorsViewModel>() .OnInitialized("onInitialized") .OnOptionChanged("onOptionChanged") .ShowValidationSummary(true) .Items(items => { items.AddGroup() .Caption("Credentials") .Items(groupItems => { groupItems.AddSimpleFor(m => m.Email) .Editor(e => e.TextBox() .ValueChangeEvent("keyup") ); groupItems.AddSimpleFor(m => m.Password) .Editor(e => e.TextBox() .Mode(TextBoxMode.Password) .InputAttr("aria-label", "Password") .OnValueChanged("passwordChanged") .ValueChangeEvent("keyup") .Buttons(buttons => { buttons.Add() .Name("password") .Location(TextEditorButtonLocation.After) .Widget(w => w.Button() .Type(ButtonType.Default) .Icon("eyeopen") .StylingMode(ButtonStylingMode.Text) .OnClick("() => changePasswordMode('Password')") ); }) ); groupItems.AddSimpleFor(m => m.ConfirmPassword) .Editor(e => e.TextBox() .Mode(TextBoxMode.Password) .InputAttr("aria-label", "Password") .ValueChangeEvent("keyup") .Buttons(buttons => { buttons.Add() .Name("password") .Location(TextEditorButtonLocation.After) .Widget(w => w.Button() .Type(ButtonType.Default) .Icon("eyeopen") .StylingMode(ButtonStylingMode.Text) .OnClick("() => changePasswordMode('ConfirmPassword')") ); }) ); }); items.AddGroup() .Caption("Personal Data") .Items(groupItems => { groupItems.AddSimpleFor(m => m.Name) .Editor(e => e.TextBox() .ValueChangeEvent("keyup") ); groupItems.AddSimpleFor(m => m.Date) .Editor(e => e .DateBox() .OpenOnFieldClick(true) .Placeholder("Birth Date") .AcceptCustomValue(false) ); groupItems.AddSimpleFor(m => m.VacationDates) .Editor(e => e .DateRangeBox() .StartDatePlaceholder("Start Date") .AcceptCustomValue(false) .EndDatePlaceholder("End Date") ) .ValidationRules(validationRule => { validationRule.AddCustom() .Message("The vacation period must not exceed 25 days") .ValidationCallback("validateVacationDatesRange"); validationRule.AddCustom() .Message("Both start and end dates must be selected") .ValidationCallback("validateVacationDatesPresence"); }); }); items.AddGroup() .Caption("Billing address") .Items(groupItems => { groupItems.AddSimpleFor(m => m.Country) .Editor(e => e .SelectBox() .InputAttr("aria-label", "Country") .DataSource(d => d.Mvc().Controller("GeoNames").LoadAction("Countries")) ); groupItems.AddSimpleFor(m => m.City) .Editor(e => e .Autocomplete() .ValueChangeEvent("keyup") .MinSearchLength(2) .DataSource(d => d.Mvc().Controller("GeoNames").LoadAction("Cities")) ); groupItems.AddSimpleFor(m => m.Address) .Editor(e => e.TextBox() .ValueChangeEvent("keyup") ); groupItems.AddSimpleFor(m => m.Phone) .HelpText("Enter the phone number in USA phone format") .Editor(e => e.TextBox() .Mask("+1 (X00) 000-0000") .InputAttr("aria-label", "Phone") .ValueChangeEvent("keyup") .MaskRules(new { X = new JS("/[02-9]/") }) .MaskInvalidMessage("The phone must have a correct USA phone format") ); }); items.AddGroup() .CssClass("last-group") .ColCountByScreen(c => c.Md(2).Sm(2).Lg(2)) .Items(groupItems => { groupItems.AddSimpleFor(m => m.Accepted) .Label(l => l.Visible(false)) .Editor(editor => editor.CheckBox() .Width(270) .Text("I agree to the Terms and Conditions") ); groupItems.AddGroup() .ColCountByScreen(c => c.Md(2).Sm(2).Lg(2)) .CssClass("buttons-group") .Items(secondGroupItems => { secondGroupItems.AddButton().Name("Reset") .ButtonOptions(b => b.Text("Reset") .Icon(Url.Content("refresh")) .Width(120) .Disabled(true) .OnClick("onResetButtonClick") ); secondGroupItems.AddButton() .ButtonOptions(b => b.Text("Register") .Type(ButtonType.Default) .Width(120) .UseSubmitBehavior(true) ); }); }); }) .FormData(Model) ) } } <script> let formInstance; function onInitialized(e) { formInstance = e.component; } function onOptionChanged(e) { if(e.name === 'isDirty') { const resetButton = formInstance.getButton('Reset'); resetButton.option('disabled', !e.value); } } function onResetButtonClick(e) { formInstance.reset(); } function passwordChanged(e) { const editor = formInstance.getEditor('ConfirmPassword'); if (editor.option('value')) { editor.element().dxValidator('validate'); } } function changePasswordMode(name) { let editor = formInstance.getEditor(name); editor.option('mode', editor.option('mode') === 'text' ? 'password' : 'text'); } function validateVacationDatesRange({ value }) { const [startDate, endDate] = value; if (startDate === null || endDate === null) { return true; } const millisecondsPerDay = 24 * 60 * 60 * 1000; const daysDifference = Math.abs((endDate - startDate) / millisecondsPerDay); return daysDifference < 25; } function validateVacationDatesPresence({ value }) { const [startDate, endDate] = value; if (startDate === null && endDate === null) { return true; } return startDate !== null && endDate !== null; } </script>
<p>The submitted data has been successfully accepted.</p> <br /> @(Html.DevExtreme().Button() .Text("Reload demo") .Type(ButtonType.Default) .Icon("refresh") .OnClick(@<text> function() { window.location = window.location; } </text>) )
using DevExtreme.AspNet.Data; using DevExtreme.AspNet.Mvc; using DevExtreme.NETCore.Demos.Models.SampleData; using DevExtreme.NETCore.Demos.ViewModels; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Linq; namespace DevExtreme.NETCore.Demos.Controllers { public class FormController : Controller { [HttpGet] public ActionResult Validation() { return View(new EditorsViewModel() { Name = "Peter" }); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Validation(EditorsViewModel userInfo) { if(ModelState.IsValid) { return View("SuccessValidation"); } return View(userInfo); } } }
using DevExtreme.AspNet.Data; using DevExtreme.AspNet.Mvc; using DevExtreme.NETCore.Demos.Models.SampleData; using Microsoft.AspNetCore.Mvc; using System; using System.Linq; using System.Net.Http; namespace DevExtreme.NETCore.Demos.Controllers.ApiControllers { [Route("api/[controller]/[action]")] public class GeoNamesController : Controller { [HttpGet] public object Countries(DataSourceLoadOptions loadOptions) { return DataSourceLoader.Load(SampleData.Countries, loadOptions); } [HttpGet] public object Cities(DataSourceLoadOptions loadOptions) { return DataSourceLoader.Load(SampleData.Cities, loadOptions); } } }
using System; using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using DevExtreme.NETCore.Demos.Models.DataGrid; namespace DevExtreme.NETCore.Demos.Controllers { public class RemoteValidationController : Controller { InMemoryEmployeesValidationDataContext _db; public RemoteValidationController(IHttpContextAccessor httpContextAccessor, IMemoryCache memoryCache) { _db = new InMemoryEmployeesValidationDataContext(httpContextAccessor, memoryCache); } [HttpPost] public JsonResult CheckUniqueEmailAddress(EmployeeValidation model) { var isValid = !_db.Employees.Any(emp => { var equals = string.Equals(emp.Email, model.Email, StringComparison.OrdinalIgnoreCase); return model.ID != emp.ID && equals; }); return Json(isValid); } [HttpPost] public JsonResult CheckEmailAddress(string email) { var isValid = !string.Equals(email, "test@dx-email.com", StringComparison.OrdinalIgnoreCase); return Json(isValid); } } }
using DevExtreme.AspNet.Mvc; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; namespace DevExtreme.NETCore.Demos.ViewModels { public class EditorsViewModel { [Required(ErrorMessage = "Email is required")] [RegularExpression(@"^[\d\w._-]+@[\d\w._-]+\.[\w]+$", ErrorMessage = "Email is invalid")] [Remote("CheckEmailAddress", "RemoteValidation", ErrorMessage = "Email is already registered", HttpMethod = "POST")] public string Email { get; set; } = string.Empty; [Required(ErrorMessage = "Name is required")] [RegularExpression(@"^[^0-9]+$", ErrorMessage = "Do not use digits in the Name.")] [StringLength(int.MaxValue, MinimumLength = 2, ErrorMessage = "Name must have at least 2 symbols")] public string Name { get; set; } = string.Empty; [Required(ErrorMessage = "Password is required")] public string Password { get; set; } = string.Empty; [Required(ErrorMessage = "Confirm Password is required")] [System.ComponentModel.DataAnnotations.Compare("Password", ErrorMessage = "'Password' and 'Confirm Password' do not match.")] public string ConfirmPassword { get; set; } = string.Empty; [RegularExpression(@"^[02-9]\d{9}$", ErrorMessage = "The phone must have a correct USA phone format")] public string Phone { get; set; } = string.Empty; public string Extension { get; set; } [Required(ErrorMessage = "Country is required")] public string Country { get; set; } [Required(ErrorMessage = "Address is required")] public string Address { get; set; } = string.Empty; public string Description { get; set; } public int Age { get; set; } public string Drink { get; set; } [Required(ErrorMessage = "City is required")] [RegularExpression("^[^0-9]+$", ErrorMessage = "Do not use digits in the City name.")] [StringLength(int.MaxValue, MinimumLength = 2, ErrorMessage = "City must have at least 2 symbols")] public string City { get; set; } public IEnumerable<string> Colors { get; set; } public IEnumerable<string> SelectedColors { get; set; } public string Color { get; set; } [Display(Name = "Date of birth")] [Required(ErrorMessage = "Date of birth is required")] [VerifyAge(21, ErrorMessage = "You must be at least {1} years old")] public DateTime? Date { get; set; } [VerifyDateRange(25, ErrorMessage = "The vacation period must not exceed {1} days")] public DateTime?[] VacationDates { get; set; } [DevExtremeRequired(ErrorMessage = "You must agree to the Terms and Conditions")] public bool Accepted { get; set; } } }
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; namespace DevExtreme.NETCore.Demos.ViewModels { public class VerifyAgeAttribute : ValidationAttribute, IClientModelValidator { public VerifyAgeAttribute(int age) { Age = age; } public int Age { get; private set; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { if((DateTime?)value <= DateTime.Now.AddYears(-Age)) { return ValidationResult.Success; } return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); } void IClientModelValidator.AddValidation(ClientModelValidationContext context) { context.Attributes.Add("data-val-custom-verifyage", FormatErrorMessage(context.ModelMetadata.GetDisplayName())); context.Attributes.Add( "data-val-custom-verifyage-validationcallback", $@"function(options) {{ var now = new Date(); return options.value && options.value <= now.setFullYear(now.getFullYear() - {Age}); }}"); } public override string FormatErrorMessage(string name) { return string.Format(ErrorMessageString, name, Age); } } }
form { margin: 10px 10px 15px; } .last-group { margin-top: 30px; margin-bottom: 10px; } .last-group .dx-item-content { align-items: start; justify-content: center; } .last-group .dx-field-item { padding: 0 !important; } .buttons-group { display: flex; width: 100%; justify-content: end; } .buttons-group .dx-item-content { gap: 10px; }

The following validation rules are shown in this demo:

  • RequiredRule
    Requires that a validated editor has a value.

  • CompareRule
    Compares the editor's value to the specified expression.

  • PatternRule
    Checks whether an editor value matches a specified pattern.

  • RangeRule
    Checks whether an editor value is in a specified range.

  • StringLengthRule
    Requires that an editor value length is in a specified range.

  • EmailRule
    Requires that an editor value matches the Email pattern.

  • AsyncRule
    Allows you to add custom server-side validation logic. Rules of this type run last, only if all other rules passed. In this demo, an AsyncRule checks whether user input matches test@dx-email.com.

  • CustomRule
    A rule with custom validation logic.

To submit form data, do the following:

  1. Wrap the Form component in the HTML <form> element.

  2. Use the Button Form Item to add a button to the form. This button submits the form data.

  3. Enable the button's useSubmitBehavior property.

When users click the button, the Form validates all editors that belong to the same validationGroup as this button. In this demo, all these editors belong to the customerData group. Form data can be submitted to a server only if input validation is successful. Enable the showValidationSummary property to display all validation errors at the bottom of the Form.

This demo also shows an implementation of the Reset button. When users click the button, it resets all Form data to initial values. The Form also uses the isDirty flag to check whether changes are made, and if not, disables the button.