Validation - MVVM Approach

End-user data entry errors are common and it's the developer's responsibility to prevent associated issues. DevExtreme includes a powerful and flexible validation engine and a number of readily usable validation rules. You can choose from standard restrictions (such as "required","stringLength", etc.) or implement your own logic with any level of detail, including remote validation.

Like the rest in DevExtreme, data validation is consistent across all of our shipping themes.

The DevExtreme validation engine comes with a powerful and straightforward API. This enables you to check validation rules at any points. In addition, you are free to use a custom validation engine but indicate an invalid validation result in a UI using the DevExtreme mechanisms. And vise versa, you can use the DevExtreme validation engine, but indicate an invalid validation result in a custom manner.

Read below to learn how to perform the tasks that can be performed using the DevExtreme Validation.

Similar article is available for jQuery approach.

View Demo

Watch Video

Associate Editor with Validator

To validate an editor value, associate either the UI editor or the underlying view or application model field with the dxValidator widget.

To validate a DevExtreme editor, the latter should be associated with the dxValidator widget.

Editor-Validator Association

The dxValidator widget validates the editor's value against a predefined set of rules. To define the rules to be checked, assign an array of rules to the validator's validationRules configuration option.

HTML
var validatorOptions = { 
    validationRules: [
        {
            type: 'required',
            message: 'login is required'
        }, {
            type: 'pattern',
            pattern: '^[a-zA-Z]+$',
            message: 'Do not use digits.'
        }
    ]
}

As you can see, validation rules have the type, message and type-specific fields. The type field specifies the rule type, the message field specifies the message to be displayed if a rule is not satisfied. Rule type specific fields are required to specify additional conditions for validation. You can see a full structure of each predefined rule type in the Validation Rules Reference section.

NOTE: The message rule field is not required to be specified. There are default messages for each rule type. Moreover, you can personalize the default messages by setting the name of the validated editor for the validator's name configuration option.

To associate an editor with a validator, apply the corresponding validator attribute to the element that is bound to the required editor (see the code below).

Angular Approach
HTML
<div dx-text-box="{ value: login, placeholder: 'Login' }",
     dx-validator="{ validationRules: [{
            type: 'required'
        }, {
            type: 'pattern',
            pattern: '^[a-zA-Z]+$',
            message: 'Do not use digits.'
        }] }">   
</div>
Knockout Approach
HTML
<div data-bind="dxTextBox: { value: login, placeholder: 'Login' },
     dxValidator: { validationRules: [{
            type: 'required'
        }, {
            type: 'pattern',
            pattern: '^[a-zA-Z]+$',
            message: 'Do not use digits.'
        }] }">   
</div>

Validate Editor Values

The editors that are associated with the dxValidator widget are automatically validated on the event specified by their valueChangeEvent option. You can also validate several editors at once. For this purpose, there are validation methods that validate a validation group. So, you should associate editors with validation groups.

Validation Group

Define Validation Groups

NOTE: Nested validation groups are not supported.

Add the editors that should be validated together to the div element of the dxValidationGroup widget.

NOTE: If the editors that belong to a single view or application model should be validated together, you do not have to introduce a validation group for them. Editors from one view or application that are not associated with a validation group explicitly are combined to a default validation group.

Angular Approach
HTML
<div id="sampleGroup" dx-validation-group="{}">
    <div dx-text-box="{ value: login, placeholder: 'Login' }",
         dx-validator="{ validationRules: [{
                type: 'required'
            }, {
                type: 'pattern',
                pattern: '^[a-zA-Z]+$',
                message: 'Do not use digits.'
            }] }">   
    </div>
    <div dx-text-box="{ value: password, mode: 'password' }",
         dx-validator="{ validationRules: [{
            type: 'required,
            message: 'Password is required'
        }] }">   
    </div>
</div>
Knockout Approach
HTML
<div id="sampleGroup" data-bind="dxValidationGroup : {}" >
    <div data-bind="dxTextBox: { value: login, placeholder: 'Login' },
        dxValidator: { validationRules: [{
            type: 'required'
        }, {
            type: 'pattern',
            pattern: '^[a-zA-Z]+$',
            message: 'Do not use digits.'
        }] }"> 
    </div> 
    <div data-bind="dxTextBox: { value: password, mode: 'password' },
        dxValidator: { validationRules: [{
            type: 'required',
            message: 'Password is required'
        }] }"> 
    </div>  
