0

Here i am facing the problem regarding below specified third point (when user changes password value).

  • Test Scenario:
    1. user enters all field properly - [Button Enabled]
    2. if password field is invalid as it doesn't meet the password strength then the create an account button is in disable mode.
    3. If user changes password value but the password field is valid. EX: [Yash@123 to Yash@1234] here confirmation field value is Yash@123. then button should be in disable mode.

enter image description here

I have tested by accessing Parent Scope, Scope values between two directives like this.

function okPasswordDirective() {
    $element.on('blur change keydown', function( evt ) {
        if( ngPasswordModel.$valid ) {
            $scope.passwordVal = ngPasswordModel.$viewValue;
            console.log('Updated Val : ', $scope.passwordVal);
            $scope.updatePass();
        }
    }
}

function compareToDirective() {
    //use scope.$parent to associate the function called to directive function
    scope.$parent.updatePass = function() {
        console.log('$watch « Password Element Watcher.')
        console.log('Pswd: ',scope.$parent.passwordVal, '\t Cnfirm:', ngModel.$modelValue);
        //scope.registerForm.confirm.$invalid = true;
    }
}

Here in updatePass() function iam not receiving the lastcommited value of password. When i click on confirm field it used to get. but no use, i am unable to disable the button by making confirm field as invalid.

var pswd = angular.element(document.getElementById('password')).scope().registerForm.password;
var cnfm = angular.element(document.getElementById('confirmedID')).scope().registerForm.confirm;
console.log('PSWD:', pswd, '\n CNFM:', cnfm);
console.log('Diff : ', pswd.$viewValue != cnfm.$viewValue);

if( pswd.$viewValue != cnfm.$viewValue ) {
    console.log('Result : ', (pswd.$dirty && cnfm.$dirty && cnfm.$valid && ( pswd.$viewValue != cnfm.$viewValue ) ) ? 'error' : 'noErr');
    cnfm.$invalid = true;
}

Registration Form Code: fiddle

<!-- HTML CODE -->
<body ng-app="loginModule">

 <div class="main-container">
  <div class="form-container">

   <h2 class="form-label">Sign Up</h2>
    <div class="form-container" data-ng-controller="registerController" >
        <form name="registerForm" role="form" data-ng-submit="formSubmit()">
            <div class="form-group"><!-- Display Name -->
                <div class="row">
                <div class="col-md-6 col-sm-6 col-xs-6  left">
                  <div class="error form-hint" 
                    data-ng-show="registerForm.firstname.$dirty && registerForm.firstname.$error.required" 
                    data-ng-cloak>{{"This field is required."}}
                    </div>
                    <input type="text" class="form-control" name="firstname" placeholder="First name" 
                    data-ng-class="(registerForm.firstname.$dirty && registerForm.firstname.$invalid) ? 'error' : ''"
                    data-ng-required="true" data-ng-model="firstName">
                  </div>

                  <div class="col-md-6 col-sm-6 col-xs-6  right">
                    <div class="error form-hint" 
                    data-ng-show="registerForm.lastname.$dirty && registerForm.lastname.$error.required" 
                    data-ng-cloak>{{"This field is required."}}
                    </div>
                    <input type="text" class="form-control" name="lastname" placeholder="Last name" 
                    data-ng-class="(registerForm.lastname.$dirty && registerForm.lastname.$invalid) ? 'error' : ''"
                    data-ng-required="true" data-ng-model="lastName">
                    </div>
                  </div>
                </div>
                </div>
            </div>

            <div class="form-group">
                <div class="error form-hint" 
                data-ng-show="registerForm.username.$dirty && registerForm.username.$error.required" 
                data-ng-cloak>{{"This field is required."}}
                </div>

                <input type="text" class="form-control" id="userid" name="username" placeholder="User name" 
                data-ng-class="(registerForm.username.$dirty && registerForm.username.$invalid) ? 'error' : ''"
                data-ng-required="true" data-ng-model="username">
            </div>

            <div class="form-group">
                <div class="error form-hint" 
                data-ng-show="registerForm.email.$dirty && registerForm.email.$error.required" 
                data-ng-cloak>{{"You can't leave this empty."}}
                </div>
                <div class="error form-hint" 
                data-ng-show="registerForm.email.$dirty && registerForm.email.$error.email" 
                data-ng-cloak>{{"The email address you provided isn't valid"}}</div>

                <input type="email" class="form-control" id="emailid" name="email" placeholder="Email address" 
                data-ng-class="(registerForm.email.$dirty && registerForm.email.$invalid) ? 'error' : ''"
                data-ng-required="true" data-ng-model="email77">
            </div>

            <div class="form-group">
                <div class="form-hint">
                To conform with our Strong Password policy,
                Use at least one letter, one numeral, one special character, and seven characters.
                </div>

                <input type="text" class="form-control" data-ok-password-directive
                id="password" name="password" placeholder="Password" data-ng-required="true" 
                data-ng-class="(registerForm.password.$dirty && registerForm.confirm.$dirty 
                && registerForm.confirm.$valid &&
                ( registerForm.password.$viewValue != registerForm.confirm.$viewValue ) ) ? 'error' : ''"
                data-ng-model="passwordModel">

                <div class="label password-count" 
                data-ng-class="passwordModel.length > 7 ? 'label-success' : 'label-danger'" 
                data-ng-cloak>{{ passwordModel | passwordCountFilter }}</div>

                <div class="strength-meter">
                    <div class="strength-meter-fill" data-strength="{{myModulePasswordMeter}}"></div>
                </div>
            </div>

            <div class="form-group">
                <div class="error form-hint" 
                data-ng-show="registerForm.confirm.$dirty && !registerForm.confirm.$empty && registerForm.confirm.$error.required" 
                data-ng-cloak>{{"You can't leave this empty."}}
                </div>
                <div class="error form-hint" 
                data-ng-show="registerForm.confirm.$dirty && registerForm.confirm.$invalid && !registerForm.confirm.$error.required" 
                data-ng-cloak>{{"These passwords don't match. Try again?"}}</div>

                <div class="error form-hint" data-ng-show="
                ( registerForm.confirm.$dirty && registerForm.confirm.$valid && !registerForm.confirm.$invalid) && 
                ( registerForm.password.$modelValue != registerForm.confirm.$modelValue )" data-ng-cloak>
                {{'Password mismatch'}}
                </div>

                <!-- Enter to Confirm password | Enter for Password confirmation -->
                <input type="text" class="form-control" id="confirmedID" name="confirm"
                placeholder="Password confirmation" 
                data-ng-required="true" data-ng-model="confirm77"
                data-ng-class="(registerForm.confirm.$dirty &&
                ( registerForm.confirm.$invalid || 
                ( registerForm.password.$modelValue != registerForm.confirm.$modelValue ) ) ) ? 'error' : ''" 
                data-compare-to="registerForm.password" >
            </div>
            <button id="siginButton" type="submit" class="btn" data-ng-disabled="registerForm.$invalid">Create an account</button>

        </form>
    </div>

  </div>
 </div>
