Data Validation

This guide provides the detailed information on validation capabilities of DevExtreme editors. It describes how to validate a single editor or a group of editors, display the validation summary, perform remote validation, use a custom validation engine, etc.

View Demo Watch Video

Validate an Editor Value

Associate a DevExtreme editor with the Validator widget and specify validationRules to validate the editor. The full list of predefined validation rules is available in the Validation Rules Reference section.

jQuery
JavaScript
HTML
$(function () {
    $("#login").dxTextBox({
        placeholder: 'Login'
    }).dxValidator({
        validationRules: [{
            type: 'required'
        }, {
            type: 'pattern',
            pattern: '^[a-zA-Z]+$',
            message: 'Do not use digits.'
        }]
    });
});
<div id="login"></div>
Angular
app.component.html
app.component.ts
app.module.ts
<dx-text-box [(value)]="login" placeholder="Login">
    <dx-validator>
        <dxi-validation-rule
            type="required">
        </dxi-validation-rule>
        <dxi-validation-rule
            type="pattern"
            pattern="^[a-zA-Z]+$"
            message="Do not use digits.">
        </dxi-validation-rule>
    </dx-validator>
</dx-text-box>
import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    login: string;
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import { DxTextBoxModule, DxValidatorModule } from 'devextreme-angular';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        DxTextBoxModule,
        DxValidatorModule
    ],
    providers: [ ],
    bootstrap: [AppComponent]
})
export class AppModule { }
AngularJS
JavaScript
HTML
function Controller ($scope) {
    $scope.login = '';
    $scope.loginRules = [{
        type: 'required'
    }, {
        type: 'pattern',
        pattern: '^[a-zA-Z]+$',
        message: 'Do not use digits.'
    }];
}
<div dx-text-box="{ value: login, placeholder: 'Login' }",
    dx-validator="{ validationRules: loginRules }">   
</div>
Knockout
JavaScript
HTML
var viewModel = {
    login: ko.observable(""),
    loginRules: [{
        type: 'required'
    }, {
        type: 'pattern',
        pattern: '^[a-zA-Z]+$',
        message: 'Do not use digits.'
    }]
};
ko.applyBindings(viewModel);
<div data-bind="dxTextBox: { value: login, placeholder: 'Login' },
    dxValidator: { validationRules: loginRules }">
</div>
Vue
App.vue
<template>
    <dx-text-box :value.sync="login" placeholder="Login">
        <dx-validator>
            <dx-required-rule />
            <dx-pattern-rule pattern="^[a-zA-Z]+$" message="Do not use digits." />
        </dx-validator>
    </dx-text-box>
</template>

<script>
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import DxTextBox from 'devextreme-vue/text-box';
import DxValidator, {
    DxRequiredRule,
    DxPatternRule
} from 'devextreme-vue/validator';

export default {
    components: {
        DxTextBox,
        DxValidator,
        DxRequiredRule,
        DxPatternRule
    },
    data() {
        return {
            login: undefined
        }
    }
}
</script>
React
App.js
import React from 'react';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import TextBox from 'devextreme-react/text-box';
import Validator, {
    RequiredRule,
    PatternRule
} from 'devextreme-react/validator';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            login: undefined
        }
        this.setLogin = this.setLogin.bind(this);
    }

    setLogin(e) {
        this.setState({ login: e.value });
    }
    render() {
        return (
            <TextBox
                value={this.state.login}
                placeholder="Login"
                onValueChanged={this.setLogin}>
                <Validator>
                    <RequiredRule />
                    <PatternRule
                        pattern="^[a-zA-Z]+$"
                        message="Do not use digits."
                    />
                </Validator>
            </TextBox>
        );
    }
}
export default App;

Validate Several Editor Values

Group the Editors

Editors belonging to a single Validation Group can be validated together. All editors on a page are automatically collected in a Default Validation Group, which is suitable when you do not need to validate collections of editors separately. In other cases, define Validation Groups as shown in the following code:

jQuery
JavaScript
HTML
$(function () {
    var loginGroup = "loginGroup";
    $("#login")
        .dxTextBox({ /* ... */ })
        .dxValidator({
            // ...
            validationGroup: loginGroup
        }); 
    $("#password")
        .dxTextBox({ /* ... */ })
        .dxValidator({
            // ...
            validationGroup: loginGroup
        });
});
<div id="login"></div>
<div id="password"></div>
Angular
app.component.html
app.component.ts
app.module.ts
<dx-validation-group name="loginGroup">
    <dx-text-box [(value)]="login" placeholder="Login">
        <dx-validator>
            <!-- Login validation rules are configured here -->
        </dx-validator>
    </dx-text-box>
    <dx-text-box [(value)]="password" placeholder="Password">
        <dx-validator>
            <!-- Password validation rules are configured here -->
        </dx-validator>
    </dx-text-box>