</div>

Validate Groups

To validate a group, use one of the following approaches.

  • At a button's click event
    Associate a dxButton widget with the required validation group. In this instance, the associated group will come in the validationGroup field of the parameter passed to a button's click event handler. Call the validate() method of the button's validation group.

    To associate the dxButton widget with a validation group, create the dxValidationGroup widget and add the dxButton widget inside of the element representing the validation group.

    NOTE: If you are going to use a button to validate the view or application model's validation group, do not associate the button with a validation group. The validation group will come in the parameter object of the click event handler.

    Angular Approach
    HTML
    <div id="sampleGroup" dx-validation-group="{}">
        <div dx-text-box="{ value: login, placeholder: 'Login' }",
             dx-validator="{ validationRules: [{
                    type: 'required'
                }, {
                    type: 'pattern',
                    pattern: '^[a-zA-Z]+$',
                    message: 'Do not use digits.'
                }] }">   
        </div>
        <div dx-text-box="{ value: password, mode: 'password' }",
             dx-validator="{ validationRules: [{
                type: 'required',
                message: 'Password is required'
            }] }">   
        </div>
        <div dx-button="{ text: 'Validate and submit', onClick: validateAndSubmit }"></div>
    </div>
    JavaScript
    function Controller($scope) {
        $scope.login = '';
        $scope.password = '';
        $scope.validateAndSubmit = function(params) {
            var result = params.validationGroup.validate();                
            if (result.isValid) {
                DevExpress.ui.notify({
                    message: "You are registered",
                    position: {
                        my: "left top",
                        at: "left top",
                        offset: 15
                    }
                }, "success", 3000);
            }
        }
    }
    Knockout Approach
    HTML
    <div id="sampleGroup" data-bind="dxValidationGroup : {}" >
        <div data-bind="dxTextBox: { value: login, placeholder: 'Login' },
            dxValidator: { validationRules: [{
                type: 'required'
            }, {
                type: 'pattern',
                pattern: '^[a-zA-Z]+$',
                message: 'Do not use digits.'
            }] }">  
        </div>
        <div data-bind="dxTextBox: { value: password, mode: 'password' },
            dxValidator: { validationRules: [{
                type: 'required',
                message: 'Password is required'
            }] }"> 
        </div>  
        <div data-bind="dxButton: { text: 'Validate and submit', onClick: validateAndSubmit }"></div>
    </div>
    JavaScript
    var viewModel = {
        login: ko.observable(""),
        password: ko.observable(""),
        validateAndSubmit: function(params) {
            var result = params.validationGroup.validate();                
            if (result.isValid) {
                DevExpress.ui.notify({
                    message: "You are registered",
                    position: {
                        my: "left top",
                        at: "left top",
                        offset: 15
                    }
                }, "success", 3000);
            }
        }
    };
    ko.applyBindings(viewModel);
  • At any place
    Call the DevExpress.validationEngine.validateGroup(group) method passing the required validation group as a parameter.

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

NOTE: To validate the default validation group, call the DevExpress.validationEngine.validateGroup() method without parameters.

Whenever you force validation for a group when an editor value has not changed since the last validation, validation will not be performed repeatedly for this editor. For example, if an editor value has not changed since it was validated on a value change, it will not be validated on a button click when a validation group is validated. However, some rules have the reevaluate option, which allows you to change this behavior and check the required rules no matter the target value changed.

Display Validation Errors

The DevExtreme Validation comes with an integrated UI part. First, each editor is highlighted to indicate that it has an invalid value, when a validation rule is not satisfied during validation.

Validated Editors

Second, a summary on all the validation rules that are not satisfied in a validation group is displayed by the dxValidationSummary widget. This widget has an item collection that is populated each time a validation error occurs in the validation group with which the widget is associated. The summary items are displayed using the default item template that is based on the messages specified for the failed validation rules. You can use a custom item template, as in any collection widget in DevExtreme.

See Also

Validation Summary

To associate the dxValidationSummary widget with a validation group, add the dxValidationSummary widget inside of the element representing the dxValidationGroup widget.

NOTE: You do not have to associate the dxValidationSummary widget with a validation group, if you are going to display errors occurring in the editors that belong to the view or application to which the dxValidationSummary widget is related.