</body>

Script Code:

(function() {

  var loginModule = angular.module('loginModule', []);
  loginModule.constant('USERCONSTANTS', (function() {
    return {
        PASSWORD_LENGTH: 7
    }
  })());

  loginModule.controller('registerController', ['$scope','$http', '$window', '$location', registerControllerFun]);
  function registerControllerFun($scope, $http, $window, $location) {
    console.log(' registerControllerFun...');
  }
  loginModule.factory('myfactory', [function() {
    return {
        score: function() {
            //console.log('arguments List : ', arguments);
            var score = 0, value = arguments[0], passwordLength = arguments[1];
            var containsLetter = /[a-zA-Z]/.test(value), containsDigit = /\d/.test(value), containsSpecial = /[^a-zA-Z\d]/.test(value);
            var containsAll = containsLetter && containsDigit && containsSpecial;

            console.log(" containsLetter - ", containsLetter,
                    " : containsDigit - ", containsDigit,
                    " : containsSpecial - ", containsSpecial);

            if( value.length == 0 ) {
                score = 0;
            } else {
                if( containsAll ) {
                    score += 3;
                } else {
                    if( containsLetter ) score += 1;
                    if( containsDigit ) score += 1;
                    if( containsSpecial ) score += 1;
                }
                if(value.length >= passwordLength ) score += 1;
            }
            /*console.log('Factory Arguments : ', value, " « Score : ", score);*/
            return score;
        }
    };
  }]);

  loginModule.directive('okPasswordDirective', ['myfactory', 'USERCONSTANTS', function(myfactory, USERCONSTANTS) {
    return {
        restrict: 'AC',
        // use the NgModelController
        require: 'ngModel',

        // add the NgModelController as a dependency to your link function
        link: function($scope, $element, $attrs, ngPasswordModel) {
            console.log('Directive - USERCONSTANTS.PASSWORD_LENGTH : ', USERCONSTANTS.PASSWORD_LENGTH);

            $element.on('blur change keydown', function( evt ) {
                $scope.$evalAsync(function($scope) {
                    var pwd = $scope.password = $element.val();

                    $scope.myModulePasswordMeter = pwd ? (pwd.length > USERCONSTANTS.PASSWORD_LENGTH 
                            && myfactory.score(pwd, USERCONSTANTS.PASSWORD_LENGTH) || 0) : null;
                    ngPasswordModel.$setValidity('okPasswordController', $scope.myModulePasswordMeter > 3);
                });
                if( ngPasswordModel.$valid ) {
                    $scope.passwordVal = ngPasswordModel.$viewValue;
                    console.log('Updated Val : ', $scope.passwordVal);
                    $scope.updatePass();
                }
            });
        }
    };
  }]);

  loginModule.filter('passwordCountFilter', [ function() {
    var passwordLengthDefault = 7;
    return function( passwordModelVal ) {
        passwordModelVal = angular.isString(passwordModelVal) ? passwordModelVal : '';
        var retrunVal = passwordModelVal && 
            (passwordModelVal.length > passwordLengthDefault ? passwordLengthDefault + '+' : passwordModelVal.length);
        return retrunVal;
    };
  } ]);

  var compareTo = function() {
    return {
        require: "ngModel",
        // directive defines an isolate scope property (using the = mode) two-way data-binding
        scope: {
            passwordEleWatcher: "=compareTo"
        },

        link: function(scope, element, attributes, ngModel) {
            console.log('Confirm Password Link Function call.');

            var pswd = scope.passwordEleWatcher;

            ngModel.$validators.compareTo = function( compareTo_ModelValue ) {
                //console.log('scope:',scope);

                if( (pswd != 'undefined' && pswd.$$rawModelValue != 'undefined') && (pswd.$valid && pswd.$touched) ) {
                    var pswdModelValue = pswd.$modelValue;
                    var isVlauesEqual = ngModel.$viewValue == pswdModelValue;
                    return isVlauesEqual;
                } else {
                    console.log('Please enter valid password, before conforming the password.');
                    return false;
                }
            };

            scope.$watch("passwordEleWatcher", function() {
                console.log('$watch « Confirm-Password Element Watcher.')
                ngModel.$validate();
            });

            scope.$parent.updatePass = function() {
                console.log('$watch « Password Element Watcher.')
                console.log('Pswd: ',scope.$parent.passwordVal, '\t Cnfirm:', ngModel.$modelValue);
                //scope.registerForm.confirm.$invalid = true;
            }
        },
    };
  };
  loginModule.directive("compareTo", compareTo);
})(window.angular);

