scope.$eval() is undefined if both input fields have the same directive

124 views Asked by At

I'm trying to validate two password input fields. Simply confirm that they are equal. (Suggest another approach if mine is way wrong)

I have implemented a directive with a simple validation that checks if the "confirm" password is the same as the original. But the directive also checks for other things, so I need to have both input fields to have it.

The problem is that when I have my directive on both input fields, I cannot read their model values through the attribute (to check if they match).

Here is a working demo without the directive on the first password:

var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {

});
app.directive('myDir', function() {
  return {
    require: 'ngModel',
    link: function(scope, elem, attrs, ctrl) {
      ctrl.$validators.mismatch = function(modelValue, viewValue) {
        // MAIN CODE:
        return viewValue === scope.$eval(attrs.confirm);
      };

      ctrl.$validators.short = function(modelValue, viewValue) {
        if (ctrl.$isEmpty(modelValue)) {
          return true;
        }
        if (modelValue.length >= 3) {
          return true;
        }
        return false;
      }
    }
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>

<div ng-app="myApp" ng-controller="myCtrl">

  <form name="form1">
    <input type="password" name="password1" ng-model="pass1"><br>
    <input type="password" my-dir confirm="pass1" name="password2" ng-model="pass2"><br>
    <pre>{{form1.password2.$error | json}}</pre>
    <p ng-show="form1.password2.$error.mismatch" style="color:red">Passwords are different</p>
  </form>

</div>

If I change the first filed to:

<input type="password" my-dir confirm="pass2" name="password1" ng-model="pass1">

to validate in both directions, then scope.$eval(attrs.confirm) becomes undefined for both fields.

Here is a demo of my issue:

var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {

});
app.directive('myDir', function() {
  return {
    require: 'ngModel',
    link: function(scope, elem, attrs, ctrl) {
      ctrl.$validators.mismatch = function(modelValue, viewValue) {
        // `scope.$eval(attrs.confirm)` always undefined
        return viewValue === scope.$eval(attrs.confirm);
      };

      ctrl.$validators.short = function(modelValue, viewValue) {
        if (ctrl.$isEmpty(modelValue)) {
          return true;
        }
        if (modelValue.length >= 3) {
          return true;
        }
        return false;
      }
    }
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>

<div ng-app="myApp" ng-controller="myCtrl">

  <form name="form1">
    <input type="password" my-dir confirm="pass2" name="password1" ng-model="pass1"><br>
    <input type="password" my-dir confirm="pass1" name="password2" ng-model="pass2"><br>
    <pre>{{form1.password2.$error | json}}</pre>
    <p ng-show="form1.password2.$error.mismatch || form1.password.$error.mismatch" style="color:red">
      Passwords are different
    </p>
  </form>

</div>

1

There are 1 answers

2
Petr Averyanov On

You need to do 2 things: 1. Add ng-model-options="{allowInvalid: true}" so invalid value will still update scope value. 2. Now you have problem that e.g. changing 2nd input wont trigger 1st re-validation. This is done using observe:

  <body ng-controller="MainCtrl" ng-init="x = 0; y = 0">
    <form name="form1">
    <input type="password" my-dir="{{y}}" confirm="pass2" name="password1" ng-model="pass1" ng-model-options="{allowInvalid: true}"
       ng-change="x = x + 1"><br>
    <input type="password" my-dir="{{x}}" confirm="pass1" name="password2" ng-model="pass2" ng-model-options="{allowInvalid: true}"
       ng-change="y = y + 1"><br>

and

attrs.$observe('myDir', function() {
    ctrl.$validate();
});

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

P.S. for usual fields I would write my-dir="{{pass1}}" and then no need in $eval and ng-change, but for passwords... not sure