Dismiss
Announcing Stack Overflow Documentation

We started with Q&A. Technical documentation is next, and we need your help.

Whether you're a beginner or an experienced developer, you can contribute.

Sign up and start helping → Learn more about Documentation →

I am trying to do a form validation using AngularJS. I am especially interested in comparing two values. I want the user to confirm some data he entered before he continues. Lets say I have the code below:

<p>
    Email:<input type="email" name="email1" ng-model="emailReg">
    Repeat Email:<input type="email" name="email2" ng-model="emailReg2">
<p>

and then I can use validation with:

<span ng-show="registerForm.email1.$error.required">Required!</span>
<span ng-show="registerForm.email1.$error.email">Not valid email!</span>
<span ng-show="emailReg !== emailReg2">Emails have to match!</span>  <-- see this line

registerForm.$valid will react correctly as to the text in inputs except I do not know how to use comparison within this validation to force the emails to be the same before allowing the user to submit the form.

I would love to have a solution without custom directives, but if this can't be achieved without it I will deal with it. Here is an answer that addresses similar issue with a custom directive.

Any help appreciated, thank you

share|improve this question
3  
How about ng-disabled="emailReg != emailReg2" on the submit button? – CodeHater Mar 4 '14 at 13:18

13 Answers 13

up vote 26 down vote accepted

One way to achieve this is with a custom directive. Here's an example using the ngMatch directive:

<p>Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-match="emailReg"></p>

<span data-ng-show="myForm.emailReg2.$error.match">Emails have to match!</span>

NOTE: It's not generally recommended to use ng- as a prefix for a custom directive because it may conflict with an official AngularJS directive.

Update

It's also possible to get this functionality without using a custom directive:

HTML

<button ng-click="add()></button>
<span ng-show="IsMatch">Emails have to match!</span>

Controller

$scope.add = function() {
  if ($scope.emailReg != $scope.emailReg2) {
    $scope.IsMatch=true;
    return false;
  }
  $scope.IsMatch=false;
}
share|improve this answer
    
Thanks for your answer. So I do have to use directive for this to work… no other way around probably so far? – trainoasis Mar 4 '14 at 13:34
1  
@Why directive for this? you can simply use my code. It's relay easy. otherwise you need manually write some code – Ramesh Rajendran Mar 4 '14 at 13:36
    
@trainoasis I have updated my answer, please check it now – Ramesh Rajendran Mar 4 '14 at 13:40
1  
Thank you, this is good - and I can still copy the directive from the page you linked and it works as well, so I will see what works best later. – trainoasis Mar 4 '14 at 13:43
15  
ng-match is not a validation directive that is provided by the AngularJS framework. Please do not name your own custom directives with ng-*. The valid list of existing validators is available at docs.angularjs.org/api/ng/directive/input – Ryan Kimber Jul 23 '14 at 15:56

trainosais - you are right, validation should be done on a directive level. It's clean, modular and allows for reusability of code. When you have basic validation like that in a controller you have write it over and over again for different forms. That's super anti-dry.

I had a similar problem recently and sorted it out with a simple directive, which plugs in to the parsers pipeline, therefore stays consistent with Angular architecture. Chaining validators makes it very easy to reuse and that should be considered the only solution in my view.

Without further ado, here's the simplified markup:

<form novalidate="novalidate">
    <label>email</label>
    <input type="text"
        ng-model="email"
        name="email" />
    <label>email repeated</label>
    <input ng-model="emailRepeated"
        same-as="email"
        name="emailRepeated" />
</form>

And the JS code:

angular.module('app', [])
    .directive('sameAs', function() {
        return {
            require: 'ngModel',
            link: function(scope, elem, attrs, ngModel) {
                ngModel.$parsers.unshift(validate);

                // Force-trigger the parsing pipeline.
                scope.$watch(attrs.sameAs, function() {
                    ngModel.$setViewValue(ngModel.$viewValue);
                });

                function validate(value) {
                    var isValid = scope.$eval(attrs.sameAs) == value;

                    ngModel.$setValidity('same-as', isValid);

                    return isValid ? value : undefined;
                }
            }
        };
    });

The directive hooks into the parsers pipeline in order to get notified of any changes to the view value and set validity based on comparison of the new view value and the value of the reference field. That bit is easy. The tricky bit is sniffing for changes on the reference field. For that the directive sets a watcher on the reference value and force-triggeres the parsing pipeline, in order to get all the validators run again.

If you want to play with it, here is my pen: http://codepen.io/jciolek/pen/kaKEn

