Angular: Passwords

by cambiata

HTML

<script src="http://ci.angularjs.org/job/angular.js-angular-master/lastSuccessfulBuild/artifact/build/pkg/0.10.7-e86bafec/angular-0.10.7-e86bafec.js"></script>
<h3>Two ways of password validation</h3>

<form name="form">
  Password: <input type="password" ng:model="password" name="pwd" required />
  Confirm: <input type="password" ng:model="passwordConfirm" name="pwd2" password-validator="pwd" required/>
</form>

<ul>
<li ng:repeat="(name, e) in form.error">
  {{name}}:
  <ul>
    <li ng:repeat="widget in e">{{widget.widgetId}}</li>
  </ul>
</li>
</ul>

<hr />

<form name="form2" form-pwd-validator="pwd pwd2">
  Password: <input type="password" ng:model="password" name="pwd" required ng:model-instant/>
    Confirm: <input type="password" ng:model="passwordConfirm" name="pwd2" required ng:model-instant /><br />
    Both these inputs have ng:model-instant, so they are updated immediately:<br />
    pwd = {{password}}<br />
    confirm = {{passwordConfirm}}<br />
</form>

<ul>
<li ng:repeat="(name, e) in form2.error">
  {{name}}:
  <ul>
    <li ng:repeat="widget in e">{{widget.widgetId}}</li>
  </ul>
</li>
</ul>

CSS

.ng-invalid {
    border-color: red;
}

.ng-valid {
    border-color: green;
}

.ng-dirty {
    border-style: solid;
    border-width: 2px;
}

.ng-pristine {
    border-style: solid;
    border-width: 1px;
}

.errors {
    color: red;
}
h3 {
  font-size: 1.4em;
  font-weight: bold;
  margin-bottom: 15px;
}

JavaScript

var myApp = angular.module('myApp', []);

// directive on widget
myApp.directive('passwordValidator', [function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attr, ctrl) {
      // must be on the second password, when linking first one, the second one is not registered yet
      var pwdWidget = elm.inheritedData('$form')[attr.passwordValidator];

      ctrl.parsers.push(function(value) {
        if (value === pwdWidget.viewValue) {
          ctrl.setValidity('MATCH', true);
          return value;
        }
        ctrl.setValidity('MATCH', false);
      });

      pwdWidget.parsers.push(function(value) {
        ctrl.setValidity('MATCH', value === second.viewValue);
        return value;
      });
    }
  };
}]);

// directive on form
myApp.directive('formPwdValidator', [function() {
  return {
    require: 'form',
    link: {
      post: function(scope, elm, attr, form) {
        var ids = attr.formPwdValidator.split(' '),
            first = form[ids[0]],
            second = form[ids[1]];

        first.parsers.push(function(value) {
          second.setValidity('MATCH', value === second.viewValue);
          return value;
        });

        second.parsers.push(function(value) {
          if (value === first.viewValue) {
            second.setValidity('MATCH', true);
            return value;
          }
          second.setValidity('MATCH', false);
        });
      }
    }
  }
}]);