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