</dx-validation-group>
import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    login: string;
    password: string;
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import { DxTextBoxModule, DxValidatorModule, DxValidationGroupModule } from 'devextreme-angular';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        DxTextBoxModule,
        DxValidatorModule,
        DxValidationGroupModule
    ],
    providers: [ ],
    bootstrap: [AppComponent]
})
export class AppModule { }
AngularJS
HTML
<div id="loginGroup" dx-validation-group="{ }">
    <div dx-text-box="{ value: login, ... }",
        dx-validator="{ ... }">
    </div>
    <div dx-text-box="{ value: password, ... }",
        dx-validator="{ ... }">   
    </div>
</div>
Knockout
HTML
<div id="loginGroup" data-bind="dxValidationGroup: { }">
    <div data-bind="dxTextBox: { value: login, ... },
        dxValidator: { ... }">
    </div>
    <div data-bind="dxTextBox: { value: password, ... },
        dxValidator: { ... }">   
    </div>
</div>
Vue
App.vue
<template>
    <dx-validation-group name="loginGroup">
        <dx-text-box :value.sync="login" placeholder="Login">
            <dx-validator>
                <!-- Login validation rules are configured here -->
            </dx-validator>
        </dx-text-box>
        <dx-text-box :value.sync="password" placeholder="Password">
            <dx-validator>
                <!-- Password validation rules are configured here -->
            </dx-validator>
        </dx-text-box>
    </dx-validation-group>
</template>

<script>
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import DxTextBox from 'devextreme-vue/text-box';
import DxValidator, {
    DxRequiredRule,
    DxPatternRule
} from 'devextreme-vue/validator';
import DxValidationGroup from 'devextreme-vue/validation-group';

export default {
    components: {
        DxTextBox,
        DxValidator,
        DxRequiredRule,
        DxPatternRule,
        DxValidationGroup
    },
    data() {
        return {
            login: undefined,
            password: undefined
        }
    }
}
</script>
React
App.js
import React from 'react';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import TextBox from 'devextreme-react/text-box';
import Validator, { 
    // Validation rule types are imported here
} from 'devextreme-react/validator';
import ValidationGroup from 'devextreme-react/validation-group';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            login: undefined,
            password: undefined
        }
        this.setLogin = this.setLogin.bind(this);
        this.setPassword = this.setPassword.bind(this);
    }

    setLogin(e) {
        this.setState({ login: e.value });
    }
    setPassword(e) {
        this.setState({ password: e.value });
    }
    render() {
        return (
            <ValidationGroup name="loginGroup">
                <TextBox
                    value={this.state.login}
                    placeholder="Login"
                    onValueChanged={this.setLogin}>
                    <Validator>
                        {/* Login validation rules are configured here */}
                    </Validator>
                </TextBox>
                <TextBox
                    value={this.state.password}
                    placeholder="Password"
                    onValueChanged={this.setPassword}>
                    <Validator>
                        {/* Password validation rules are configured here */}
                    </Validator>
                </TextBox>
            </ValidationGroup>
        );
    }
}
export default App;

Validate the Group

You can validate any group by calling its validate() method in a Button's onClick event handler. You can access the Validation Group via the handler's argument. The Button always validates the group to which it belongs. If the membership is not specified, the Button validates the Default Validation Group.

jQuery
JavaScript
HTML
$(function () {
    // var loginGroup = "loginGroup";
    $("#login")
        .dxTextBox({ /* ... */ })
        .dxValidator({
            // validationGroup: loginGroup,
            validationRules: [ /* ... */ ]
        });
    $("#password")
        .dxTextBox({ /* ... */ })
        .dxValidator({
            // validationGroup: loginGroup,
            validationRules: [ /* ... */ ]
        });
    $("#loginButton").dxButton({
        text: "Sign in",
        // validationGroup: loginGroup
        onClick: function (e) {
            var result = e.validationGroup.validate();
            if (result.isValid) {
                // ...
            }
        }
    });
});
<div id="login"></div>
<div id="password"></div>
<div id="loginButton"></div>
Angular
app.component.html
app.component.ts
app.module.ts
<!-- <dx-validation-group name="loginGroup"> -->
    <dx-text-box [(value)]="login">
        <dx-validator>
            <!-- Login validation rules are configured here -->
        </dx-validator>
    </dx-text-box>
    <dx-text-box [(value)]="password">
        <dx-validator>
            <!-- Password validation rules are configured here -->
        </dx-validator>
    </dx-text-box>
    <dx-button text="Sign in" (onClick)="signIn($event)"></dx-button>