I hope it helps, Jacek

share|improve this answer
    
Thanks alot dude, I had been trying for hours to work out how to fire the $parsers ngModel.$setViewValue(ngModel.$viewValue); worked for me. – Theo Kouzelis Nov 7 '14 at 16:20
    
No worries :) I am glad I could help. – Jacek Ciolek Nov 9 '14 at 19:11
1  
Sweet! This is the magic juju I was looking for. ng-love – Rod Hartzell Jun 3 '15 at 22:43
    
This worked for me > wijmo.com/easy-form-validation-in-angularjs – Soichi Hayashi Dec 19 '15 at 22:05
    
On Angular 1.5.0, ngModel.$setViewValue(ngModel.$viewValue); is not causing the $parsers to run. I had to do validate(ngModel.$viewValue); instead. – Cabloo Aug 22 at 23:58

You should be able to use ng-pattern/regex for comparing 2 input values

Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">

and validation with:

<span ng-show="registerForm.email2.$error.pattern">Repeat Email should have the same value with email!</span>
share|improve this answer
    
It won't work in the following case: email: abc, email2: abcd – Manto Feb 14 '15 at 1:10
    
it should work but if you are putting 'abc' or 'abcd' as input, those are not valid email addresses which probably failed the validation ahead of going into the pattern validation. – Henry Neo Feb 15 '15 at 22:20
    
this is close, just put the emailReg inside parentheses, I will add an example below. – dmo Aug 28 '15 at 18:07
1  
This is brilliant. Already implemented a custom directive which is what everyone seems to suggest, but I'm totally ditching it since this feels to me a lot more in tune with the 'Angular way' of doing things! – Boris Oct 2 '15 at 8:53
    
Awesome! Perfectly works! – Vinay Jul 18 at 15:19

I recently wrote a custom directive which can be generic enough to do any validation. It take a validation function from the current scope

module.directive('customValidator', [function () {
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: { validateFunction: '&' },
            link: function (scope, elm, attr, ngModelCtrl) {
                ngModelCtrl.$parsers.push(function (value) {
                    var result = scope.validateFunction({ 'value': value });
                    if (result || result === false) {
                        if (result.then) {
                            result.then(function (data) {           //For promise type result object
                                ngModelCtrl.$setValidity(attr.customValidator, data);
                            }, function (error) {
                                ngModelCtrl.$setValidity(attr.customValidator, false);
                            });
                        }
                        else {
                            ngModelCtrl.$setValidity(attr.customValidator, result);
                            return result ? value : undefined;      //For boolean result return based on boolean value
                        }
                    }
                    return value;
                });
            }
        };
    }]);

To use it you do

<input type="email" name="email2" ng-model="emailReg2" custom-validator='emailMatch' data-validate-function='checkEmailMatch(value)'>
<span ng-show="registerForm.email2.$error.emailMatch">Emails have to match!</span>

In you controller then you can implement the method, that should return true or false

$scope.checkEmailMatch=function(value) {
    return value===$scope.emailReg;
}

The advantage is that you do not have to write custom directive for each custom validation.

share|improve this answer
    
thanks this is useful! – trainoasis Jul 28 '14 at 17:51
    
very nice... this is def the way to go with custom validation - ngModel! – Sten Muchow Sep 24 '14 at 8:23
    
@chandermani - what is this: attr.appcrestCustomValidator, i dont see the appcrest anywhere... – Sten Muchow Sep 24 '14 at 9:30
    
