76

I'm writing a password verify directive :

 Directives.directive("passwordVerify",function(){
    return {
        require:"ngModel",
        link: function(scope,element,attrs,ctrl){
            ctrl.$parsers.unshift(function(viewValue){
                var origin = scope.$eval(attrs["passwordVerify"]);
                if(origin!==viewValue){
                    ctrl.$setValidity("passwordVerify",false);
                    return undefined;
                }else{
                    ctrl.$setValidity("passwordVerify",true);
                    return viewValue;
                }
            });

        }
    };
});

html :

<input data-ng-model='user.password' type="password" name='password' placeholder='password' required>
<input data-ng-model='user.password_verify' type="password" name='confirm_password' placeholder='confirm password' required data-password-verify="user.password">

Given 2 password fields in a form, if both password values are equal then the field affected by the directive is valid. The issue is that it works one way (i.e. when I type a password in the password-verify field). However, when the original password field is updated, the password-verify doesn't become valid.

Any idea how I could have a "two way binding verify?"

gary
  • 4,227
  • 3
  • 31
  • 58
mpm
  • 20,148
  • 7
  • 50
  • 55

24 Answers24

114

I use the following directive because I want to re-validate both input field regardless of whether value 1 or value 2 was changed:

directive:

'use strict';

angular.module('myApp').directive('equals', function() {
  return {
    restrict: 'A', // only activate on element attribute
    require: '?ngModel', // get a hold of NgModelController
    link: function(scope, elem, attrs, ngModel) {
      if(!ngModel) return; // do nothing if no ng-model

      // watch own value and re-validate on change
      scope.$watch(attrs.ngModel, function() {
        validate();
      });

      // observe the other value and re-validate on change
      attrs.$observe('equals', function (val) {
        validate();
      });

      var validate = function() {
        // values
        var val1 = ngModel.$viewValue;
        var val2 = attrs.equals;

        // set validity
        ngModel.$setValidity('equals', ! val1 || ! val2 || val1 === val2);
      };
    }
  }
});

usage