<!-- </dx-validation-group> -->
import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    login: string;
    password: string;
    signIn(e) {
        let result = e.validationGroup.validate();
        if (result.isValid) {
            // Submit values to the server
        }
    }
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import {
    DxTextBoxModule,
    DxValidatorModule,
    // DxValidationGroupModule,
    DxButtonModule
} from 'devextreme-angular';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        DxTextBoxModule,
        DxValidatorModule,
        // DxValidationGroupModule,
        DxButtonModule
    ],
    providers: [ ],
    bootstrap: [AppComponent]
})
export class AppModule { }
AngularJS
HTML
JavaScript
<!-- div id="loginGroup" dx-validation-group="{ }" -->
    <div dx-text-box="{ value: login, ... }",
        dx-validator="{
            validationRules: [ ... ]
        }">
    </div>
    <div dx-text-box="{ value: password, ... }",
        dx-validator="{ 
            validationRules: [ ... ]
        }">   
    </div>
    <div dx-button="{ text: 'Login', onClick: loginClick }"></div>
<!-- /div -->
function Controller($scope) {
    // ...
    $scope.loginClick = function (e) {
        var result = e.validationGroup.validate();
        if (result.isValid) {
            // ...
        }
    };
}
Knockout
HTML
JavaScript
<!-- div id="loginGroup" data-bind="dxValidationGroup: { }" -->
    <div data-bind="dxTextBox: { value: login, ... },
        dxValidator: { 
            validationRules: [ ... ]
        }">
    </div>
    <div data-bind="dxTextBox: { value: password, ... },
        dxValidator: { 
            validationRules: [ ... ]
        }">   
    </div>
    <div data-bind="dxButton: { text: 'Login', onClick: loginClick }></div>
<!-- /div -->
var viewModel = {
    // ...
    loginClick: function (e) {
        var result = e.validationGroup.validate();
        if (result.isValid) {
            // ...
        }
    }
};
ko.applyBindings(viewModel);
Vue
App.vue
<template>
    <!-- <dx-validation-group name="loginGroup"> -->
        <dx-text-box :value.sync="login" placeholder="Login">
            <dx-validator>
                <!-- Login validation rules are configured here -->
            </dx-validator>
        </dx-text-box>
        <dx-text-box :value.sync="password" placeholder="Password">
            <dx-validator>
                <!-- Password validation rules are configured here -->
            </dx-validator>
        </dx-text-box>
        <dx-button text="Sign in" @click="signIn" />
    <!-- </dx-validation-group> -->
</template>

<script>
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import DxTextBox from 'devextreme-vue/text-box';
import DxValidator, {
    DxRequiredRule,
    DxPatternRule
} from 'devextreme-vue/validator';
// import DxValidationGroup from 'devextreme-vue/validation-group';
import DxButton from 'devextreme-vue/button';

export default {
    components: {
        DxTextBox,
        DxValidator,
        DxRequiredRule,
        DxPatternRule,
        // DxValidationGroup,
        DxButton
    },
    data() {
        return {
            login: undefined,
            password: undefined
        }
    },
    methods: {
        signIn(e) {
            let result = e.validationGroup.validate();
            if (result.isValid) {
                // Submit values to the server
            }
        }
    }
}
</script>
React
App.js
import React from 'react';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import TextBox from 'devextreme-react/text-box';
import Validator, { 
    // Validation rule types are imported here
} from 'devextreme-react/validator';
import ValidationGroup from 'devextreme-react/validation-group';
import Button from 'devextreme-react/button';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            login: undefined,
            password: undefined
        }
        this.setLogin = this.setLogin.bind(this);
        this.setPassword = this.setPassword.bind(this);
        this.signIn = this.signIn.bind(this);
    }

    setLogin(e) {
        this.setState({ login: e.value });
    }
    setPassword(e) {
        this.setState({ password: e.value });
    }

    signIn(e) {
        let result = e.validationGroup.validate();
        if (result.isValid) {
            // Submit values to the server
        }
    }

    render() {
        return (
            {/* <ValidationGroup name="loginGroup"> */}
                <TextBox
                    value={this.state.login}
                    placeholder="Login"
                    onValueChanged={this.setLogin}>
                    <Validator>
                        {/* Login validation rules are configured here */}
                    </Validator>
                </TextBox>
                <TextBox
                    value={this.state.password}
                    placeholder="Password"
                    onValueChanged={this.setPassword}>
                    <Validator>
                        {/* Password validation rules are configured here */}
                    </Validator>
                </TextBox>
                <Button
                    text="Sign in"
                    onClick={this.signIn}
                />
            {/* </ValidationGroup> */}
        );
    }
}
export default App;