Angular Approach
HTML
<div id="sampleGroup" dx-validation-group="{}">
    <div dx-text-box="{ value: login, placeholder: 'Login' }",
        dx-validator="{ validationRules: [{
            type: 'required'
        }, {
            type: 'pattern',
            pattern: '^[a-zA-Z]+$',
            message: 'Do not use digits.'
        }] }">   
    </div>
    <div dx-text-box="{ value: password, mode: 'password' }",
        dx-validator="{ validationRules: [{
            type: 'required',
            message: 'Password is required'
        }] }">   
    </div>
    <div dx-validation-summary="{ }"></div>
    <div dx-button="{ text: 'Validate and submit', onClick: validateAndSubmit }"></div>
</div>
Knockout Approach
HTML
<div id="sampleGroup" data-bind="dxValidationGroup : {}" >
    <div data-bind="dxTextBox: { value: login, placeholder: 'Login' },
        dxValidator: { validationRules: [{
            type: 'required'
        }, {
            type: 'pattern',
            pattern: '^[a-zA-Z]+$',
            message: 'Do not use digits.'
        }] }"> 
    </div> 
    <div data-bind="dxTextBox: { value: password, mode: 'password' },
        dxValidator: { validationRules: [{
            type: 'required',
            message: 'Password is required'
        }] }"> 
    </div>
    <div data-bind="dxValidationSummary: { }"></div>
    <div data-bind="dxButton: { text: 'Validate and submit', onClick: validateAndSubmit }"></div>
</div>

Remote Scenarios

When validation requires a request to a server, some time may pass until a response from the server comes. Thus, validation should be performed with a delay. Therefore, we recommend that you use the 'custom' rule for such remote scenarios. The 'custom' rule allows you to define a function where you can validate the editor value yourself.

Below, you can see an example where two fields must be validated. One of these fields demands a request to a server. Thus, this field will be validated within the validationCallback function of a 'custom' rule.

To emulate a request to a server, a validation will be performed after the specified timeout. While waiting for the specified timeout, the editor value can be considered either valid or invalid. Choose the scenario that is most appropriate for your application.

Angular Approach
HTML
<div dx-text-box="{ value: login, placeholder: 'Login' }",
    dx-validator="{
        validationRules: [
            { type: 'required', message: 'Login is required' },
            { type: 'custom', validationCallback: checkLoginAvailable,
                message: 'This login is not available, please choose another one.' }
        ]
    }">   
</div>
<div dx-text-box="{ value: password, mode: 'password', placeholder: 'Password' }"
    dx-validator="{ 
        validationRules: [
            { type: 'required', message: 'Password can not be empty.' }
        ]
    }">   
</div>
<div dx-validation-summary="{ }"></div>
<div dx-button="{ text: 'Register', onClick: register }"></div>
Knockout Approach
HTML
<div data-bind="dxTextBox: { value: login, placeholder: 'Login' },
    dxValidator: {
        validationRules: [
            { type: 'required', message: 'Login is required' },
            { type: 'custom', validationCallback: checkLoginAvailable,
                message: 'This login is not available, please choose another one.' }
        ]
    }">
</div>
<img style="position: absolute; right:10px; top: 9px;" data-bind="visible: loginRemoteCheckTimeout" src="../Images/preloader.gif" />
<div data-bind="dxTextBox: { value: password, mode: 'password', placeholder: 'Password' },
    dxValidator: {
        validationRules: [
            { type: 'required', message: 'Password can not be empty.' }
        ]
    }"> 
</div>
<div data-bind="dxValidationSummary: { }"></div>
<div data-bind="dxButton: { text: 'Register', onClick: register }"></div>

Positive Scenario

Show Example:
AngularJS
Knockout
jQuery

In this example, the 'Login' editor is considered valid while waiting a response from a server. Enter the 'Alex' login to get a negative response from the server and see how the 'custom' rule is broken.