<input type="password" ng-model="value1" equals="{{value2}}" required>
<input type="password" ng-model="value2" equals="{{value1}}" required>
Jan Laussmann
  • 1,510
  • 3
  • 16
  • 19
  • 4
    I found this to work quite well. One thing that caught me off guard is that if you have some other angular validator like: `ng-minlength` on the first field, then that model isn't set until it's actually valid – creamcheese Feb 26 '14 at 20:44
  • 5
    I wrapped `ngModel.$setValidity` with `if (val1 && val2) { .. }` just so the form is not invalid when both values are empty. – jesal Mar 18 '14 at 21:52
  • 1
    Like @jesal I wrapped ngModel.$setValidity with if, but instead of && I use || because if val1 or val2 is set and the other no, it's invalid. if(val1 || val2){..} – juanmhidalgo Mar 19 '14 at 13:18
  • 4
    This has some small problems when there are more validation rules in play. If another validation fails, Angular doesn't update the model and weird stuff happen in the comparison... – user2173353 Jul 14 '14 at 11:22
  • 7
    This worked great for me. As a newbie it would help to add the validation string to this. `[form name].[field name].$error.equals` Use this to control what fields should or should show. I'm using it for error labels. – metric152 Jul 21 '14 at 17:10
  • 1
    Been using this for a while now but it doesn't seem to work since 1.3.0-beta12 as you can see by this plnkr here: http://plnkr.co/edit/W6AFHF308nyKVMQ9vomw?p=preview – creamcheese Jul 29 '14 at 17:44
  • Very clear solution and good work abstracting the functionality. – gyss Sep 27 '14 at 14:20
  • 3
    I think it's better to replace ```ngModel.$setValidity('equals', val1 === val2);``` with ```ngModel.$setValidity('equals', ! val1 || ! val2 || val1 === val2);``` – bullgare Oct 04 '14 at 16:09
  • I've tried to use this snipped, but always get val2 is equal to "". What can be wrong? – Nikolai Golub Feb 24 '15 at 21:44
  • @NikolayGolub Could you please provide a [Plunker](http://plnkr.co/) of the problem? – Jan Laussmann Feb 25 '15 at 06:04
  • 2
    @JanLaussmann, during transferring(removing not related stuff) my app to plunker, problem has disappeared. It means, that problem is somewhere in my code. Anyway, I post here a link with a working example. http://plnkr.co/edit/gFmCkUI6A2y4j64OWtrt?p=preview – Nikolai Golub Feb 25 '15 at 09:27
85

Creating a separate directive for this is not needed. There is already a build in Angular UI password validation tool. With this you could do:

<input name="password" required ng-model="password">
<input name="confirm_password"
       ui-validate=" '$value==password' "
       ui-validate-watch=" 'password' ">

 Passwords match? {{!!form.confirm_password.$error.validator}}
Rob
  • 5,353
  • 33
  • 34
bicycle
  • 8,315
  • 9
  • 52
  • 72
  • 3
    which currently requires jQuery – creamcheese Feb 26 '14 at 20:14
  • 24
    @DominicWatson What you're downvoting me for? This question is about angular and i refer to the angular docs. Go learn it if you don't understand the difference between them. – bicycle Feb 28 '14 at 01:02
  • 24
    @DominicWatson The directive is here https://github.com/angular-ui/ui-utils/blob/master/modules/validate/validate.js except from jquery lite there's no freaking jquery in it. If you are so anti jquery you shouldn't be using this framework anyway since it incorporates jquery. – bicycle Feb 28 '14 at 12:00
  • 3
    I'm having the same issue as the original post. If there's something already built-in to angular-ui doesn't it make sense to use it instead of re-inventing the wheel? – PeterG Mar 03 '14 at 19:44
  • http://angular-ui.github.io/ui-utils/ : I don't see where jQuery is required for ui.validate. Unless I'm missing something all that's needed is jqlite. – PeterG Mar 03 '14 at 20:12
  • 2
    @PeterG I don't think Dominic Watson had a clue what he was talking about – bicycle Mar 10 '14 at 23:56
  • No point re-inventing the wheel. This is the perfect solution indeed. – Sanket Sahu Aug 13 '14 at 09:46
  • @bicycle I think your display of the password match boolean value is showing the inverse state of the validation. In my case at least. – Stephane Sep 17 '14 at 08:12
  • 1
    Unfortunately with this solution, like with Jan Laussmann answer, `confirm_password` is also invalidated if `password` has extra validation criteria not fulfilled. – AxeEffect Sep 26 '14 at 03:42
  • After implementing Jan Laussmann's example, I then scrolled down further to find this - perfect! – Forge_7 Oct 10 '14 at 15:39
  • Reinventing the wheel is _the only possible way_ to make things work the way you need. – BorisOkunskiy Dec 09 '14 at 19:34
  • One who will try it '$value==password' and 'password' are using model values not an input field. – m1ld Jun 27 '16 at 13:38
60

This should solve it:

View:

<div ng-controller='Ctrl'>
   <form name='form'>
      <input data-ng-model='user.password' type="password" name='password' placeholder='password' required>
      <div ng-show="form.password.$error.required">
        Field required</div>
      <input ng-model='user.password_verify' type="password" name='confirm_password' placeholder='confirm password' required data-password-verify="user.password">
      <div ng-show="form.confirm_password.$error.required">
        Field required!</div>
      <div ng-show="form.confirm_password.$error.passwordVerify">
        Fields are not equal!</div>
   </form
</div>

Directive

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

app.directive("passwordVerify", function() {
   return {
      require: "ngModel",
      scope: {
        passwordVerify: '='
      },
      link: function(scope, element, attrs, ctrl) {
        scope.$watch(function() {
            var combined;

            if (scope.passwordVerify || ctrl.$viewValue) {
               combined = scope.passwordVerify + '_' + ctrl.$viewValue; 
            }                    
            return combined;
        }, function(value) {
            if (value) {
                ctrl.$parsers.unshift(function(viewValue) {
                    var origin = scope.passwordVerify;
                    if (origin !== viewValue) {
                        ctrl.$setValidity("passwordVerify", false);
                        return undefined;
                    } else {
                        ctrl.$setValidity("passwordVerify", true);
                        return viewValue;
                    }
                });
            }
        });
     }
   };
});
asgoth
  • 35,552
  • 12
  • 89
  • 98
  • 1
    I used the validation snippet in the doc, let me try your code. – mpm Dec 23 '12 at 16:31
  • I've changed my answer. This should work. See [jsFiddle](http://jsfiddle.net/asgoth/WZxg4/) – asgoth Dec 23 '12 at 18:10
  • This still doesn't give 2 way bindings, or something has changed since then? – Hadesara May 10 '13 at 08:23
  • @asgoth I'm implementing your directive but it seems it's trigerring a required error as well. It's my first take at directives so I'm looking into it. – Stephane Sep 09 '14 at 18:09
  • 1
    I'm agree with CWSpear. The solution should be corrected with the bug commented above – gyss Sep 27 '14 at 14:14
  • 9
    There is a memory leak with this solution. Every watch event that gets triggered with push another parser onto the ctrl.$parsers array. Inspecting ctrl.$parsers.length at the end of the watch event handler will show this. – mshiltonj Oct 18 '14 at 19:42
  • I found this way better and cleaner: http://odetocode.com/blogs/scott/archive/2014/10/13/confirm-password-validation-in-angularjs.aspx – ogaihtorecic Jun 03 '16 at 15:09
  • 1
    This solution has a major flaw: If the user types the 2 passwords correctly and then changes the first password (i.e. user.password model), no error is triggered. – Xeroxoid Aug 01 '16 at 14:29
22

Yet another take on this is to match the model of one input to another input’s value.

app.directive('nxEqual', function() {
    return {
        require: 'ngModel',
        link: function (scope, elem, attrs, model) {
            if (!attrs.nxEqual) {
                console.error('nxEqual expects a model as an argument!');
                return;
            }
            scope.$watch(attrs.nxEqual, function (value) {
                model.$setValidity('nxEqual', value === model.$viewValue);
            });
            model.$parsers.push(function (value) {
                var isValid = value === scope.$eval(attrs.nxEqual);
                model.$setValidity('nxEqual', isValid);
                return isValid ? value : undefined;
            });
        }
    };
});

So, if the password box’s model is login.password then you set the following attribute on the verification box: nx-equal="login.password", and test for formName.elemName.$error.nxEqual. Like so:

<form name="form">
    <input type="password" ng-model="login.password">
    <input type="password" ng-model="login.verify" nx-equal="login.password" name="verify">
    <span ng-show="form.verify.$error.nxEqual">Must be equal!</span>
</form>

Extended version:

For a new project of mine I had to modify the above directive so that it would only display the nxEqual error when, and only when, the verification input had a value. Otherwise the nxEqual error should be muted. Here’s the extended version:

app.directive('nxEqualEx', function() {
    return {
        require: 'ngModel',
        link: function (scope, elem, attrs, model) {
            if (!attrs.nxEqualEx) {
                console.error('nxEqualEx expects a model as an argument!');
                return;
            }
            scope.$watch(attrs.nxEqualEx, function (value) {
                // Only compare values if the second ctrl has a value.
                if (model.$viewValue !== undefined && model.$viewValue !== '') {
                    model.$setValidity('nxEqualEx', value === model.$viewValue);
                }
            });
            model.$parsers.push(function (value) {
                // Mute the nxEqual error if the second ctrl is empty.
                if (value === undefined || value === '') {
                    model.$setValidity('nxEqualEx', true);
                    return value;
                }
                var isValid = value === scope.$eval(attrs.nxEqualEx);
                model.$setValidity('nxEqualEx', isValid);
                return isValid ? value : undefined;
            });
        }
    };
});

And you would use it like so:

<form name="form">
    <input type="password" ng-model="login.password">
    <input type="password" ng-model="login.verify" nx-equal-ex="login.password" name="verify">
    <span ng-show="form.verify.$error.nxEqualEx">Must be equal!</span>
</form>

Try it: http://jsfiddle.net/gUSZS/

Fredric
  • 1,223
  • 18
  • 16
  • 3
    This one is the best. It's most succinct. It works on model values and it works both ways. – CMCDragonkai Mar 05 '14 at 17:44
  • One thing though, is that the $parser pipe should return the value or undefined. That way subsequent pipes don't just end because it's always returned undefined when there's no return function. Also scope should be false. – CMCDragonkai Mar 08 '14 at 12:03
  • @CMCDragonkai: Good catch! I’ve updated the parser-function’s return value. Regarding the scope, as far as I know it defaults to `false`, so by not explicitly specifying a scope it is equal to `scope: false`. – Fredric Mar 08 '14 at 14:47
  • I think it actually defaults to true being a child scope. But I haven't checked recently. – CMCDragonkai Mar 08 '14 at 15:00
  • @CMCDragonkai: no it’s indeed `false` by default, have a look at [AngularJS compile.js](https://github.com/angular/angular.js/blob/master/src/ng/compile.js) where it says: “**If set to `true`,** then a new scope will be created for this directive.” or [ng-newsletter.com’s article “Build custom directives with AngularJS”](http://www.ng-newsletter.com/posts/directives.html) which states: “It can be set to true (it’s false by default).” – Fredric Mar 08 '14 at 16:57
  • @Fredric Why you used `$eval` and not binding option like `=`? – Gill Bates Aug 29 '14 at 12:43
  • 1
    @GillBates Using an isolated scope and binding the argument by `=` will not reexamine the first input after it has been filled in. I made a quick [JSFiddle](http://jsfiddle.net/82oxcvz3/) that displays this problem. – Fredric Sep 18 '14 at 07:43
14

I have done it without directive.

<input type="password" ng-model="user.password" name="uPassword" required placeholder='Password' ng-minlength="3" ng-maxlength="15" title="3 to 15 characters" />
    <span class="error" ng-show="form.uPassword.$dirty && form.uPassword.$error.minlength">Too short</span>
    <span ng-show="form.uPassword.$dirty && form.uPassword.$error.required">Password required.</span><br />

    <input type="password" ng-model="user.confirmpassword" name="ucPassword" required placeholder='Confirm Password' ng-minlength="3" ng-maxlength="15" title="3 to 15 characters" />
    <span class="error" ng-show="form.ucPassword.$dirty && form.ucPassword.$error.minlength">Too short</span>
    <span ng-show="form.ucPassword.$dirty && form.ucPassword.$error.required">Retype password.</span>
    <div ng-show="(form.uPassword.$dirty && form.ucPassword.$dirty) && (user.password != user.confirmpassword)">
        <span>Password mismatched</span>
    </div>
Foyzul Karim
  • 4,252
  • 5
  • 47
  • 70
  • 4
    You should use a directive as already pointed out, the form stays in a valid state. Otherwise you could've as well made some jquery listener for it. – bicycle Oct 29 '13 at 16:45
8

As of angular 1.3.0-beta12, invalid inputs don't write to ngModel, so you can't watch AND THEN validate as you can see here: http://plnkr.co/edit/W6AFHF308nyKVMQ9vomw?p=preview. A new validators pipeline was introduced and you can attach to this to achieve the same thing.

Actually, on that note I've created a bower component for common extra validators: https://github.com/intellix/angular-validators which includes this.

angular.module('validators').directive('equals', function() {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function(scope, elem, attrs, ngModel)
        {
            if (!ngModel) return;

            attrs.$observe('equals', function() {
                ngModel.$validate();
            });

            ngModel.$validators.equals = function(value) {
                return value === attrs.equals;
            };
        }
    };
});

angular.module('validators').directive('notEquals', function() {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function(scope, elem, attrs, ngModel)
        {
            if (!ngModel) return;

            attrs.$observe('notEquals', function() {
                ngModel.$validate();
            });

            ngModel.$validators.notEquals = function(value) {
                return value === attrs.notEquals;
            };
        }
    };
});
creamcheese
  • 2,524
  • 3
  • 29
  • 55
8

https://github.com/wongatech/angular-confirm-field is a good project for this.

Example here http://wongatech.github.io/angular-confirm-field/

The code below shows 2 input fields with the implemented functionality

<input ng-confirm-field ng-model="emailconfirm" confirm-against="email" name="my-email-confirm"/>
<input ng-model="email" name="my-email" />
eddiec
  • 7,608
  • 5
  • 34
  • 36
Mauro
  • 1,447
  • 1
  • 26
  • 46
7

I've used this directive with success before:

 .directive('sameAs', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$parsers.unshift(function(viewValue) {
        if (viewValue === scope[attrs.sameAs]) {
          ctrl.$setValidity('sameAs', true);
          return viewValue;
        } else {
          ctrl.$setValidity('sameAs', false);
          return undefined;
        }
      });
    }
  };
});