Alternatively, you can validate a group using the DevExpress.validationEngine.validateGroup method. Call it without arguments to validate the Default Validation Group:

JavaScript
DevExpress.validationEngine.validateGroup();

... or pass the group instance to validate a named group:

JavaScript
DevExpress.validationEngine.validateGroup($("#loginGroup").dxValidationGroup("instance"));

Pass the group name instead of the instance if you have created widgets using jQuery.

JavaScript
DevExpress.validationEngine.validateGroup("loginGroup");

Display Validation Errors

All group validation errors can be displayed in the ValidationSummary widget. The following code shows how to add this widget to a page. The commented-out codelines associate the Validation Summary with a named Validation Group.

jQuery
JavaScript
HTML
$(function () {
    // var loginGroup = "loginGroup";
    $("#summary").dxValidationSummary({
        // validationGroup: loginGroup
    });
});
...
<div id="summary"></div>
Angular
app.component.html
app.module.ts
<!-- <dx-validation-group name="loginGroup"> -->
    ...
    <dx-validation-summary></dx-validation-summary>
<!-- </dx-validation-group> -->
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import {
    // ...
    // DxValidationGroupModule,
    DxValidationSummaryModule
} from 'devextreme-angular';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        // ...
        // DxValidationGroupModule,
        DxValidationSummaryModule
    ],
    providers: [ ],
    bootstrap: [AppComponent]
})
export class AppModule { }
AngularJS
HTML
<!-- div id="loginGroup" dx-validation-group="{ }" -->
    ...
    <div dx-validation-summary="{ }"></div>
<!-- /div -->
Knockout
HTML
<!-- div id="loginGroup" data-bind="dxValidationGroup: { }" -->
    <div data-bind="dxValidationSummary: { }"></div>
<!-- /div -->
Vue
App.vue
<template>
    <!-- <dx-validation-group name="loginGroup"> -->
        ...
        <dx-validation-summary />
    <!-- </dx-validation-group> -->
</template>

<script>
// ...
import DxValidationSummary from 'devextreme-vue/validation-summary';
// import DxValidationGroup from 'devextreme-vue/validation-group';

export default {
    components: {
        // ...
        // DxValidationGroup,
        DxValidationSummary
    },
    // ...
}
</script>
React
App.js
import React from 'react';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

// ...
// import ValidationGroup from 'devextreme-react/validation-group';
import ValidationSummary from 'devextreme-react/validation-summary';

class App extends React.Component {
    // ...
    render() {
        return (
            {/* <ValidationGroup name="loginGroup"> */}
                ...
                <ValidationSummary />
            {/* </ValidationGroup> */}
        );
    }
}
export default App;

Server-Side Validation

Use the "custom" validation rule that allows you to implement a custom validation function for server-side validation. In this function, perform an HTTP request and, when it succeeds, update the validation state and error message.

jQuery
JavaScript
HTML
$(function() {
    var validateLogin = function(params) {
        $.ajax({
            url: "https://www.example.com/services/validate-login",
            method: "POST",
            data: JSON.stringify({
                login: params.value
            }),
            dataType: "json",
            contentType: "application/json",
            success: function(result) {
                params.rule.isValid = result.Result;
                params.rule.message = result.Message;
                params.validator.validate();
            }
        })
        // Validation result until the response is received
        return false;
    };
    $("#login").dxTextBox({
        placeholder: "Login"
    }).dxValidator({
        validationRules: [{
            type: "required",
            message: "Login is required"
        }, {
            type: "custom",
            validationCallback: validateLogin
        }]
    });
});
<div id="login"></div>
Angular
app.component.ts
app.component.html
app.module.ts
import { Component } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

