2

Angular adds ng-invalid class automatically to email input field if it's invalid, how's it possible to have the class ng-invalid added to input#confirmEmail if it's doesn't match to input#email and remove if they match.

Basically I'm not required to show any messages in case of mismatch, just wanna highlight the input field through ng-invalid class and validate the form based on that.

<li>
    <label for="email">Email</label>
    <input type="email" id="email" name="email" ng-model="data.account.email" ng-required="">
</li>
<li>
    <label for="confirmEmail">Confirm Email</label>
    <input type="email" id="confirmEmail" name="confirmEmail" ng-model="data.account.confirmEmail">
</li>
PSL
  • 123,204
  • 21
  • 253
  • 243

3 Answers3

4

Expanding my comment to answer. Here is a simple compare directive validation which can be used to in-validate the form and the inputs if it does not match the entered value. Making use of ngModelController's (>= V1.3.x) $validators it becomes easy to handle it.

Directive will look like this:

.directive('comparewith', ['$parse', function($parse){
  return {
     require:'ngModel',
      link:function(scope, elm, attr, ngModel){
        //Can use $parse or also directly comparing with scope.$eval(attr.comparewith) will work as well
        var getter = $parse(attr.comparewith);

        ngModel.$validators.comparewith = function(val){
          return val === getter(scope);
        }
        scope.$watch(attr.comparewith, function(v, ov){
          if(v !== ov){
             ngModel.$validate();
          }
        });
      }
  }
}]);

and use it as:

<li>
    <label for="email">Email</label>
    <input type="email" id="email" name="email" 
          ng-model="data.account.email" required>
</li>
<li>
    <label for="confirmEmail">Confirm Email</label>
    <input type="text" id="confirmEmail" name="confirmEmail"
           comparewith="data.account.email" required 
           ng-model="data.account.confirmEmail">
</li>

Demo

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

app.controller('MainCtrl', function($scope) {
  $scope.submit = function(form) {
    if (form.$invalid) {
      alert("oops password and confirm must match and must be valid");
    } else {
      alert("Äll good!!");
    }
  };
}).directive('comparewith', ['$parse', function($parse) {
  return {
    require: 'ngModel',
    link: function(scope, elm, attr, ngModel) {
      var getter = $parse(attr.comparewith);

      ngModel.$validators.comparewith = function(val) {
        return val === getter(scope);
      }
      
      scope.$watch(attr.comparewith, function(v, ov){
         if(v !== ov){
            ngModel.$validate();
         }
      });
    }
  }
}]);
/* Put your css in here */

form.ng-invalid {
  border: 1px solid red;
  box-sizing: border-box;
}
input.ng-invalid {
  border: 2px solid red;
  box-sizing: border-box;
}
<!DOCTYPE html>
<html ng-app="plunker">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>
  <link rel="stylesheet" href="style.css" />
  <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
  <script src="app.js"></script>
</head>

<body ng-controller="MainCtrl">
  <form name="form" ng-submit="submit(form)" novalidate>
    <ul>
      <li>
        <label for="email">Email</label>
        <input type="email" id="email" name="email" ng-model="data.account.email" required>
      </li>
      <li>
        <label for="confirmEmail">Confirm Email</label>
        <input type="text" id="confirmEmail" name="confirmEmail" comparewith="data.account.email" required ng-model="data.account.confirmEmail">
      </li>
    </ul>
    <button>Submit</button>
  </form>
</body>

</html>
PSL
  • 123,204
  • 21
  • 253
  • 243
  • can you please make it minification safe, not sure why I was having an issue after minifcation error: `http://errors.angularjs.org/1.3.9/$injector/unpr?p0=eProvider%20%3C-%20e%20%3C-%20matchDirective` fyi: I'm using word "match" instead of "compareWith" –  Jan 23 '15 at 23:08
1

You can use ng-class and just check if the models are equal:

ng-class="{'ng-invalid' : data.account.confirmEmail != data.account.email}"
tymeJV
  • 103,943
  • 14
  • 161
  • 157
  • Clever!! :) But will it make the form invalid? the field will still be valid right? I believe we need to add custom validation and set validity of the ng-model which will take care of class addition as well as form's/control's state to be invalid/valid.? – PSL Jan 19 '15 at 22:06
  • also if I enter a valid email to `#confirmEmail` but mismatch to `#email` the `ng-invalid` disappears because it's a valid email but it's still a mismatch, it gets in conflict with angular's validation, if there's no solution I'll use a different class to avoid the conflict, let's say `.mismatch {}` –  Jan 19 '15 at 22:08
  • @user3610227 You may have to create custom validator. Try this question. http://stackoverflow.com/questions/14012239/password-check-directive-in-angularjs – PSL Jan 19 '15 at 22:32
  • I've changed the confirmEmail type from email to text and it works without any conflict. The email textfield is enough for email validation while confirmEmail should be for matching text only therefore it should be typed text, any possible problem with this approach, guys? –  Jan 19 '15 at 22:32
  • @user3610227 That is not a bad approach, clever indeed and you can just add to validator list as well with a directive, something like this http://plnkr.co/edit/8Tomo9?p=preview – PSL Jan 19 '15 at 23:02
  • @PSL thanks for the link, I understand this will actually force the form to be in invalid state as well. –  Jan 19 '15 at 23:30
  • @user3610227 Yes, the would be right way IMO because you are just making use of angular validation with just providing a validator function. Cheers! – PSL Jan 19 '15 at 23:31
  • @PSL can you please write it as an answer –  Jan 20 '15 at 16:42
  • @user3610227 are u sure. I can add the answer once I get back... thanks. – PSL Jan 20 '15 at 17:08
0

You can add it all in the html

<li>
   <label for="email">Email</label>
   <input type="email" id="email" name="email" ng-model="data.account.email" 
          ng-required="">
</li>
<li>
   <label for="confirmEmail">Confirm Email</label>
   <input type="email" id="confirmEmail" name="confirmEmail" 
          ng-model="data.account.confirmEmail" ng-pattern="{{data.account.email}}">

   <div ng-messages="data.account.confirmEmail.$error">
        <div ng-message="pattern">Confirm email must match email.</div>
   </div>
</li>

you need ngMessages for this