UNPKG

whitesource

Version:
163 lines (151 loc) 6.74 kB
/** * General-purpose validator for ngModel. * angular.js comes with several built-in validation mechanism for input fields (ngRequired, ngPattern etc.) but using * an arbitrary validation function requires creation of custom directives for interact with angular's validation mechanism. * The ui-validate directive makes it easy to use any function(s) defined in scope as a validator function(s). * A validator function will trigger validation on both model and input changes. * * This utility bring 'ui-validate' directives to handle regular validations and 'ui-validate-async' for asynchronous validations. * * @example <input ui-validate=" 'myValidatorFunction($value)' "> * @example <input ui-validate="{ foo : '$value > anotherModel', bar : 'validateFoo($value)' }"> * @example <input ui-validate="{ foo : '$value > anotherModel' }" ui-validate-watch=" 'anotherModel' "> * @example <input ui-validate="{ foo : '$value > anotherModel', bar : 'validateFoo($value)' }" ui-validate-watch=" { foo : 'anotherModel' } "> * @example <input ui-validate-async=" 'myAsyncValidatorFunction($value)' "> * @example <input ui-validate-async="{ foo: 'myAsyncValidatorFunction($value, anotherModel)' }" ui-validate-watch=" 'anotherModel' "> * * @param ui-validate {string|object literal} If strings is passed it should be a scope's function to be used as a validator. * If an object literal is passed a key denotes a validation error key while a value should be a validator function. * In both cases validator function should take a value to validate as its argument and should return true/false indicating a validation result. * It is possible for a validator function to return a promise, however promises are better handled by ui-validate-async. * * @param ui-validate-async {string|object literal} If strings is passed it should be a scope's function to be used as a validator. * If an object literal is passed a key denotes a validation error key while a value should be a validator function. * Async validator function should take a value to validate as its argument and should return a promise that resolves if valid and reject if not, * indicating a validation result. * ui-validate-async supports non asyncronous validators. They are wrapped into a promise. Although is recomented to use ui-validate instead, since * all validations declared in ui-validate-async are registered un ngModel.$asyncValidators that runs after ngModel.$validators if and only if * all validators in ngModel.$validators reports as valid. */ angular.module('ui.validate',[]) .directive('uiValidate', function($$uiValidateApplyWatch) { return { restrict: 'A', require: 'ngModel', link: function(scope, elm, attrs, ctrl) { var validateFn, validateExpr = scope.$eval(attrs.uiValidate); if (!validateExpr) { return; } if (angular.isString(validateExpr)) { validateExpr = { validator: validateExpr }; } angular.forEach(validateExpr, function(exprssn, key) { validateFn = function(modelValue, viewValue) { // $value is left for retrocompatibility var expression = scope.$eval(exprssn, { '$value': modelValue, '$modelValue': modelValue, '$viewValue': viewValue }); // Keep support for promises for retrocompatibility if (angular.isObject(expression) && angular.isFunction(expression.then)) { expression.then(function() { ctrl.$setValidity(key, true); }, function() { ctrl.$setValidity(key, false); }); // Return as valid for now. Validity is updated when promise resolves. return true; } else { return expression; } }; ctrl.$validators[key] = validateFn; }); // Support for ui-validate-watch if (attrs.uiValidateWatch) { $$uiValidateApplyWatch(scope, ctrl, scope.$eval(attrs.uiValidateWatch)); } } }; }) .directive('uiValidateAsync', function ($$uiValidateApplyWatch, $timeout, $q) { return { restrict: 'A', require: 'ngModel', link: function (scope, elm, attrs, ctrl) { var validateFn, validateExpr = scope.$eval(attrs.uiValidateAsync); if (!validateExpr){ return;} if (angular.isString(validateExpr)) { validateExpr = { validatorAsync: validateExpr }; } angular.forEach(validateExpr, function (exprssn, key) { validateFn = function(modelValue, viewValue) { // $value is left for ease of use var expression = scope.$eval(exprssn, { '$value': modelValue, '$modelValue': modelValue, '$viewValue': viewValue }); // Check if it's a promise if (angular.isObject(expression) && angular.isFunction(expression.then)) { return expression; // Support for validate non-async validators } else { return $q(function(resolve, reject) { setTimeout(function() { if (expression) { resolve(); } else { reject(); } }, 0); }); } }; ctrl.$asyncValidators[key] = validateFn; }); // Support for ui-validate-watch if (attrs.uiValidateWatch){ $$uiValidateApplyWatch( scope, ctrl, scope.$eval(attrs.uiValidateWatch) ); } } }; }) .service('$$uiValidateApplyWatch', function() { return function(scope, ctrl, watch) { //string - update all validators on expression change if (angular.isString(watch)) { scope.$watch(watch, function() { ctrl.$validate(); }); //array - update all validators on change of any expression } else if (angular.isArray(watch)) { angular.forEach(watch, function(expression) { scope.$watch(expression, function() { ctrl.$validate(); }); }); //object - update appropriate validator } else if (angular.isObject(watch)) { angular.forEach(watch, function(expression/*, validatorKey*/) { //value is string - look after one expression if (angular.isString(expression)) { scope.$watch(expression, function() { ctrl.$validate(); }); } //value is array - look after all expressions in array if (angular.isArray(expression)) { angular.forEach(expression, function(intExpression) { scope.$watch(intExpression, function() { ctrl.$validate(); }); }); } }); }}; });