const requestConfig = {
    headers: new HttpHeaders({
        'Content-Type': 'application/json'
    })
}

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    login: string;

    constructor(private http:HttpClient) {
        this.validateLogin = this.validateLogin.bind(this);
    }

    validateLogin(params) {
        this.http.post(
            'https://www.example.com/services/validate-login',
            JSON.stringify({
                login: params.value
            }),
            requestConfig
        ).subscribe(response => {
            params.rule.isValid = response['result'];
            params.rule.message = response['message'];
            params.validator.validate();
        })
        return false;
    }
}
<dx-text-box 
    [(value)]="login"
    placeholder="Login">
    <dx-validator>
        <dxi-validation-rule
            type="required"
            message="Login is required">
        </dxi-validation-rule>
        <dxi-validation-rule
            type="custom"
            [validationCallback]="validateLogin">
        </dxi-validation-rule>
    </dx-validator>
</dx-text-box>
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import { DxTextBoxModule, DxValidatorModule } from 'devextreme-angular';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        DxTextBoxModule,
        DxValidatorModule
    ],
    providers: [ ],
    bootstrap: [AppComponent]
})
export class AppModule { }
AngularJS
JavaScript
HTML
angular.module('DemoApp', ['dx'])
    .controller('demoController', function ($scope) {
        $scope.validateLogin = function (params) {
            $.ajax({
                url: "http://www.example.com/services/validate-login",
                method: "POST",
                data: {
                    login: params.value
                },
                success: function (result) {
                    params.rule.isValid = result.Result;
                    params.rule.message = result.Message;
                    params.validator.validate();
                }
            })
            // Validation result until the response is recieved
            return false;
        };
        $scope.login = "";
        $scope.loginRules = [{
            type: 'required',
            message: 'Login is required'
        }, {
            type: "custom",
            validationCallback: validateLogin
        }];
    });
<div ng-controller="demoController">
    <div dx-text-box="{ placeholder: 'Login' bindingOptions: { value: 'login' } }",
        dx-validator="{ validationRules: loginRules }">   
    </div>
</div>
Knockout
JavaScript
HTML
var viewModel = {
    validateLogin: function (params) {
        $.ajax({
            url: "http://www.example.com/services/validate-login",
            method: "POST",
            data: {
                login: params.value
            },
            success: function (result) {
                params.rule.isValid = result.Result;
                params.rule.message = result.Message;
                params.validator.validate();
            }
        })
        // Validation result until the response is recieved
        return false;
    },
    login: ko.observable(""),
    loginRules: [{
        type: 'required',
        message: 'Login is required'
    }, {
        type: "custom",
        validationCallback: validateLogin
    }]
}
ko.applyBindings(viewModel);
<div data-bind="dxTextBox: { value: login, placeholder: 'Login' },
    dxValidator: { validationRules: loginRules }">
</div>
Vue

In this example, HTTP requests are performed using the axios library. To replicate the example in your application, install this library:

npm install axios
App.vue
<template>
    <dx-text-box
        :value.sync="login"
        placeholder="Login">
        <dx-validator>
            <dx-required-rule message="Login is required" />
            <dx-custom-rule :validation-callback="validateLogin" />
        </dx-validator>
    </dx-text-box>
</template>

<script>
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import DxTextBox from 'devextreme-vue/text-box';
import DxValidator, {
    DxRequiredRule,
    DxCustomRule
} from 'devextreme-vue/validator';
import axios from 'axios';

const requestConfig = {
    headers: {
        'Content-Type': 'application/json'
    }
}

export default {
    components: {
        DxTextBox,
        DxValidator,
        DxRequiredRule,
        DxCustomRule
    },
    data() {
        return {
            login: undefined
        };
    },
    methods: {
        validateLogin(params) {
            axios.post(
                'https://www.example.com/services/validate-login',
                JSON.stringify({
                    login: params.value
                }),
                requestConfig
            ).then(response => {
                params.rule.isValid = response.data['result'];
                params.rule.message = response.data['message'];
                params.validator.validate();
            })
            return false;
        }
    }
}
</script>
React

In this example, HTTP requests are performed using the axios library. To replicate the example in your application, install this library:

npm install axios
App.js
import React from 'react';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import TextBox from 'devextreme-react/text-box';
import Validator, {
    RequiredRule,
    CustomRule
} from 'devextreme-react/validator';
import axios from 'axios';

const requestConfig = {
    headers: {
        'Content-Type': 'application/json'
    }
}

class App extends React.Component {
    constructor(props) {
        super(props);
        this.login = undefined;
    }

    validateLogin(params) {
        axios.post(
            'https://www.example.com/services/validate-login',
            JSON.stringify({
                login: params.value
            }),
            requestConfig
        ).then(response => {
            params.rule.isValid = response.data['result'];
            params.rule.message = response.data['message'];
            params.validator.validate();
        })
        return false;
    }