Usage

     <input ... name="password" />
    <input type="password" placeholder="Confirm Password" 
name="password2" ng-model="password2" ng-minlength="9" same-as='password' required>
dmackerman
  • 2,938
  • 5
  • 26
  • 43
7

I was dealing with the same issue and found a good blog post about it written by Piotr Buda. It's a good read and it explains the process very well. The code is as follows:

directives.directive("repeatPassword", function() {
    return {
        require: "ngModel",
        link: function(scope, elem, attrs, ctrl) {
            var otherInput = elem.inheritedData("$formController")[attrs.repeatPassword];

            ctrl.$parsers.push(function(value) {
                if(value === otherInput.$viewValue) {
                    ctrl.$setValidity("repeat", true);
                    return value;
                }
                ctrl.$setValidity("repeat", false);
            });

            otherInput.$parsers.push(function(value) {
                ctrl.$setValidity("repeat", value === ctrl.$viewValue);
                return value;
            });
        }
    };
});

So you could do something like:

<input type="password" name="repeatPassword" id="repeatPassword" placeholder="repeat password" ng-model="user.repeatPassword" repeat-password="password" required>

Credit goes to the author

bicycle
  • 8,315
  • 9
  • 52
  • 72
  • After an awful lot of mucking around, this was the best solution for me when using additional validation attributes too (e.g. minimum length, required, etc.) – Carlos P Jan 10 '16 at 22:45
