var app = angular.module("plunker", ['ui.validate']);
app.service("remoteValidator", function($q, $timeout) {
this.validate = function(value) {
return $q(function(resolve, reject) {
setTimeout(function() {
if (value === "bad") {
reject("Value is bad");
} else {
resolve();
}
}, 1000);
});
}
});
app.controller('MainCtrl', function($scope, $timeout, $q, remoteValidator) {
$scope.passwordForm = {
currentPassword: "",
newPassword: "",
currentPassword2: "456456",
nextPassword2: "emi"
};
$scope.min = function(value, limit) {
return ((value + '').length >= limit);
};
$scope.max = function(value, limit) {
return ((value + '').length <= limit);
};
$scope.equal = function(value, compare) {
return value === compare;
};
$scope.equalAsync = function(value, compare) {
return $q(function(resolve, reject) {
setTimeout(function() {
if (value === compare) {
resolve();
} else {
reject();
}
}, 200);
});
};
$scope.remoteValAsync = function(value) {
var promise = remoteValidator.validate(value);
promise.then(function() {
delete $scope.errorMsg;
}, function(error) {
$scope.errorMsg = error;
});
return promise;
};
$scope.minAsync = function(value, limit) {
return $q(function(resolve, reject) {
setTimeout(function() {
if ((value + '').length >= limit) {
resolve();
} else {
reject();
}
}, limit * 80);
});
};
$scope.maxAsync = function(value, limit) {
return $q(function(resolve, reject) {
setTimeout(function() {
if ((value + '').length <= limit) {
resolve();
} else {
reject();
}
}, limit * 80);
});
};
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<link data-require="bootstrap@*" data-semver="3.3.2" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
<script data-require="jquery@2.1.3" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script data-require="angular.js@1.3.1" data-semver="1.3.1" src="https://code.angularjs.org/1.3.1/angular.js"></script>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script src="validate.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div class="container">
<hr />
<h2>Validation Test</h2>
<form class="form" name="myForm">
<div class="form-group">
<p>Test common validators (with limited support for async validators)</p>
<div class="input-group-inline">
<input class="form-control"
type="text"
name="currentPassword"
ng-model="passwordForm.currentPassword"
ng-model-options="{ debounce: 100 }"
ui-validate="{ notTheSame: 'equal($value,passwordForm.currentPassword2)', maxLength : 'max($value,8)', minLength : 'min($value,3)'}"
ui-validate-watch="'passwordForm.currentPassword2'"
/>
<span ng-show="myForm.currentPassword.$pending">Checking length...</span>
<ul>
<li ng-repeat="(key, errors) in myForm.currentPassword.$error track by $index">{{ key }}</li>
</ul>
</div>
<div class="input-group-inline">
<input class="form-control" type="text" name="currentPassword2" ng-model-options="{ debounce: 100 }" ng-model="passwordForm.currentPassword2" />
</div>
</div>
<div class="form-group">
<p>Test Async validators (with support for regular validators)</p>
<div class="input-group-inline">
<input class="form-control" type="text" name="nextPassword" ng-model-options="{ debounce: 100 }" ng-model="passwordForm.nextPassword" ui-validate="{ blacklist: '$value!==\'black\'' }"
ui-validate-async="{ badValue: 'remoteValAsync($value)', notTheSame: 'equalAsync($value,passwordForm.nextPassword2)', maxLength : 'maxAsync($value,8)', minLength : 'minAsync($value,3)'}"
ui-validate-watch="'passwordForm.nextPassword2'" />
<p>Pending validations:</p>
<ul>
<li ng-repeat="(key, errors) in myForm.nextPassword.$pending track by $index">{{ key }}</li>
</ul>
<p>Bad validations:</p>
<ul>
<li ng-repeat="(key, errors) in myForm.nextPassword.$error track by $index">{{ key }}</li>
</ul>
<p>{{ errorMsg }}</p>
</div>
<div class="input-group-inline">
<input class="form-control" type="text" name="nextPassword2" ng-model-options="{ debounce: 100 }" ng-model="passwordForm.nextPassword2" />
</div>
</div>
</form>
</div>
</body>
</html>
/* Put your css in here */
/**
* 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 a custom formatters and / or parsers.
* 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.
*
* @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' } ">
*
* @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.
*/
angular.module('ui.validate',[])
.directive('uiValidate', function($$uiValidateApplyWatch) {
'use strict';
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) {
'use strict';
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 = { validator: 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();
});
});
}
});
}}
});