thenetimp
thenetimp

Reputation: 9820

How to restrict directive to single element if used twice in the same template

I have a directive I have created to validate password strength.

  /**
   *
   * A directive that compares the model value of the associated input
   * against an algorythm to prevent poor passwords.
   *
   * Passwords must contain the following.
   * at least one uppercase letter
   * at least one lowercase letter
   * at least one numeric character
   * at least one special character
   * No more than 2 sequential alpha characters
   * No more than 2 sequential numeric characters
   * No more than 2 consecutive alpha characters
   * No consecutive numeric characters
   * No consecutive symbols
   *
   */
  app.directive('passwordStrength',[function()
  {
    return {
      restrict: "A",
      require: 'ngModel',
      link: function($scope, $element, $attrs, ctrl)
      {
        if(!ctrl) return;

        // Observe when a change is made to the comparitor value
        $attrs.$observe('passwordStrength', function(value)
        {
          ctrl.$validate();
        });

        // 
        ctrl.$validators.passwordStrength = function(modelValue, viewValue)
        {
          // Strings to use to validate against sequentials.
          // and accepted special characters.
            var sAlphas = "abcdefghijklmnopqrstuvwxyz";
            var sNumerics = "0123456789";
            var sSymbols = '?!@#$%^&*(){}[]<>';
          var sFwd = "";
          var sRev = "";

          var alphaFail = false;
          var numericFail = false;
          var alphaConsecFail = false;
          var numericConsecFail = false;
          var symbolConsecFail = false;

          console.log($element.value);

          // Fail if the password doesn't contain at leasto one uppercase letter
          if(!modelValue.match(/[A-Z]/))
          {
            ctrl.$setValidity('alphaUpper', false);
            console.log("Failed uppercase character check");
            return false;
          }

          // Fail if the password doesn't contain at leasto one lowercase letter
          if(modelValue.match(/[a-z]/))
          {
            ctrl.$setValidity('alphaLower', false);
            console.log("Failed lowercase character check");
            return false;
          }

          // Fail if the password doesn't contain at least one number
          if(modelValue.match(/[0-9]/))
          {
            ctrl.$setValidity('numeric', false);
            console.log("Failed numeric character check");
            return false;
          }

          // Make sure that there are no sequential letters of 3 or more.
          for (var s=0; s < 23; s++)
          {
              sFwd = sAlphas.substring(s,parseInt(s+3)).toLowerCase();
                    sRev = sFwd.strReverse();
              if(
                (modelValue.indexOf(sFwd) != -1 && modelValue != "") ||
                (modelValue.indexOf(sRev) != -1 && modelValue != "")
              )
              {
                alphaFail = true;
              }
          }

          if(alphaFail)
          {
            console.log("Failed sequential alpha character check");
            ctrl.$setValidity('serialAlpha', false);
            return false;
          }

          // Make sure that there are no sequential letters of 3 or more.
          var numericFail = false;
          for (var s=0; s < 8; s++)
          {
              sFwd = sNumerics.substring(s,parseInt(s+3));
                    sRev = sFwd.strReverse();
              if(
                (modelValue.indexOf(sFwd) != -1 && modelValue != "") ||
                (modelValue.indexOf(sRev) != -1 && modelValue != "")
              )
              {
                numericFail = true;
              }
          }

          if(numericFail)
          {
            console.log("Failed sequential numeric character check");
            ctrl.$setValidity('serialNumeric', false);
            return false;
          }

          // Look for consecutive letters
          for (var s=0; s < 26; s++)
          {
            testCase = Array(4).join(sAlphas.substring(s,parseInt(s+1)));
            if(
              (modelValue.indexOf(testCase) != -1 && modelValue != "") || 
              (modelValue.indexOf(testCase.toUpperCase()) != -1 && modelValue != "")
            )
            {
              alphaConsecFail = true;
            }
          }

          if(alphaConsecFail)
          {
            console.log("Failed consecutive alpha character check");
            ctrl.$setValidity('consecutiveAlpha', false);
            return false;
          }

          // Look for consecutive numbers
          for (var s=0; s < 10; s++)
          {
            testCase = Array(4).join(sNumerics.substring(s,parseInt(s+1)));
            if(modelValue.indexOf(testCase) != -1 && modelValue != "")
            {
              numericConsecFail = true;
            }
          }

          if(numericConsecFail)
          {
            console.log("Failed consecutive number character check");
            ctrl.$setValidity('consecutiveNum', false);
            return false;
          }

          // Look for consecutive symbols
          for (var s=0; s < 10; s++)
          {
            testCase = Array(3).join(sSymbols.substring(s,parseInt(s+1)));
            if(modelValue.indexOf(testCase) != -1 && modelValue != "")
            {
              symbolConsecFail = true;
            }
          }

          if(symbolConsecFail)
          {
            console.log("Failed consecutive symbol character check");
            ctrl.$setValidity('consecutiveSym', false);
            return false;
          }

          console.log("Succeeded vaidation");
          ctrl.$setValidity('alphaUpper', true);
          ctrl.$setValidity('alphaLower', true);
          ctrl.$setValidity('numeric', true);
          ctrl.$setValidity('serialAlpha', true);
          ctrl.$setValidity('serialNumeric', true);
          ctrl.$setValidity('consecutiveAlpha', true);
          ctrl.$setValidity('consecutiveNum', true);
          ctrl.$setValidity('consecutiveSym', true);
          return true;
        }
      }
    };
  }]); 

If I attach this to 2 different form fields inside the same HTML template for some reason instead of validating them separately like I expected it seems to validate both of them at the same time which basically breaks the validation. I don't understand what I need todo to get them to validate separately.

Thoughts?

Upvotes: 0

Views: 227

Answers (2)

thenetimp
thenetimp

Reputation: 9820

While I already gave a correct answer to @jsonmurphy I found that the following also works.

return {
  restrict: "A",
  require: '^ngModel',
  link: function($scope, $element, $attrs, ctrl){
  ...
}

}

Upvotes: 0

jsonmurphy
jsonmurphy

Reputation: 1600

Perhaps you could isolate the scope of the directive by setting scope: true in the object returned:

 return {
  restrict: "A",
  scope: true,
  require: 'ngModel',
  link: function($scope, $element, $attrs, ctrl){
  ...
  }
 }

More info: angularjs directive scope default value?

Upvotes: 1

Related Questions