3

Is this not good enough:

<input type="password" ng-model="passwd1" />
<input type="password" ng-model="passwd2" />
<label ng-show="passwd1 != passwd2">Passwords do not match...</label>
<button ng-disabled="passwd1 != passwd2">Save</button>

Simple, and works just fine for me.

Jasper
  • 8,440
  • 31
  • 92
  • 133
3

This solution is similar to the one given by Dominic Watson, which uses $validators and is the one I like best. The only changes are that you can watch an expression.

$validators A collection of validators that are applied whenever the model value changes. The key value within the object refers to the name of the validator while the function refers to the validation operation. The validation operation is provided with the model value as an argument and must return a true or false value depending on the response of that validation

from https://code.angularjs.org/1.3.15/docs/api/ng/type/ngModel.NgModelController

I'm using angular 1.3. My directive looks something like this

angular.module('app').directive("passwordConfirm", function() {
    "use strict";
    return {
        require : "ngModel",
        restrict : "A",
        scope : {
            //We will be checking that our input is equals to this expression
            passwordConfirm : '&'
        },
        link : function(scope, element, attrs, ctrl) {
            //The actual validation
            function passwordConfirmValidator(modelValue, viewValue) {
                return modelValue == scope.passwordConfirm();
            }
            //Register the validaton when this input changes
            ctrl.$validators.passwordConfirm = passwordConfirmValidator;
            //Also validate when the expression changes
            scope.$watch(scope.passwordConfirm, ctrl.$validate);
        }
    };
});