    render() {
        return (
            <TextBox
                value={this.login}
                placeholder="Login">
                <Validator>
                    <RequiredRule message="Login is required" />
                    <CustomRule validationCallback={this.validateLogin} />
                </Validator>
            </TextBox>
        );
    }
}

Validate a Custom Value

You can use the DevExtreme validation engine to validate a custom value, for example, a non-DevExtreme editor value or a concatenation of several editor values, by configuring the Validator's adapter option. The following example creates two text boxes and a button. A button click checks that at least one of these text boxes is filled. Their values are provided by the getValue function.

jQuery
JavaScript
HTML
$(function() {
    var callbacks = [];
    var revalidate = function() {
        callbacks.forEach(func => {
            func();
        });
    }
    var phone = $("#phone").dxTextBox({
        placeholder: "Phone",
        onValueChanged: revalidate
    }).dxTextBox("instance");
    var email = $("#email").dxTextBox({
        type: "email",
        placeholder: "Email",
        onValueChanged: revalidate
    }).dxTextBox("instance");
    $("#validator").dxValidator({
        validationRules: [{
            type: "required",
            message: "Specify your phone or email."
        }],
        adapter: {
            getValue: function() {
                return phone.option("value") || email.option("value");
            },
            applyValidationResults: function(e) {
                $("#contacts").css({ "border": e.isValid ? "none" : "1px solid red" });
            },
            validationRequestsCallbacks: callbacks
        }
    });
    $("#button").dxButton({
        text: "Contact me",
        onClick: function(e) {
            var result = e.validationGroup.validate();
            if (result.isValid) {
                // Submit values to the server
            }
        }
    });
    $("#summary").dxValidationSummary({ });
});
<div id="contacts">
    <div id="phone"></div>
    <div id="email"></div>
</div>
<div id="validator"></div>
<div id="summary"></div>
<div id="button"></div>
Angular
app.component.html
app.component.ts
app.module.ts
<div id="contacts" [style.border]="borderStyle">
    <dx-text-box
        [(value)]="phone"
        placeholder="Phone"
        (onValueChanged)="revalidate()">
    </dx-text-box>
    <dx-text-box
        [(value)]="email"
        type="email"
        placeholder="Email"
        (onValueChanged)="revalidate()">
    </dx-text-box>
</div>
<dx-validator
    [adapter]="adapterConfig">
    <dxi-validation-rule
        type="required"
        message="Specify your phone or email.">
    </dxi-validation-rule>
</dx-validator>
<dx-validation-summary></dx-validation-summary>
<dx-button
    text="Contact me"
    (onClick)="submit($event)">
</dx-button>
import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    callbacks = [];
    phone: string;
    email: string;
    borderStyle: string = "none";
    adapterConfig = {
        getValue: () => {
            return this.phone || this.email;
        },
        applyValidationResults: (e) => {
            this.borderStyle = e.isValid ? "none" : "1px solid red";
        },
        validationRequestsCallbacks: this.callbacks
    };
    revalidate() {
        this.callbacks.forEach(func => {
            func();
        });
    };
    submit(e) {
        let result = e.validationGroup.validate();
        if (result.isValid) {
            // Submit values to the server
        }
    }
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import {
    DxTextBoxModule,
    DxValidatorModule,
    DxValidationSummaryModule,
    DxButtonModule
} from 'devextreme-angular';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        DxTextBoxModule,
        DxValidatorModule,
        DxValidationSummaryModule,
        DxButtonModule
    ],
    providers: [ ],
    bootstrap: [AppComponent]
})
export class AppModule { }
AngularJS
JavaScript
HTML
angular.module('DemoApp', ['dx'])
    .controller('demoController', function ($scope) {
        $scope.callbacks = [];
        $scope.phone = "";
        $scope.email = "";
        $scope.borderStyle = "none";
        $scope.rules = [{
            type: "required",
            message: "Specify your phone or email"
        }];
        $scope.adapterConfig = {
            getValue: function () {
                return $scope.phone + $scope.email;
            },
            applyValidationResults: function (e) {
                $scope.borderStyle = e.isValid ? "none" : "1px solid red";
            },
            validationRequestsCallbacks: $scope.callbacks
        };
        $scope.revalidate = function () {
            $scope.callbacks.forEach(func => {
                func();
            })
        };
        $scope.submit = function (e) {
            e.validationGroup.validate();
        }
    });