Sorry, this needs to be fixed :(. I released this as validation library, check this blog blog.technovert.com/2014/03/… and the source here github.com/technovert/angular-validation – Chandermani Sep 24 '14 at 9:48
    
I have fixed it, this code was taken from a project, which i generalized later. – Chandermani Sep 24 '14 at 9:50

When upgrading angular to 1.3 and above I found an issue using Jacek Ciolek's great answer:

  • Add data to the reference field
  • Add the same data to the field with the directive on it (this field is now valid)
  • Go back to the reference field and change the data (directive field remains valid)

I tested rdukeshier's answer (updating var modelToMatch = element.attr('sameAs') to var modelToMatch = attrs.sameAs to retrieve the reference model correctly) but the same issue occurred.

To fix this (tested in angular 1.3 and 1.4) I adapted rdukeshier's code and added a watcher on the reference field to run all validations when the reference field is changed. The directive now looks like this:

angular.module('app', [])
  .directive('sameAs', function () {
    return {
      require: 'ngModel',
      link: function(scope, element, attrs, ctrl) {
        var modelToMatch = attrs.sameAs;      

        scope.$watch(attrs.sameAs, function() {
          ctrl.$validate();          
        })

        ctrl.$validators.match = function(modelValue, viewValue) {
          return viewValue === scope.$eval(modelToMatch);
        };
      }
   };
});

Updated codepen

share|improve this answer
    
This is necessary for 1.4 and above. Thanks! – user1916988 Jan 18 at 16:57

@Henry-Neo's method was close, it just needed more strict Regex rules.

<form name="emailForm">
    Email: <input type="email" name="email1" ng-model="emailReg">
    Repeat Email: <input type="email" name="email2" ng-model="emailReg2" ng-pattern="(emailReg)">
</form>

By including the regex rule inside parentheses, it will match the entire string of emailReg to emailReg2 and will cause the form validation to fail because it doesn't match.

You can then drill into the elements to find out which part is failing.

 <p ng-show="emailForm.$valid">Form Valid</p>
 <p ng-show="emailForm.email1.$error">Email not valid</p>
 <p ng-show="emailForm.email1.$valid && emailForm.email1.$error.pattern">
     Emails Do Not Match
 </p>
share|improve this answer
2  
This does not work if emailReg is .* , then you can enter anything as emailReg2. – Laszlo B Sep 9 '15 at 13:55
    
won't work also for [email protected] – Alessio Dec 3 '15 at 13:39

use ng-pattern, so that ng-valid and ng-dirty can act correctly

Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">

<span ng-show="registerForm.email2.$error.pattern">Emails have to match!</span>
share|improve this answer

This module works well for comparing two fields. Works great with Angular 1.3+. Simple to use https://www.npmjs.com/package/angular-password

It also allows saving the module as a generic. Just include them in packages list of your module.

share|improve this answer

Here is an angular 1.3 version of the sameAs directive:

angular.module('app').directive('sameAs', [function() {
  'use strict';

  return {
    require: 'ngModel',
    restrict: 'A',
    link: function(scope, element, attrs, ctrl) {
      var modelToMatch = element.attr('sameAs');      
      ctrl.$validators.match = function(modelValue, viewValue) {
        return viewValue === scope.$eval(modelToMatch);
      };
    }
  };
}]);
share|improve this answer

No need a function or a directive. Just compare their $modelValue from the view:

ng-show="formName.email.$modelValue !== formName.confirmEmail.$modelValue"

More detailed example:

<span ng-show="(formName.email.$modelValue !== formName.confirmEmail.$modelValue) 
                && formName.confirmEmail.$touched
                && !formName.confirmEmail.$error.required">Email does not match.</span>

Please note that the ConfirmEmail is outside the ViewModel; it's property of the $scope. It doesn't need to be submitted.

share|improve this answer

Mine is similar to your solution but I got it to work. Only difference is my model. I have the following models in my html input:

ng-model="new.Participant.email"
ng-model="new.Participant.confirmEmail"

and in my controller, I have this in $scope:

 $scope.new = {
        Participant: {}
    };

and this validation line worked:

<label class="help-block" ng-show="new.Participant.email !== new.Participant.confirmEmail">Emails must match! </label>
share|improve this answer

Thanks for the great example @Jacek Ciolek. For angular 1.3.x this solution breaks when updates are made to the reference input value. Building on this example for angular 1.3.x, this solution works just as well with Angular 1.3.x. It binds and watches for changes to the reference value.

angular.module('app', []).directive('sameAs', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    scope: {
      sameAs: '='
    },
    link: function(scope, elm, attr, ngModel) {
      if (!ngModel) return;

      attr.$observe('ngModel', function(value) {
        // observes changes to this ngModel
        ngModel.$validate();
      });

      scope.$watch('sameAs', function(sameAs) {
        // watches for changes from sameAs binding
        ngModel.$validate();
      });

      ngModel.$validators.sameAs = function(value) {
        return scope.sameAs == value;
      };
    }
  };
});

Here is my pen: http://codepen.io/kvangrae/pen/BjxMWR

share|improve this answer

You have to look at the bigger problem. How to write the directives that solve one problem. You should try directive use-form-error. Would it help to solve this problem, and many others.

    <form name="ExampleForm">
  <label>Password</label>
  <input ng-model="password" required />
  <br>
   <label>Confirm password</label>
  <input ng-model="confirmPassword" required />
  <div use-form-error="isSame" use-error-expression="password && confirmPassword && password!=confirmPassword" ng-show="ExampleForm.$error.isSame">Passwords Do Not Match!</div>
</form>

Live example jsfiddle

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.