<div ng-controller="demoController">
    <div dx-text-box="{ value: login, placeholder: 'Login' }",
        dx-validator="{
		    validationRules: [
			    { type: 'required', message: 'Login is required' },
			    { type: 'custom', validationCallback: checkLoginAvailable,
				    message: 'This login is not available, please choose another one.' }
		    ]
	    }">   
    </div>
    <img style="position: absolute; right:10px; top: 9px;" ng-show="loginRemoteCheckTimeout" src="/Content/data/preloader.gif" />
    <div dx-text-box="{ value: password, mode: 'password', placeholder: 'Password' }"
        dx-validator="{ 
		    validationRules: [
			    { type: 'required', message: 'Password can not be empty.' }
		    ]
	    }">   
    </div>
    <div dx-validation-summary="{ }"></div>
    <div dx-button="{ text: 'Register', onClick: register }"></div>
</div>
 
var myApp = angular.module('myApp', ['dx']);
myApp.controller("demoController", function ($scope) {
    $scope.login = "";
    $scope.password = "";
    $scope.register = function (params) {
        var result = params.validationGroup.validate();
        if (result.isValid) {
            DevExpress.ui.notify({
                message: "You are logged in as " + $scope.login,
                position: {
                    my: "left top",
                    at: "left top"
                }
            }, "success", 3000);
        }
    };
    $scope.loginRemoteCheckTimeout = null;
    $scope.checkLoginAvailable = function (params) {
        if ($scope.loginRemoteCheckTimeout) {
            clearTimeout(viewModel.loginRemoteCheckTimeout);
        }
        var timeoutId = setTimeout(function () {
            $scope.loginRemoteCheckTimeout = null;
            //just came back from a server...
            if (params.value === "Alex") {
                params.rule.message = "User name " + params.value + " is already used. Please choose another one.";
                params.rule.isValid = false;
                params.validator.validate();
            } else {
                params.rule.isValid = true;
                params.validator.validate();
            }
        }, 1000);
        $scope.loginRemoteCheckTimeout = timeoutId;
        return true;
    }
});
angular.element(document).ready(function () {
    angular.bootstrap(document, ['myApp']);
});

In this example, the 'Login' editor is considered valid while waiting a response from a server. Enter the 'Alex' login to get a negative response from the server and see how the 'custom' rule is broken.

<div data-bind="dxTextBox: { value: login, placeholder: 'Login' },
	dxValidator: { validationRules: [{ type: 'required', message: 'Login is required' },
		{ type: 'custom', validationCallback: checkLoginAvailable,
		message: 'This login is not available, please choose another one.' }] }">
</div>
<img style="position: absolute; right:10px; top: 9px;" data-bind="visible: loginRemoteCheckTimeout" src="/Content/data/preloader.gif" />
<div data-bind="dxTextBox: { value: password, mode: 'password', placeholder: 'Password' },
	dxValidator: { validationRules: [{
        type: 'required',
        message: 'Password can not be empty.'
    }] }"> 
</div>
<div data-bind="dxValidationSummary: { }"></div>
<div data-bind="dxButton: { text: 'Register', onClick: register }"></div>
 
var viewModel = {
    login: ko.observable(""),
    password: ko.observable(""),
    register: function (params) {
        var result = params.validationGroup.validate();
        if (result.isValid) {
            DevExpress.ui.notify({
                message: "You are logged in as " + this.login(),
                position: {
                    my: "left top",
                    at: "left top"
                }
            }, "success", 3000);
        }
    },
    loginRemoteCheckTimeout: ko.observable(),
    checkLoginAvailable: function (params) {
        if (viewModel.loginRemoteCheckTimeout()) {
            clearTimeout(viewModel.loginRemoteCheckTimeout());
        }
        var timeoutId = setTimeout(function () {
            viewModel.loginRemoteCheckTimeout(null);
            //just came back from a server...
            if (params.value === "Alex") {
                params.rule.message = "User name " + params.value + " is already used. Please choose another one.";
                params.rule.isValid = false;
                params.validator.validate();
            } else {
                params.rule.isValid = true;
                params.validator.validate();
            }
        }, 1000);
        viewModel.loginRemoteCheckTimeout(timeoutId);
        return true;
    }
}
ko.applyBindings(viewModel);

In this example, the 'Login' editor is considered valid while waiting a response from a server. Enter the 'Alex' login to get a negative response from the server and see how the 'custom' rule is broken.

<div id="login"></div>
<img id="image" style="position: absolute; right:10px; top: 9px;" src="/Content/data/preloader.gif" />
<div id="password"></div>
<div id="summary"></div>
<div id="button"></div>
 
