AngularJS Password Strength Validation - angularjs

I was wondering if you would be able to help me out. I am trying to get validation to work for a password field. The password must comply to certain criteria to be valid and once each criteria is met it is checked off on the fly.
To do this I created 3 hidden fields each of which copies the value from the password field and validates it against one of the 3 criteria it requires. Ie one of the criteria requires the user to input a number, once they do this the text stating a number is required turns green. Same thing happens for the other two hidden fields. these hidden fields can be seen below:
<!--7-25 characters -->
<input required ng-model="password" ng-pattern="/.{7,25}/" name="length" type="text" />
<!--Contain at least 1 letter -->
<input required ng-model="password" ng-pattern="/[a-zA-Z]+/" name="letter" type="text" />
<!--Contain at least 1 number -->
<input required ng-model="password" ng-pattern="/[0-9]+/" name="number" type="text" />
For the actual password field I then wanted to create a directory which would only set that field as valid if the three hidden fields are also valid.
My directory looks like this (my form is named registration and to begin with im only testing it against the length hidden field):
app.directive('validPwd', function() {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, elem, attrs, ngModel) {
if(!ngModel) return;
scope.$watch(attrs.ngModel, function() {
validate();
});
var validate = function() {
// set validity
ngModel.$setValidity('validPwd', registration.length.$valid);
};
}
}
});
My issue with this is that my directive is giving me the error that it cannot read property length of undefined. Am I not referring to it correctly or what? I cant seem to figure it out.
Any help would be greatly appreciated.

That's a very convoluted way to validate your field. You shouldn't pollute your view with 3 hidden fields just to validate a 4th one. Why don't you simply check each condition inside a custom validator, as explained in the documentation?
app.directive('validPwd', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
var containsLetter = containsLetter(viewValue);
var containsDigit = containsDigit(viewValue);
var hasCorrectLength = hasCorrectLength(viewValue);
ctrl.$setValidity('containsLetter', containsLetter);
ctrl.$setValidity('containsDigit', containsDigit);
ctrl.$setValidity('hasCorrectLength', hasCorrectLength);
if (containsLetter && containsDigit && hasCorrectLength) {
return viewValue;
}
else {
return undefined;
}
});
}
};
});

Related

trim the trailing space and show placeholder when there is no visible input - Angularjs

Consider the following input
<input ng-if="isRequired()"
type="text"
name="{{fieldNamePrefix}}[name]"
ng-value="orderCommodity.name"
class="form-control top15"
ng-disabled="disabled"
placeholder="CERTIFICATION" />
Now consider that that the user enters a space and moves to the next field. I need to trim that space and show the placeholder again since there is no visible input.
I have tried ng-trim but doesn't seem to work. Any idea on how to fix this?
You will need a custom directive. It triggers on blur and checks input value. If it contains only spaces then it will empty the value. Note that you will need to use ng-model instead of ng-value:
myApp.directive('trimDir', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
element.bind('blur', function(e) {
console.log(ctrl)
let trimmed = ctrl.$viewValue.replace(/[\s]/g, '');
if (trimmed.length == 0) {
ctrl.$setViewValue('');
ctrl.$render();
}
});
}
};
})
And in html:
<input type="text"
name="name"
ng-model="orderCommodity.name"
class="form-control top15"
placeholder="CERTIFICATION"
trim-dir />
Check a working demo here: DEMO

In AngularJS, how to force the re-validation of a field in a form when another value in the same form is changed?