<div ng-controller="demoController">
    <div id="contacts" ngStyle="borderStyle">
        <div dx-text-box="{
            placeholder: 'Phone',
            onValueChanged: revalidate,
            bindingOptions: {
                value: 'phone'
            }
        }"></div>
        <div dx-text-box="{
            placeholder: 'Email',
            onValueChanged: revalidate,
            bindingOptions: {
                value: 'email'
            }
        }"></div>
        <div dx-validator="{
            validationRules: rules,
            adapter: adapterConfig
        }"></div>
        <div dx-validation-summary="{ }"></div>
        <div dx-button= "{
            text: 'Contact me',
            onClick: submit
        }"></div>
    </div>
</div>
Knockout
JavaScript
HTML
callbacks = [];
var viewModel = {
    phone: ko.observable(""),
    email: ko.observable(""),
    borderStyle: ko.observable("none"),
    rules: [{
        type: "required",
        message: "Specify your phone or email"
    }],
    adapterConfig: {
        getValue: function () {
            return viewModel.phone() + viewModel.email();
        },
        applyValidationResults: function (e) {
            viewModel.borderStyle(e.isValid ? "none" : "1px solid red");
        },
        validationRequestsCallbacks: callbacks
    },
    revalidate: function () {
        callbacks.forEach(func => {
            func();
        })
    },
    submit: function (e) {
        e.validationGroup.validate();
    }
}
ko.applyBindings(viewModel);
<div id="contacts" data-bind="style: { border: borderStyle }">
    <div data-bind="dxTextBox: {
        placeholder: 'Phone',
        onValueChanged: revalidate,
        value: phone
    }"></div>
    <div data-bind="dxTextBox: {
        placeholder: 'Email',
        onValueChanged: revalidate,
        value: email
    }"></div>
    <div data-bind="dxValidator: {
        validationRules: rules,
        adapter: adapterConfig
    }"></div>
    <div data-bind="dxValidationSummary: { }"></div>
    <div data-bind="dxButton:{
        text: 'Contact me',
        onClick: submit
    }"></div>
</div>

Use a Custom Validation Engine

Each DevExtreme editor allows changing its validation state and error message using the isValid and validationError options, which enables you to use any validation engine or custom validation logic. The following example shows a custom function called validateLogin validating a text box value:

jQuery
JavaScript
HTML
$(function() {
    var validateLogin = function (e) {
        if (!e.value) {
            e.component.option({
                validationError: { message: "Login is required" },
                isValid: false
            });
            return;
        }
        if (!e.value.match(/^[a-zA-Z0-9]+$/)) {
            e.component.option({
                validationError: { message: "Login can contain only numbers and letters" },
                isValid: false
            });
            return;
        }   
        e.component.option("isValid", true);
    }
    $("#login").dxTextBox({
        placeholder: "Login",
        onValueChanged: validateLogin
    })
});
<div id="login"></div>
Angular
app.component.html
app.component.ts
app.module.ts
<dx-text-box 
    [(value)]="login"
    placeholder="Login"
    [isValid]="isLoginValid"
    [validationError]="loginValidationError"
    (onValueChanged)="validateLogin()">
</dx-text-box>
import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    login: string;
    isLoginValid: boolean;
    loginValidationError: { message?: string };

    constructor() {
        this.isLoginValid = true;
        this.loginValidationError = {};
    }

    validateLogin() {
        if (!this.login) {
            this.loginValidationError = { message: "Login is required" };
            this.isLoginValid = false;
            return;
        }
        if (!this.login.match(/^[a-zA-Z0-9]+$/)) {
            this.loginValidationError = { message: "Login can contain only numbers and letters" };
            this.isLoginValid = false;
            return;
        }   
        this.isLoginValid = true;
    }
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import { DxTextBoxModule } from 'devextreme-angular';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        DxTextBoxModule
    ],
    providers: [ ],
    bootstrap: [AppComponent]
})
export class AppModule { }
AngularJS
JavaScript
HTML
angular.module('DemoApp', ['dx'])
    .controller('demoController', function ($scope) {
        $scope.login = "";
        $scope.isLoginValid = true;
        $scope.loginValidationError = {};
        $scope.validateLogin = function (params) {
            if ($scope.login === "") {
                $scope.loginValidationError = { message: "Login cannot be empty" };
                $scope.isLoginValid = false;
                return;
            }
            if (!$scope.login.match(/^[a-zA-Z0-9]+$/)) {
                $scope.loginValidationError = { message: "Login contains unexpected characters" };
                $scope.isLoginValid = false;
                return;
            }   
            $scope.isLoginValid = true;
        };
    });
<div ng-controller="demoController">
    <div dx-text-box="{
        placeholder: 'Login',
        onValueChanged: 'validateLogin($event)'
        bindingOptions: {
            value: 'login',
            isValid: 'isLoginValid',
            validationError: 'loginValidationError'
        }
    }"></div>