$(function () {
    var loginRemoteCheckTimeout = null;
    var changeImageVisibility = function (param) {
        if (param) {
            $("#image").show();
        } else { $("#image").hide(); }
    };
    changeImageVisibility(false);
    var checkLoginAvailable = function (params) {
        if (loginRemoteCheckTimeout) {
            clearTimeout(loginRemoteCheckTimeout);
        }
        var timeoutId = setTimeout(function () {
            loginRemoteCheckTimeout = null;
            changeImageVisibility(loginRemoteCheckTimeout);
            //just came back from a server...
            if (params.value === "Alex") {
                params.rule.message = "User name " + params.value + " is already used. Please choose another one.";
                params.rule.isValid = false;
                params.validator.validate();
            } else {
                params.rule.isValid = true;
                params.validator.validate();
            }
        }, 1000);
        loginRemoteCheckTimeout = timeoutId;
        changeImageVisibility(loginRemoteCheckTimeout);
        return true;
    };
    var register = function (params) {
        var result = params.validationGroup.validate();
        if (result.isValid) {
            DevExpress.ui.notify({
                message: "You are logged in as " + $("#login").dxTextBox('instance').option('value'),
                position: {
                    my: "left top",
                    at: "left top"
                }
            }, "success", 3000);
        }
    };
    $("#login").dxTextBox({
        value: null,
        placeholder: 'Login'
    }).dxValidator({
        validationRules: [{
            type: 'required',
            message: 'Login is required'
        }, {
            type: 'custom',
            validationCallback: checkLoginAvailable,
            message: 'This login is not available, please choose another one.'
        }]
    });
    $("#password").dxTextBox({
        value: null,
        mode: 'password',
        placeholder: 'Password'
    }).dxValidator({
        validationRules: [{
            type: 'required',
            message: 'Password can not be empty.'
        }]
    });
    $("#summary").dxValidationSummary({});
    $("#button").dxButton({
        text: 'Register', onClick: register
    });
});

Negative Scenario

Show Example:
AngularJS
Knockout
jQuery

In this example, the 'Login' editor is considered invalid while waiting a response from a server. Enter the 'Alex' login to get a negative response from the server and see how the 'custom' rule is broken.

<div ng-controller="demoController">
    <div dx-text-box="{ value: login, placeholder: 'Login' }",
        dx-validator="{
		    validationRules: [
			    { type: 'required', message: 'Login is required' },
			    { type: 'custom', validationCallback: checkLoginAvailable,
				    message: 'This login is not available, please choose another one.' }
		    ]
	    }">   
    </div>
    <img style="position: absolute; right:10px; top: 9px;" ng-show="loginRemoteCheckTimeout" src="/Content/data/preloader.gif" />
    <div dx-text-box="{ value: password, mode: 'password', placeholder: 'Password' }"
        dx-validator="{ 
		    validationRules: [
			    { type: 'required', message: 'Password can not be empty.' }
		    ]
	    }">   
    </div>
    <div dx-validation-summary="{ }"></div>
    <div dx-button="{ text: 'Register', onClick: register }"></div>
</div>
 
var myApp = angular.module('myApp', ['dx']);
myApp.controller("demoController", function ($scope) {
    $scope.login = "";
    $scope.password = "";
    $scope.register = function (params) {
        var result = params.validationGroup.validate();
        if (result.isValid) {
            DevExpress.ui.notify({
                message: "You are logged in as " + $scope.login,
                position: {
                    my: "left top",
                    at: "left top"
                }
            }, "success", 3000);
        }
    };
    $scope.loginRemoteCheckTimeout = null;
    $scope.checkLoginAvailable = function (params) {
        if ($scope.loginRemoteCheckTimeout) {
            clearTimeout(viewModel.loginRemoteCheckTimeout);
        }
        var timeoutId = setTimeout(function () {
            $scope.loginRemoteCheckTimeout = null;
            //just came back from a server...
            if (params.value === "Alex") {
                params.rule.message = "User name " + params.value + " is already used. Please choose another one.";
                params.rule.isValid = false;
                params.validator.validate();
            } else {
                params.rule.isValid = true;
                params.validator.validate();
            }
        }, 1000);
        $scope.loginRemoteCheckTimeout = timeoutId;
        return false;
    }
});
angular.element(document).ready(function () {
    angular.bootstrap(document, ['myApp']);
});