I have a form with few fields, however a select and an input field are coupled: the validation on the input depends on which value the user chooses in the select field.
I'll try to clarify with an example. Let's say that the select contains names of planets:
<select id="planet" class="form-control" name="planet" ng-model="planet" ng-options="c.val as c.label for c in planets"></select>
in the input I apply custom validation via a custom directive named "input-validation":
<input id="city" input-validation iv-allow-if="planet==='earth'" class="form-control" name="city" ng-model="city" required>
where this is the directive:
.directive('inputValidation', [function() {
return {
require: 'ngModel',
restrict: 'A',
scope: {
ivAllowIf: '='
},
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
//input is allowed if the attribute is not present or the expression evaluates to true
var inputAllowed = attrs.ivAllowIf === undefined || scope.$parent.$eval(attrs.ivAllowIf);
if (inputAllowed) {
ctrl.$setValidity('iv', true);
return viewValue;
} else {
ctrl.$setValidity('iv', false);
return undefined;
}
});
}
};
}])
The full example can be examined in Plnkr: http://plnkr.co/edit/t2xMPy1ehVFA5KNEDfrf?p=preview
Whenever the select is modified, I need the input to be verified again. This is not happening in my code. What am I doing wrong?
I have done the same thing for validation of start-date on change of end-date. In the directive of start-date add watch for change of end-date and then call ngModel.$validate() in case end-date new value is defined.
scope.$watch(function () {
return $parse(attrs.endDate)(scope);
}, function () {
ngModel.$validate();
});
The important part to take is call to ngModel.$validate() inside the directive.
Note
you should use $validators for custom validations above to work. read here, $parsers is the old way - from angularjs 1.3 use $validators
FIXED PLUNKER LINK

How to call Angular's internal email validation method

Is it possible to call Angular's internal email validation method from JavaScript rather than declarative in the markup?
Thank you.
EDIT:
To answer all, the reason I am looking for this is so that email validation is consistent when using both the normal <input type="email"/> validation as well as when using programmatic validation.
The short answer is no. Angular uses a series of custom regular expressions to validate emails, numbers, etc on form elements. You see the source for that here.
The regex they use on <input type="email"> elements (in 1.2.14) is as follows:
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+#[a-z0-9-]+(\.[a-z0-9-]+)*$/i;
But there isn't any way to access that regex programatically.
I came to this question because I had to validate an input field where users can enter a list of email addresses.
So I wrote a custom directive that is placed on a "regular" email input:
angular.module('myModule').directive('listOfEmails', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ngModel) {
ngModel.$validators.listofemails = function(modelValue, viewValue) {
var value = modelValue || viewValue,
length, i;
if ( ! angular.isDefined(value) ) {
return true;
}
length = value.length;
for(i=0; i < length; i++) {
if ( ! ngModel.$validators.email(value[i]) ) {
return false;
}
}
//if nothing is invalid, it's valid:
ngModel.$setValidity('email', true);
return true;
};
}
});
And in the HTML:
<input type="email" ng-list ng-model="addresses" list-of-emails name="toAdresses" />
Depending on your concrete usecase you may mock something up with this or with custom form controls:
https://docs.angularjs.org/guide/forms#implementing-custom-form-controls-using-ngmodel-

Multiple validation directives not working

