Your search did not match any results.


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.

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

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.

Backend API
<form action="your-action" (submit)="onFormSubmit($event)"> <dx-form id="form" [formData]="customer" [readOnly]="false" [showColonAfterLabel]="true" [showValidationSummary]="true" validationGroup="customerData" > <dxi-item itemType="group" caption="Credentials"> <dxi-item dataField="Email"> <dxi-validation-rule type="required" message="Email is required"> </dxi-validation-rule> <dxi-validation-rule type="email" message="Email is invalid"> </dxi-validation-rule> <dxi-validation-rule type="async" message="Email is already registered" [validationCallback]="asyncValidation" > </dxi-validation-rule> </dxi-item> <dxi-item dataField="Password" [editorOptions]="passwordOptions"> <dxi-validation-rule type="required" message="Password is required"> </dxi-validation-rule> </dxi-item> <dxi-item name="ConfirmPassword" editorType="dxTextBox" [editorOptions]="confirmOptions" > <dxo-label text="Confirm Password"> </dxo-label> <dxi-validation-rule type="required" message="Confirm Password is required" > </dxi-validation-rule> <dxi-validation-rule type="compare" [comparisonTarget]="passwordComparison" message="Password and Confirm Password do not match" > </dxi-validation-rule> </dxi-item> </dxi-item> <dxi-item itemType="group" caption="Personal Data"> <dxi-item dataField="Name"> <dxi-validation-rule type="required" message="Name is required"> </dxi-validation-rule> <dxi-validation-rule type="pattern" [pattern]="namePattern" message="Do not use digits in the Name" > </dxi-validation-rule> </dxi-item> <dxi-item dataField="Date" editorType="dxDateBox" [editorOptions]="{ invalidDateMessage: 'The date must have the following format: MM/dd/yyyy' }" > <dxo-label text="Date of birth"> </dxo-label> <dxi-validation-rule type="required" message="Date of birth is required" > </dxi-validation-rule> <dxi-validation-rule type="range" [max]="maxDate" message="You must be at least 21 years old" > </dxi-validation-rule> </dxi-item> </dxi-item> <dxi-item itemType="group" caption="Billing address"> <dxi-item dataField="Country" editorType="dxSelectBox" [editorOptions]="{ dataSource: countries }" > <dxi-validation-rule type="required" message="Country is required"> </dxi-validation-rule> </dxi-item> <dxi-item dataField="City" editorType="dxAutocomplete" [editorOptions]="{ dataSource: cities, minSearchLength: 2 }" > <dxi-validation-rule type="pattern" [pattern]="cityPattern" message="Do not use digits in the City name" > </dxi-validation-rule> <dxi-validation-rule type="stringLength" [min]="2" message="Name must have at least 2 symbols" > </dxi-validation-rule> <dxi-validation-rule type="required" message="City is required"> </dxi-validation-rule> </dxi-item> <dxi-item dataField="Address"> <dxi-validation-rule type="required" message="Address is required"> </dxi-validation-rule> </dxi-item> <dxi-item dataField="Phone" helpText="Enter the phone number in USA phone format" [editorOptions]="{ mask: '+1 (X00) 000-0000', maskRules: phoneRules, maskInvalidMessage: 'The phone must have a correct USA phone format' }" > <dxi-validation-rule type="pattern" [pattern]="phonePattern" message="The phone must have a correct USA phone format" > </dxi-validation-rule> </dxi-item> <dxi-item itemType="simple" dataField="Accepted" editorType="dxCheckBox" [editorOptions]="{ text: 'I agree to the Terms and Conditions', value: false }" > <dxo-label [visible]="false"> </dxo-label> <dxi-validation-rule type="compare" [comparisonTarget]="checkComparison" message="You must agree to the Terms and Conditions" > </dxi-validation-rule> </dxi-item> </dxi-item> <dxi-item itemType="button" horizontalAlignment="left" [buttonOptions]="buttonOptions" > </dxi-item> </dx-form> </form>
import { NgModule, Component, ViewChild, enableProdMode, } from '@angular/core'; import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxCheckBoxModule, DxSelectBoxModule, DxNumberBoxModule, DxButtonModule, DxFormModule, DxAutocompleteModule, DxFormComponent, } from 'devextreme-angular'; import notify from 'devextreme/ui/notify'; import Validator from 'devextreme/ui/validator'; import { Customer, Service } from './app.service'; if (!/localhost/.test( { enableProdMode(); } const sendRequest = function (value) { const invalidEmail = ''; return new Promise((resolve) => { setTimeout(() => { resolve(value !== invalidEmail); }, 1000); }); }; @Component({ selector: 'demo-app', providers: [Service], templateUrl: 'app/app.component.html', styleUrls: ['app/app.component.css'], }) export class AppComponent { @ViewChild(DxFormComponent, { static: false }) form:DxFormComponent; passwordOptions: any = { mode: 'password', onValueChanged: () => { let editor = this.form.instance.getEditor('ConfirmPassword'); if (editor.option('value')) { let instance = Validator.getInstance(editor.element()) as Validator; instance.validate(); } }, buttons: [ { name: 'password', location: 'after', options: { icon: '../../../../images/icons/eye.png', type: 'default', onClick: () => this.changePasswordMode('Password'), }, }, ], }; confirmOptions: any = { mode: 'password', buttons: [ { name: 'password', location: 'after', options: { icon: '../../../../images/icons/eye.png', type: 'default', onClick: () => this.changePasswordMode('ConfirmPassword'), }, }, ], }; customer: Customer; countries: string[]; cities: string[]; maxDate: Date = new Date(); cityPattern = '^[^0-9]+$'; namePattern: any = /^[^0-9]+$/; phonePattern: any = /^[02-9]\d{9}$/; phoneRules: any = { X: /[02-9]/, }; buttonOptions: any = { text: 'Register', type: 'success', useSubmitBehavior: true, }; passwordComparison = () => this.form.instance.option('formData').Password; checkComparison() { return true; } changePasswordMode = (name) => { let editor = this.form.instance.getEditor(name); editor.option( 'mode', editor.option('mode') === 'text' ? 'password' : 'text', ); }; constructor(service: Service) { this.maxDate = new Date(this.maxDate.setFullYear(this.maxDate.getFullYear() - 21)); this.countries = service.getCountries(); this.cities = service.getCities(); this.customer = service.getCustomer(); } asyncValidation(params) { return sendRequest(params.value); } onFormSubmit = function (e) { notify({ message: 'You have submitted the form', position: { my: 'center top', at: 'center top', }, }, 'success', 3000); e.preventDefault(); }; } @NgModule({ imports: [ BrowserModule, BrowserTransferStateModule, DxCheckBoxModule, DxSelectBoxModule, DxNumberBoxModule, DxButtonModule, DxAutocompleteModule, DxFormModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
::ng-deep form { margin: 10px; }
import { Injectable } from '@angular/core'; export class Customer { Email: string; Password: string; Name: string; Date: Date; Country: string; City: string; Address: string; Phone: string; Accepted: boolean; } const customer : Customer = { Email: '', Password: '', Name: 'Peter', Date: null, Country: '', City: '', Address: '', Phone: '', Accepted: false, }; const countries: string[] = [ 'Afghanistan', 'Albania', 'Algeria', 'Andorra', 'Angola', 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Australia', 'Austria', 'Azerbaijan', 'The Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bhutan', 'Bolivia', 'Bosnia and Herzegovina', 'Botswana', 'Brazil', 'Brunei', 'Bulgaria', 'Burkina Faso', 'Burma', 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Central African Republic', 'Chad', 'Chile', 'China', 'Colombia', 'Comoros', 'Democratic Republic of the Congo', 'Republic of the Congo', 'Costa Rica', 'Ivory Coast', 'Croatia', 'Cuba', 'Cyprus', 'Czech Republic', 'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic', 'East Timor', 'Ecuador', 'Egypt', 'El Salvador', 'Equatorial Guinea', 'Eritrea', 'Estonia', 'Ethiopia', 'Fiji', 'Finland', 'France', 'Gabon', 'The Gambia', 'Georgia', 'Germany', 'Ghana', 'Greece', 'Grenada', 'Guatemala', 'Guinea', 'Guinea-Bissau', 'Guyana', 'Haiti', 'Honduras', 'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran', 'Iraq', 'Republic of Ireland', 'Israel', 'Italy', 'Jamaica', 'Japan', 'Jordan', 'Kazakhstan', 'Kenya', 'Kiribati', 'North Korea', 'South Korea', 'Kuwait', 'Kyrgyzstan', 'Laos', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Republic of Macedonia', 'Madagascar', 'Malawi', 'Malaysia', 'Maldives', 'Mali', 'Malta', 'Marshall Islands', 'Mauritania', 'Mauritius', 'Mexico', 'Federated States of Micronesia', 'Moldova', 'Monaco', 'Mongolia', 'Montenegro', 'Morocco', 'Mozambique', 'Namibia', 'Nauru', 'Nepal', 'Kingdom of the Netherlands', 'New Zealand', 'Nicaragua', 'Niger', 'Nigeria', 'Norway', 'Oman', 'Pakistan', 'Palau', 'State of Palestine', 'Panama', 'Papua New Guinea', 'Paraguay', 'Peru', 'Philippines', 'Poland', 'Portugal', 'Qatar', 'Romania', 'Russia', 'Rwanda', 'Saint Kitts and Nevis', 'Saint Lucia', 'Saint Vincent and the Grenadines', 'Samoa', 'San Marino', 'São Tomé and Príncipe', 'Saudi Arabia', 'Senegal', 'Serbia', 'Seychelles', 'Sierra Leone', 'Singapore', 'Slovakia', 'Slovenia', 'Solomon Islands', 'Somalia', 'South Africa', 'South Sudan', 'Spain', 'Sri Lanka', 'Sudan', 'Suriname', 'Swaziland', 'Sweden', 'Switzerland', 'Syria', 'Tajikistan', 'Tanzania', 'Thailand', 'Togo', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Tuvalu', 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'United States', 'Uruguay', 'Uzbekistan', 'Vanuatu', 'Vatican City', 'Venezuela', 'Vietnam', 'Yemen', 'Zambia', 'Zimbabwe']; const cities: string[] = [ 'New York', 'Los Angeles', 'Chicago', 'Houston', 'Philadelphia', 'Phoenix', 'San Antonio', 'San Diego', 'Dallas', 'San Jose', 'Austin', 'Indianapolis', 'Jacksonville', 'San Francisco', 'Columbus', 'Charlotte', 'Fort Worth', 'Detroit', 'El Paso', 'Memphis', 'Seattle', 'Denver', 'Washington', 'Boston', 'Nashville', 'Baltimore', 'Oklahoma City', 'Louisville', 'Portland', 'Las Vegas', 'Milwaukee', 'Albuquerque', 'Tucson', 'Fresno', 'Sacramento', 'Long Beach', 'Kansas City', 'Mesa', 'Virginia Beach', 'Atlanta', 'Colorado Springs', 'Omaha', 'Raleigh', 'Miami', 'Oakland', 'Minneapolis', 'Tulsa', 'Cleveland', 'Wichita', 'Arlington', 'New Orleans', 'Bakersfield', 'Tampa', 'Honolulu', 'Aurora', 'Anaheim', 'Santa Ana', 'St. Louis', 'Riverside', 'Corpus Christi', 'Lexington', 'Pittsburgh', 'Anchorage', 'Stockton', 'Cincinnati', 'Saint Paul', 'Toledo', 'Greensboro', 'Newark', 'Plano', 'Henderson', 'Lincoln', 'Buffalo', 'Jersey City', 'Chula Vista', 'Fort Wayne', 'Orlando', 'St. Petersburg', 'Chandler', 'Laredo', 'Norfolk', 'Durham', 'Madison', 'Lubbock', 'Irvine', 'Winston–Salem', 'Glendale', 'Garland', 'Hialeah', 'Reno', 'Chesapeake', 'Gilbert', 'Baton Rouge', 'Irving', 'Scottsdale', 'North Las Vegas', 'Fremont', 'Boise', 'Richmond']; @Injectable() export class Service { getCustomer() : Customer { return customer; } getCountries() { return countries; } getCities() { return cities; } }
// In real applications, you should not transpile code in the browser. // You can see how to create your own application with Angular and DevExtreme here: // window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, }, meta: { 'typescript': { 'exports': 'ts', }, 'devextreme/localization.js': { 'esModule': true, }, }, paths: { 'npm:': '', }, map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', '@angular/core': 'npm:@angular/core@12.2.17', '@angular/platform-browser': 'npm:@angular/platform-browser@12.2.17', '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic@12.2.17', '@angular/forms': 'npm:@angular/forms@12.2.17', '@angular/common': 'npm:@angular/common@12.2.17', '@angular/compiler': 'npm:@angular/compiler@12.2.17', 'tslib': 'npm:tslib@2.3.1/tslib.js', 'rxjs': 'npm:rxjs@7.5.3/dist/bundles/rxjs.umd.js', 'rxjs/operators': 'npm:rxjs@7.5.3/dist/cjs/operators/index.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@23.1.6/cjs', 'devextreme/bundles/dx.all': 'npm:devextreme@23.1.6/bundles/dx.all.js', 'jszip': 'npm:jszip@3.7.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.6.2/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.2', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.49', 'devextreme-angular': 'npm:devextreme-angular@23.1.6', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'app': { main: './app.component.ts', defaultExtension: 'ts', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, 'rxjs': { defaultExtension: 'js', }, 'rxjs/operators': { defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.12/inferno/package.json', 'npm:@angular/*/package.json', 'npm:@angular/common@12.2.17/*/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devextreme-angular@23.1.6/*/package.json', 'npm:devextreme-angular@23.1.6/ui/*/package.json', 'npm:devextreme-angular@23.1.6/package.json', 'npm:devexpress-diagram@2.2.2/package.json', 'npm:devexpress-gantt@4.1.49/package.json', ], }; System.config(window.config);
<!DOCTYPE html> <html xmlns=""> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="" /> <script src=""></script> <script src=""></script> <script src=""></script> <script src=""></script> <script src="config.js"></script> <script> System.import("app").catch(console.error.bind(console)); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> </body> </html>