In this example, the 'Login' editor is considered invalid while waiting a response from a server. Enter the 'Alex' login to get a negative response from the server and see how the 'custom' rule is broken.

<div data-bind="dxTextBox: { value: login, placeholder: 'Login' },
	dxValidator: { validationRules: [{ type: 'required', message: 'Login is required' },
		{ type: 'custom', validationCallback: checkLoginAvailable,
		message: 'This login is not available, please choose another one.' }] }">
</div>
<img style="position: absolute; right:10px; top: 9px;" data-bind="visible: loginRemoteCheckTimeout" src="/Content/data/preloader.gif" />
<div data-bind="dxTextBox: { value: password, mode: 'password', placeholder: 'Password' },
	dxValidator: { validationRules: [{
        type: 'required',
        message: 'Password can not be empty.'
    }] }"> 
</div>
<div data-bind="dxValidationSummary: { }"></div>
<div data-bind="dxButton: { text: 'Register', onClick: register }"></div>
 
var viewModel = {
    login: ko.observable(""),
    password: ko.observable(""),
    register: function (params) {
        var result = params.validationGroup.validate();
        if (result.isValid) {
            DevExpress.ui.notify({
                message: "You are logged in as " + this.login(),
                position: {
                    my: "left top",
                    at: "left top"
                }
            }, "success", 3000);
        }
    },
    loginRemoteCheckTimeout: ko.observable(),
    checkLoginAvailable: function (params) {
        if (viewModel.loginRemoteCheckTimeout()) {
            clearTimeout(viewModel.loginRemoteCheckTimeout());
        }
        var timeoutId = setTimeout(function () {
            viewModel.loginRemoteCheckTimeout(null);
            //just came back from a server...
            if (params.value === "Alex") {
                params.rule.message = "User name " + params.value + " is already used. Please choose another one.";
                params.rule.isValid = false;
                params.validator.validate();
            } else {
                params.rule.isValid = true;
                params.validator.validate();
            }
        }, 1000);
        viewModel.loginRemoteCheckTimeout(timeoutId);
        return false;
    }
}
ko.applyBindings(viewModel);

In this example, the 'Login' editor is considered invalid while waiting a response from a server. Enter the 'Alex' login to get a negative response from the server and see how the 'custom' rule is broken.

<div id="login"></div>
<img id="image" style="position: absolute; right:10px; top: 9px;" src="/Content/data/preloader.gif" />
<div id="password"></div>
<div id="summary"></div>
<div id="button"></div>
 
$(function () {
    var loginRemoteCheckTimeout = null;
    var changeImageVisibility = function (param) {
        if (param) {
            $("#image").show();
        } else { $("#image").hide(); }
    };
    changeImageVisibility(false);
    var checkLoginAvailable = function (params) {
        if (loginRemoteCheckTimeout) {
            clearTimeout(loginRemoteCheckTimeout);
        }
        var timeoutId = setTimeout(function () {
            loginRemoteCheckTimeout = null;
            changeImageVisibility(loginRemoteCheckTimeout);
            //just came back from a server...
            if (params.value === "Alex") {
                params.rule.message = "User name " + params.value + " is already used. Please choose another one.";
                params.rule.isValid = false;
                params.validator.validate();
            } else {
                params.rule.isValid = true;
                params.validator.validate();
            }
        }, 1000);
        loginRemoteCheckTimeout = timeoutId;
        changeImageVisibility(loginRemoteCheckTimeout);
        return false;
    };
    var register = function (params) {
        var result = params.validationGroup.validate();
        if (result.isValid) {
            DevExpress.ui.notify({
                message: "You are logged in as " + $("#login").dxTextBox('instance').option('value'),
                position: {
                    my: "left top",
                    at: "left top"
                }
            }, "success", 3000);
        }
    };
    $("#login").dxTextBox({
        value: null,
        placeholder: 'Login'
    }).dxValidator({
        validationRules: [{
            type: 'required',
            message: 'Login is required'
        }, {
            type: 'custom',
            validationCallback: checkLoginAvailable,
            message: 'This login is not available, please choose another one.'
        }]
    });
    $("#password").dxTextBox({
        value: null,
        mode: 'password',
        placeholder: 'Password'
    }).dxValidator({
        validationRules: [{
            type: 'required',
            message: 'Password can not be empty.'
        }]
    });
    $("#summary").dxValidationSummary({});
    $("#button").dxButton({
        text: 'Register', onClick: register
    });
});