I'm trying to write my own set of directives. I have written following two directives:
eaValidateEmail (Validates the format of the email address)
eaValidateUnique (Will validate the uniqueness by a call to a rest service once complete)
What I want to achieve:
First the eaValidateEmail directive is executed which returns false until the format of the email is correct
Then and only then the eaValidateUnique directive should execute and check if the email address is taken already over a rest service. If the value is not found it will return true, else it will return false.
What's happening
When I only add the eaValidateEmail directive, everything is working and the format of the email is validated.
But as soon I add the eaValidateUnique directive then the eaValidateEmail directive is left out and the ctrl.$valid method of the eaValidateUnique directive is always passing even though ctrl.$valid is false in console.log.
I have read through the AngularJS documentation, bought two books but the examples are always very basic. Currently I can't figure out where the problem could be located. It looks like there is a clash with ngModelController but I can't figure out the right way to solve this issue.
I'm currently testing with the ValidateCtrlNew form. So the field in the "New" section of the html form.
Questions:
Does anybody know how to write the directives so that they are executed in serial order as I add them as attributes to the input element?
How can I prevent such clashes with directives? Isolated scope is also no option for multiple directives.
Here is the jsfiddle: http://jsfiddle.net/charms/6j3U8/230/
<div ng-controller="ValidateCtrlNew">
<form name="user_form_new" class="pure-form" novalidate>
<fieldset>
<legend>New</legend>
<input type="text" name="email" ng-model="user.email" placeholder="E-Mail" class="txt_fld" ng-required="true" ea-validate-email ea-validate-unique/><br/>
<div class="inv_msg" ng-show="user_form_new.email.$dirty && user_form_new.email.$invalid">Invalid:
<span ng-show="user_form_new.email.$error.required">Please enter your email.</span>
<span ng-show="user_form_new.email.$error.eaValidateEmail">This is not a valid email.</span>
<span ng-show="user_form_new.email.$error.eaValidateEmailCheck">Checking email....</span>
<span ng-show="user_form_new.email.$error.eaValidateUnique">This email is already taken.</span>
</div>
</fieldset>
</form>
</div>
.directive('eaValidateUnique', ['$http', function($http) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
ctrl.$parsers.push(function(viewValue) {
console.log(ctrl);
//ctrl.$setValidity('eaValidateUnique', true);
if(ctrl.$valid) {
ctrl.$setValidity('eaValidateUnique', false);
console.log("valid was true");
}
});
}
};
}])
.directive('eaValidateEmail', [function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
var EMAIL_REGEXP = /^([\w-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
ctrl.$parsers.push(function(viewValue) {
// set validity to true to clear out if previous validators fail
ctrl.$setValidity('eaValidateEmail', true);
if(ctrl.$valid) {
// set validity to false as we need to check the value here
ctrl.$setValidity('eaValidateEmail', false);
if(viewValue !== undefined && viewValue !== "" && EMAIL_REGEXP.test(viewValue)) {
// if the format of the email is valid then we set validity to true
ctrl.$setValidity('eaValidateEmail', true);
ctrl.$setValidity('eaValidateEmailCheck', true);
console.log("TRUE");
} else {
// if the format of the email is invalid we set validity to false
ctrl.$setValidity('eaValidateEmail', false);
ctrl.$setValidity('eaValidateEmailCheck', true);
console.log("FALSE");
}
}
return viewValue;
});
}
};
}]);
you can add priority to eaValidateEmail to 100 like..
restrict: 'A',
priority:'100',
require: 'ngModel',
From what I understand is that the validators are chained on the $parsers array. AngularJs validators return the value if the validator decides the value if valid, but return an undefined if the value is invalid.
This way, the other validators on your chain would not have a value to work with anymore.
http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController#$parsers

ng-pattern changing, but form saying information is valid

So, the current code looks like this:
<input ng-pattern="validRegEx()" required>
$scope.validRegEx = function() {
// blah blah return regex
};
When I change an input on the form, I change the regex. The problem is data that was valid before (which should NOT be valid with the new RegEx) is still valid. How can I force the form to apply a regex to this field again, as opposed to just waiting for the user to blur the field again?
You could do it with a directive by watching for when the regex changes (or the conditions for it) and when it does you can re-run the validation by re-setting the value of the element.
something like this
angular.module('youappname', [])
.directive('reApply', function () {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, elm, attrs, ctrl) {
// you should change 'regex' in the following line to match the variable that when altered causes the regex to change..
scope.$watch('regex', function (value) {
if (ctrl.$viewValue !== ''){
ctrl.$setViewValue( ctrl.$viewValue );
}
});
}
};
});
and adding re-apply to your element
<input type="text" ng-pattern="validate()" re-apply="" name="userName" ng-model="user.name" required> <span class="error" ng-show="myForm.userName.$error.required">
demo at http://jsfiddle.net/Dztev/5/
This could also be done directly in the controller. This will watch the input stuff.name and if it changes, it will clear the scope.stuff.results field.
$scope.$watch("stuff.name", function(newValue, oldValue){
if(newValue != oldValue)
$scope.stuff.results = "";
});

Resources