Any help will be appreciated.

CSS classes Observed:

ng-pristine ng-untouched ng-empty ng-invalid ng-invalid-required form-control ng-isolate-scope ng-invalid-compare-to
ng-pristine ng-touched ng-empty ng-invalid ng-invalid-required form-control ng-isolate-scope ng-invalid-compare-to

ng-dirty ng-valid-parse ng-touched ng-not-empty ng-invalid ng-valid-required error form-control ng-isolate-scope ng-invalid-compare-to
ng-dirty ng-valid-parse ng-touched ng-empty ng-invalid ng-valid-required error form-control ng-isolate-scope ng-invalid-compare-to

ng-dirty ng-valid-parse ng-touched ng-not-empty ng-valid ng-valid-required form-control ng-isolate-scope ng-valid-compare-to

I just want to change css value from ng-valid to ng-invalid

Yash
  • 7,342
  • 2
  • 55
  • 63
  • ngModel.$viewValue === pswdModelValue; this small change might get you the solution, a strict validation is needed i guess, use "===" (type-safe equality operators) rather than "==" (regular counterparts ) – Rajath M S Jul 03 '17 at 06:14
  • sorry, here the problem is not with the operator. the problem is iam unable to get the update value to `updatePass() function` after i click on confirmation field iam receiving the updated value. – Yash Jul 03 '17 at 06:19

1 Answers1

0

Just added expression [To evaluate equality of password and conformation password] to the ng-disabled- Directive of a Button.

Code « Updated Fiddle

<button id="siginButton" type="submit" class="btn" data-ng-disabled="registerForm.$invalid">
Create an account
</button>

<!-- Changed to -->

<button id="siginButton" type="submit" class="btn"
    data-ng-disabled="registerForm.$invalid 
    || ( registerForm.confirm.$dirty && registerForm.confirm.$valid && !registerForm.confirm.$invalid )
    && ( registerForm.password.$modelValue != registerForm.confirm.$modelValue )">
Create an account
</button>

Confirm Password field with validation messages.

  • You can't leave this empty.
  • Confirm password with our Strong Password Policy.
  • These passwords don't match. Try again?

Solutions:

  1. Disable Conformation Password field when the password field is invalid. Updated Fiddle

  2. User ng-pattern attribute to confirm. Updated Fiddle

Example:

<form name="registerForm" role="form" data-ng-submit="formSubmit()">
    Password:<input type="password" name="password" ng-model="password77">
    ConfirmPassword:<input type="password" name="confirm" ng-model="confirm77" ng-pattern="emailReg">

    <div class="error form-hint" ng-messages="registerForm.confirm.$error" data-ng-show="registerForm.confirm.$dirty">
        <p ng-show="registerForm.confirm.$error.required" ng-message="required">{{"You can't leave this empty."}}</p>
        <p ng-show="!(registerForm.confirm.$error.pattern && registerForm.confirm.$error.required) && registerForm.password.$invalid && !registerForm.confirm.$invalid" ng-message="pattern">{{"Confirm Password with our strong password policy!"}}</p>
        <p ng-show="registerForm.confirm.$invalid && !registerForm.confirm.$error.required && registerForm.confirm.$error.pattern" ng-message="">{{"These passwords don't match. Try again?"}}</p>
    </div>
</form>
Yash
  • 7,342
  • 2
  • 55
  • 63