</div>
Knockout
JavaScript
HTML
var viewModel = {
    login: ko.observable(""),
    isLoginValid: ko.observable(true),
    loginValidationError: ko.observable({}),
    validateLogin: function (params) {
        if (this.login () === "") {
            this.loginValidationError({ message: "Login cannot be empty" });
            this.isLoginValid(false);
            return;
        }
        if (!this.login ().match(/^[a-zA-Z0-9]+$/)) {
            this.loginValidationError({ message: "Login contains unexpected characters" });
            this.isLoginValid(false);
            return;
        }   
        this.isLoginValid(true);
    }
}
ko.applyBindings(viewModel);
<div data-bind="dxTextBox: {
    value: login,
    placeholder: 'Login',
    onValueChanged: validateLogin
    isValid: isLoginValid,
    validationError: loginValidationError
}">
</div>
Vue
App.vue
<template>
    <dx-text-box
        :value.sync="login"
        placeholder="Login"
        :isValid="isLoginValid"
        :validation-error="loginValidationError"
        @value-changed="validateLogin">
    </dx-text-box>
</template>

<script>
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import DxTextBox from 'devextreme-vue/text-box';

export default {
    components: {
        DxTextBox
    },
    data() {
        return {
            login: undefined,
            isLoginValid: true,
            loginValidationError: {}
        }
    },
    methods: {
        validateLogin() {
            if (!this.login) {
                this.loginValidationError = { message: "Login is required" };
                this.isLoginValid = false;
                return;
            }
            if (!this.login.match(/^[a-zA-Z0-9]+$/)) {
                this.loginValidationError = { message: "Login can contain only numbers and letters" };
                this.isLoginValid = false;
                return;
            }   
            this.isLoginValid = true;
        }
    }
}
</script>
React
App.js
import React from 'react';

import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import TextBox from 'devextreme-react/text-box';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            login: undefined,
            isLoginValid: true,
            loginValidationError: {}
        }
        this.setLogin = this.setLogin.bind(this);
    }

    setLogin(e) {
        if (!e.value) {
            this.setState({
                login: e.value,
                isLoginValid: false,
                loginValidationError: { message: "Login is required" }
            });
            return;
        }
        if (!e.value.match(/^[a-zA-Z0-9]+$/)) {
            this.setState({
                login: e.value,
                isLoginValid: false,
                loginValidationError: { message: "Login can contain only numbers and letters" }
            });
            return;
        }   
        this.setState({
            login: e.value,
            isLoginValid: true,
            loginValidationError: {}
        });
    }

    render() {
        return (
            <TextBox
                value={this.state.login}
                placeholder="Login"
                isValid={this.state.isLoginValid}
                validationError={this.state.loginValidationError}
                onValueChanged={this.setLogin}
            />
        );
    }
}
export default App;

Knockout Only - Validate a View Model

Validating the view model object rather than the editors allows you to separate validation logic from the UI, reuse it between multiple views and unit-test the implementation. To validate a view model's observable, extend it with an object whose dxValidator field contains the validator configuration. After that, associate a target editor with the validator using the isValid and validationError options.

JavaScript
HTML
$(function () {
    var viewModel = {
        login: ko.observable("").extend({
            dxValidator: {
                validationRules: [{ type: 'required', message: 'Login is required' }]
            }
        }),
        password: ko.observable("").extend({
            dxValidator: {
                name: "Password",
                validationRules: [{ type: 'required' }]
            }
        })
    };
    ko.applyBindings(viewModel);
});
<div data-bind="dxTextBox: {
    value: login,
    placeholder: 'Login',
    isValid: login.dxValidator.isValid,
    validationError: login.dxValidator.validationError
}"></div>
<div data-bind="dxTextBox: {
    value: password,
    mode: 'password',
    placeholder: 'Password',
    isValid: password.dxValidator.isValid,
    validationError: password.dxValidator.validationError
}"></div>

Finally, pass the to the DevExpress.validationEngine.registerModelForValidation(model) method to register it in the validation engine. The registered view model can be validated at any point in your application by calling the DevExpress.validationEngine.validateModel(model) method.

JavaScript
var viewModel = {
    //...
    validateAndLogin: function (params) {
        var result = DevExpress.validationEngine.validateModel(this);
        if (result.isValid) {
            // ...
        }
    }
};

DevExpress.validationEngine.registerModelForValidation(viewModel);
ko.applyBindings(viewModel);