To use it

<input type="password" ng-model="user.password"/>
<input type="password" ng-model="user.confirmPassword" 
                password-confirm="user.password" />
Patricio Marrone
  • 1,287
  • 15
  • 20
3

In order to validation of form with two input field,i find most suitable way of

Directive

app.directive('passwordVerify', function() {
return {
    require: 'ngModel',
    link: function (scope, elem, attrs, ctrl) {
        if (!attrs.passwordVerify) {
            return;
        }
        scope.$watch(attrs.passwordVerify, function (value) {
          if( value === ctrl.$viewValue && value !== undefined) {
             ctrl.$setValidity('passwordVerify', true);
             ctrl.$setValidity("parse",undefined);
          }
          else {
             ctrl.$setValidity('passwordVerify', false);
          }
        });
        ctrl.$parsers.push(function (value) {
            var isValid = value === scope.$eval(attrs.passwordVerify);
            ctrl.$setValidity('passwordVerify', isValid);
            return isValid ? value : undefined;
        });
    }
  };
});

HTML

     <div class="row">
        <div class="col-md-10 col-md-offset-1">
          <div class="form-group" ng-class="{ 'has-error': form.password.$dirty && form.password.$error.required || (form.password.$error.minlength || form.password.$error.maxlength)}">
              <input type="password" name="password" ng-minlength="6" ng-maxlength="16" id="password" class="form-control" placeholder="Password" ng-model="user.password" required />
              <span ng-show="form.password.$dirty && form.password.$error.required" class="help-block">Password is required</span>
              <span ng-show="form.password.$error.minlength || form.password.$error.maxlength" class="help-block">Password must be 6-16 character long</span>
          </div>
        </div>
       </div>
       <div class="row">
         <div class="col-md-10 col-md-offset-1">
           <div class="form-group" ng-class="{ 'has-error': (form.confirm_password.$dirty && form.confirm_password.$error.required) || form.confirm_password.$error.passwordVerify }">
              <input type="password" name="confirm_password" id="confirm_password" class="form-control" placeholder="Confirm Password" ng-model="user.confirm_password" required password-verify="user.password" />
              <span ng-show="form.confirm_password.$dirty && form.confirm_password.$error.required" class="help-block">Confirm Password is required</span>
              <span ng-show="form.confirm_password.$error.passwordVerify" class="help-block">Please make sure passwords match & must be 6-16 character long</span>
          </div>
        </div>
      </div>