Validate View Model

NOTE: This information is useful for Knockout approach.

You may want to validate the ViewModel object rather than the UI editors. This will allow you to separate validation logic from the UI, reuse it between multiple views and easily unit-test the implementation.

Associate ViewModel Members with a Validator

To validate a ViewModel's observable member, the latter should be associated with the dxValidator object.

Validated View Model

The dxValidator object validates the ViewModel's members against a predefined set of rules. To define the rules to be checked, assign an array of rules to the validator's validationRules field.

JavaScript
dxValidator = {
    validationRules: [{
        type: "required",
        message: "login is required" //optional
    }, {
        type: "pattern",
        pattern: "^[a-zA-Z]+$",
        message: "Do not use digits." //optional
    }]
};

You can see a full structure of each predefined rule type in the Validation Rules Reference section.

Generally, the structure of the dxValidator object repeats the structure of the dxValidator widget's configuration object.

To associate a ViewModel's observable member with a dxValidatior object, extend this member with an object that has the dxValidator field set to the dxValidator object.

JavaScript
$(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);
});

Validate ViewModel Members

A ViewModel for its validated members serves as a validation group for validated fields. However, while validation groups are registered for validation automatically, ViewModel objects should be registered manually. For this purpose, use the DevExpress.validationEngine.registerModelForValidation(model) method.

See Also
JavaScript
var viewModel = {
    //...
};
DevExpress.validationEngine.registerModelForValidation(viewModel);
ko.applyBindings(viewModel);

To validate a registered ViewModel object, call the DevExpress.validationEngine.validateModel(model) method.

JavaScript
var viewModel = {
        //...
        validateAndLogin: function(params) {
            var result = DevExpress.validationEngine.validateModel(this);
            if(result.isValid) {
                DevExpress.ui.notify({
                    message: "You are logged in as " + this.login(),
                    position: {
                        my: "left top",
                        at: "left top"
                    }
                }, "success", 3000);
            }
        }
    };
};
DevExpress.validationEngine.registerModelForValidation(viewModel);
ko.applyBindings(viewModel);

Display Validation Errors

Since ViewModel fields are related to some editors in a UI, validation errors found in the ViewModel fields should be shown for the related editors. Thus, set the isValid and validationError configuration options of the target editors to the corresponding fields of the ViewModel's validators.

HTML
<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>

In addition, you can use the dxValidationSummary widget to display a summary of validation errors. Add this widget to the view that is bound to the validated ViewModel.

HTML
<div data-bind="dxValidationSummary: { }"></div>

Validate Custom Editors

There can be scenarios when you cannot associate the dxValidator widget with an editor, because this editor is not a DevExtreme editor or a validated value is a concatenation of several DevExtreme editor values. In this instance, create the dxValidator widget and specify an adapter for it using the adapter configuration option. An adapter is required to "tell" the dxValidator what and when to validate and how to apply the validation result.

The adapter option of the dxValidator widget is an object that exposes the following fields.

  • getValue
    A function that returns the value to be validated.

  • validationRequestsCallbacks
    The jQuery.Callbacks() object that is fired when the specified value should be validated. The dxValidator widget will add a handler to this object, in which it will call its validate function.

  • applyValidationResults
    A function that the dxValidator widget calls after validating the specified value. This function should apply the validation result that the validator passes as a parameter. The validation result is an object. You can see its structure in the description of the dxValidator.validated event argument.

  • reset
    A function that resets the validated values so that end users can then specify other values. This function is called by the reset method of the dxValidator widget.

  • focus
    A function that sets focus to the validated editors when the dxValidationSummary is focused.

  • bypass
    A function that returns a Boolean value specifying whether or not to bypass validation. If the function returns true, validation is bypassed, otherwise validation is performed.

By default, when you do not specify the adapter option, a default adapter is used. The default adapter does the following.

  • Specifies the current value of the associated editor as the getValue function's return value.
  • Fires validationRequestsCallbacks passed to the validator when the event specified by the editor's valueChangeEvent is raised.
  • Sets the editor's isValid and validationError options to the values provided by the validator's validation result.
  • Sets the editor's value to undefined.
  • Focuses the validated editor.
Show Example:
AngularJS
Knockout
jQuery

This example illustrates how to define an adapter for the dxValidator widget, to show a message for an end user when neither an email or phone number is specified.

<div ng-controller="demoController">
    <div dx-text-box="{ bindingOptions: { value: 'contactEmail'}, mode: 'email', placeholder: 'Email' }"></div>
    <div dx-text-box="{ bindingOptions: { value: 'contactPhone'}, placeholder: 'Phone number' }"></div>
    <div dx-validator="{ validationRules: [{ type: 'required', message: 'Specify your email or phone number!'}], adapter: phoneAndEmailAdapter }"></div>
    <div dx-validation-summary="{}"></div>
    <div dx-button="{text: 'Contact me', onClick: contactMeClick}"></div>
</div>
var myApp = angular.module('myApp', ['dx']);
myApp.controller("demoController", function ($scope) {
    var $callbacks = $.Callbacks();

    $scope.contactPhone = '';
    $scope.contactEmail = '';
    $scope.validationResultStyle = { 'border-width': '0px' };
    $scope.contactMeClick = function (p) {
        var result = p.validationGroup.validate();
        if (result.isValid) {
            DevExpress.ui.notify({
                message: "We will contact you when we have information for you.",
                position: {
                    my: "right top",
                    at: "right top",
                    offset: "-10 10"
                }
            }, "success", 3000);
        }
    };
    $scope.phoneAndEmailAdapter = {
        getValue: function () {
            return $scope.contactPhone + $scope.contactEmail;
        },
        applyValidationResults: function (result) {
            $scope.validationResultStyle['border-width'] = result.isValid ? "0px" : "1px";
        },
        validationRequestsCallbacks: $callbacks
    }
});
angular.element(document).ready(function () {
    angular.bootstrap(document, ['myApp']);
});

This example illustrates how to define an adapter for the dxValidator widget, to show a message for an end user when neither an email or phone number is specified.

<div data-bind="dxTextBox: { value: contactEmail, mode: 'email', placeholder: 'Email' }"></div>
<div data-bind="dxTextBox: { value: contactPhone, placeholder: 'Phone number' }"></div>
<div data-bind="dxValidator: { 
    adapter: { 
        getValue: getContactPhoneAndEmail,
        validationRequestsCallbacks: contactInfoValidationRequests,
        applyValidationResults: applyContactValidation
    },
    validationRules: [   { type: 'required', message: 'Specify your email or phone number!' }] }">
</div>
<div data-bind="if: showRequestContactInfo " style="color: #000971">Please, let us contact you!</div>
<div data-bind="dxValidationSummary: { }"></div>
<div data-bind="dxButton: { text: 'Contact me', onClick: contactMeClick }"></div>
var viewModel = {
    contactEmail: ko.observable(""),
    contactPhone: ko.observable(""),
    showRequestContactInfo: ko.observable(false),
    contactMeClick: function (params) {
        var result = params.validationGroup.validate();
        if (result.isValid) {
            DevExpress.ui.notify({
                message: "We will contact you when we have information for you.",
                position: {
                    my: "right top",
                    at: "right top",
                    offset: "-10 10"
                }
            }, "success", 3000);
        }
    },
    getContactPhoneAndEmail: function () {
        return viewModel.contactPhoneAndEmail();
    }
};
viewModel.contactPhoneAndEmail = ko.computed(function () {
    return this.contactEmail() + this.contactPhone();
}, viewModel);
viewModel.applyContactValidation = function (result) {
    viewModel.showRequestContactInfo(!result.isValid);
};
viewModel.contactInfoValidationRequests = $.Callbacks();
viewModel.contactInfoChangedHandler = function () {
    viewModel.contactInfoValidationRequests.fire();
};
viewModel.contactPhoneAndEmail.subscribe(viewModel.contactInfoChangedHandler)
ko.applyBindings(viewModel);

This example illustrates how to define an adapter for the dxValidator widget, to show a message for an end user when neither an email or phone number is specified.

Use Custom Validation Engine

If you need to use a custom validation engine for DevExtreme editors, but want to display the validation result using the DevExtreme UI implementation, specify the following options for the validated DevExtreme editors.

  • isValid
    Specify whether the current editor value is valid against the validation rules that you checked using your custom validation engine.

  • validationError
    Specify an object that has the message field. This field should be a string that defines the error that occurred during validation.