Gautam
  • 1,668
  • 18
  • 17
2

This works both ways and it is simple and clean

JavaScript

var app = angular.module("app");

app.controller("SamePaswordController", function () {

  this.password;
  this.confirm;

  this.save = function () {
    alert("Saved!");
  };
}


app.directive("match", function () {
  return {
    restrict:"A",
    require:"ngModel",

    link: function(scope, element, attrs, ctrl) {

      function matchValidator(value) {      

        scope.$watch(attrs.match, function(newValue, oldValue) {

          var isValid = value === scope.$eval(attrs.match);                    
          ctrl.$setValidity('match', isValid);

        });

        return value;
      }

      ctrl.$parsers.push(matchValidator);
    }
  };
});

HTML: note the match directive

<form name="regForm" ng-controller="SamePaswordController as regCtrl"
      ng-submit="regForm.$valid && regCtrl.save()" novalidate>

  <input name="password" ng-model="regCtrl.password" 
         type="password" required placeholder="Password"/>                

  <input name="confirm" ng-model="regCtrl.confirm" match="regCtrl.password"
         type="password" required placeholder="Confirm password"/>


  <div> regForm is valid:{{regForm.$valid}}</div>

  <input type="submit" value="Save"/>

</form>

You can clone the repo with this example https://github.com/rogithub/roangularjs

Ro.
  • 1,525
  • 1
  • 14
  • 17
  • The nice parts are: scope.$watch, scope.$eval and match="regCtrl.password". – Ro. Apr 03 '15 at 21:24
  • This did not work for me. Watches on the confirm for changes and the example I believe is incorrect. match={{ regCtrl.password" }} – Enkode Sep 15 '15 at 09:09
2

Not a directive solution but is working for me:

<input ng-model='user.password'
 type="password"
 name='password'
 placeholder='password'
 required>
<input ng-model='user.password_verify'
 type="password" 
 name='confirm_password'
 placeholder='confirm password'
 ng-pattern="getPattern()"
 required>

And in the controller:

//Escape the special chars
    $scope.getPattern = function(){
        return $scope.user.password && 
              $scope.user.password.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1');
    }

http://plnkr.co/edit/QDTnipCsHdg56vgygsqC?p=preview

Musma
  • 924
  • 9
  • 16
1

The following is my take on the problem. This directive would compare against a form value instead of the scope.

'use strict';
(function () {
    angular.module('....').directive('equals', function ($timeout) {
        return {
            restrict: 'A',
            require: ['^form', 'ngModel'],
            scope: false,
            link: function ($scope, elem, attrs, controllers) {
                var validationKey = 'equals';
                var form = controllers[0];
                var ngModel = controllers[1];

                if (!ngModel) {
                    return;
                }

                //run after view has rendered
                $timeout(function(){
                    $scope.$watch(attrs.ngModel, validate);

                    $scope.$watch(form[attrs.equals], validate);
                }, 0);

                var validate = function () {
                    var value1 = ngModel.$viewValue;
                    var value2 = form[attrs.equals].$viewValue;
                    var validity = !value1 || !value2 || value1 === value2;
                    ngModel.$setValidity(validationKey, validity);
                    form[attrs.equals].$setValidity(validationKey,validity);
                };
            }
        };
    });
})();

in the HTML one now refers to the actual form instead of the scoped value:

<form name="myForm">
  <input type="text" name="value1" equals="value2">
  <input type="text" name="value2" equals="value1">
  <div ng-show="myForm.$invalid">The form is invalid!</div>
</form>
netbrain
  • 9,194
  • 6
  • 42
  • 68
  • Doesn't look like form values can be watched so I think that `$scope.$watch(form[attrs.equals], validate);` is never actually called. Otherwise, it would be sufficient to just have `equals` attribute on only one element. – Vadym Aug 11 '15 at 20:05
0

In order to achieve validation when both inputs change, I use the following code (which was a combination of all others other answers):

angular.module('app.directives')
.directive('passwordVerify', [function () {
    return {
        require: '?ngModel',
        restrict: 'A',
        scope: {
            origin: '=passwordVerify'
        },
        link: function (scope, element, attrs, ctrl) {
            if(!ctrl) {
                return;
            }

            function validate(value) {
                ctrl.$setValidity('passwordMatch', scope.origin === value);
                return value;
            }

            ctrl.$parsers.unshift(validate);

            scope.$watch('origin', function(value) {
                validate(ctrl.$viewValue);
            });
        }
    };
}]);
Igor Rafael
  • 489
  • 1
  • 4
  • 7
0

First, I would like to thank Fredric for posting this excellent example. There is one tiny issue that I came across by coincidence. on the Fiddle you posted http://jsfiddle.net/gUSZS/

If you type in a password, and then type in the same password in the verify input element everything works fine, but try to add a space to the second box and angular will automatically trim that space. This means that the directive doesnt "see" the extra space. Now the passwords are different, but the form is still valid.

to fix this we need to add

ng-trim="false"

to the input elements. This doesnt work in angular 1.0.3 so if you want to try it in this fiddle you need to add 1.1.1 to the Fiddle (http://ajax.googleapis.com/ajax/libs/angularjs/1.1.1/angular.js)

But again, thanx Frederic, I will use your solution in my app!

Anton P.S. I wanted to comment on Frederic's post, but Im new to this forum and dont seem to have enough credit. So it would be very much appreciated if some of you can up vote my comment if you like it :-)

0

No need for an extra directive, here's my take on this:

HTML:

<div class="form-group" data-ng-class="{ 'has-error': submitted && !form.new_passwd.$valid }">
    <input type="password" name="new_passwd" class="form-control" data-ng-model="data.new_passwd" placeholder="New Password" required data-ng-pattern="passwdRegex">
    <small class="help-block" data-ng-show="submitted && form.new_passwd.$error.required">New password is required!</small>
    <small class="help-block" data-ng-show="submitted && !form.new_passwd.$error.required && form.new_passwd.$error.pattern">New password is not strong enough!</small>
</div>

<div class="form-group" data-ng-class="{ 'has-error': submitted && !form.new_passwd_conf.$valid }">
    <input type="password" name="new_passwd_conf" class="form-control" data-ng-model="data.new_passwd_conf" placeholder="Confirm New Password" required data-ng-pattern="passwdConfRegex">
    <small class="help-block" data-ng-show="submitted && form.new_passwd_conf.$error.required">New password confirmation is required!</small>
    <small class="help-block" data-ng-show="submitted && !form.new_passwd_conf.$error.required && form.new_passwd_conf.$error.pattern">New password confirmation does not match!</small>
</div>

Javascript:

$scope.passwdRegex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,}$/;
$scope.$watch('data.new_passwd', function() {
    $scope.passwdConfRegex = new RegExp(Regex.escape($scope.data.new_passwd));
});

where Regex.escape() can be found here.

Works like a charm!

Community
  • 1
  • 1
valmarv
  • 854
  • 1
  • 8
  • 14
0

To add to the large number of already existing solutions, this works well for me.

(Jan Laussmann answer stopped working with the latest AngularJS beta releases).

directive:

angular.module('myApp').directive('matchValidator', [function() {
        return {
            require: 'ngModel',
            link: function(scope, elm, attr, ctrl) {
                var pwdWidget = elm.inheritedData('$formController')[attr.matchValidator];

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

                    if (value && pwdWidget.$viewValue) {
                        ctrl.$setValidity('match', false);
                    }

                });

                pwdWidget.$parsers.push(function(value) {
                    if (value && ctrl.$viewValue) {
                        ctrl.$setValidity('match', value === ctrl.$viewValue);
                    }
                    return value;
                });
            }
        };
    }])

usage

<input type="email" ng-model="value1" name="email" required>
<input type="email" ng-model="value2" name="emailConfirm" match-validator="email" required>

display error

<div ng-if="[[yourFormName]].emailConfirm.$error">
    <div ng-if="[[yourFormName]].emailConfirm.$error.match">
        Email addresses don't match.
    </div>
</div>
Jarrod
  • 9,349
  • 5
  • 58
  • 73
0
   <input name="password" type="text" required="" ng-model="password" placeholder="password" class="ng-dirty ng-valid ng-valid-required">
   <input name="confirm_password" type="text" required="" ng-model="confirm_password" ui-validate=" '$value==password' " ui-validate-watch=" 'password' " placeholder="confirm password" class="ng-dirty ng-valid-required ng-invalid ng-invalid-validator"> 
   <span ng-show="form.confirm_password.$error.validator">Passwords do not match!</span>
        password errors: {
        "required": false,
        "validator": true
        }
0

This worked for me.

Directive:

modulename.directive('passwordCheck', function () {

    return {
        restrict: 'A', // only activate on element attribute
        require: '?ngModel', // get a hold of NgModelController
        link: function (scope, elem, attrs, ngModel) {
            if (!ngModel) return; // do nothing if no ng-model

            var Value = null;

            // watch own value and re-validate on change
            scope.$watch(attrs.ngModel, function (val) {
                Value = val;


                validate();
            });

            // observe the other value and re-validate on change
            attrs.$observe('passwordCheck', function () {
                validate();
            });

            var validate = function () {

                // values
                var val1 = Value;
                var val2 = attrs.passwordCheck;

                // set validity

                if (val1 != '' && val1 != undefined) {
                    ngModel.$setValidity('passwordCheck', val1 == val2);

                }

                else {
                    ngModel.$setValidity('passwordCheck', true);
                }
            };
        }
    }
});

HTML:

ng-model="confirmpassword.selected" type="password" name="confirmpassword" 

password-check="{{password.selected}}"

ng-show="resetpasswordform.confirmpassword.$error.passwordCheck && submitted" Password does not match
Kukeltje
  • 12,223
  • 4
  • 24
  • 47
Meena
  • 685
  • 8
  • 31
0

I had the same problem when i was trying to build my own directive, and i fixed with this add

ctrl.$validate();

where ctrl is my ngModelController

this is my view

<input type="password" match="signupCtrl.registrationData.password" name="confirmPassword" class="form-control" placeholder="Confirm Password" data-ng-model="signupCtrl.registrationData.confirmPassword" required>
        <span ng-messages="registerForm.confirmPassword.$error">
            <span ng-message="match">The Password must match</span>
        </span>

this is my directive

(function () {
    'use strict';
    angular.module('matchDirective', [
        // Angular modules
        // Custom modules
        // 3rd Party Modules
    ]);
})(); 
(function () {
    'use strict';
    angular
        .module('matchDirective')
        .directive('match', match);
    match.$inject = ['$window'];

    function match($window) {
        // Usage:
        //     <element match="source"></element>
        // Creates:
        //
        var directive = {
            link: link,
            restrict: 'A',
            require: 'ngModel',
        };
        return directive;

        function link(scope, element, attrs, ctrl) {
            scope.$watch(attrs['match'], function (newVal, oldVal) {
                ctrl.$validators.match = function (modelValue, viewValue) {
                    if (newVal == modelValue) {
                        return true;
                    } else {
                        return false;
                    }
                }
                ctrl.$validate();
            });
        }
    }
})();
Alejandro Serret
  • 1,139
  • 15
  • 16
0

Something like this works for me:

js:

.directive('sameAs', function() { return {
    require : 'ngModel',
    link : function(scope, elm, attrs, ngModelCtrl) {

        ngModelCtrl.$validators.sameAs = function(modelValue, viewValue) {
            var checkedVal = attrs.sameAs;
            var thisInputVal = viewValue;

            if (thisInputVal == checkedVal) {
                return true; // valid
            } else {
                return false;
            }
        };
    }
}; });

html:

<input type="password" name="password" id="password" ng-model="password" />

<input type="password" name="passwordRepeat" id="passwordRepeat" 
    ng-model="passwordRepeat" same-as="{{password}}" />
Maciej Łoziński
  • 812
  • 1
  • 10
  • 16
0

The Keep It Simple And Stupid(KISS) principle might be useful on this one. Its more faster and easier to check if both passwords match by doing the following:

<div ng-app="app" ng-controller="passwordCheck">
  <form name="signUp" ng-submit="submitForm()" novalidate>
     <input type="password" name="password" ng-model="password" required>
     <input type="password" name="ConfirmPassword" ng-model="passwordconfirm"   required>
     <button type="submit"> Submit</button>
  </form>

  <hr>
  <span>Do they match?</span> {{signUp.password.$viewValue == signUp.confirmPassword.$viewValue}}
    </div>

And Before submitting the form, you can do this in your js

var app = angular.module("app", []);
app.controller("passwordCheck", function($scope) {
   $scope.submitForm = function() {
      if ($scope.signUp.$valid && $scope.signUp.password.$viewValue == $scope.signUp.confirmPassword.$viewValue) {
            alert('Its a match!');
        };
};
});

You can test it in JSfiddle as well.

Angie
  • 140
  • 1
  • 8