How to add custom validation to an angular component? - javascript

I have this angular component where I like to add a custom input validator (see plunker).
I'm trying to access the ngModelController in the $onInit function. But it seams that the form is not populated at this time. Later on in the sendEmail() function it's no problem to access the the input model controller. How can I access the ngModelController and add a custom validator?
emailInput.js
(function(angular) {
'use strict';
function EmailInputController($log) {
var ctrl = this;
ctrl.$onInit = function() {
// ctrl.myForm.myEmailInput is not populated
//$log.debug("Email view value is: "+(ctrl.myForm.myEmailInput.$viewValue));
};
ctrl.sendEmail = function() {
$log.debug("EmailInputController.sendEmail");
$log.debug("Email view value is: " + (ctrl.myForm.myEmailInput.$viewValue));
};
}
angular.module('emailInputApp').component('emailInput', {
templateUrl: 'emailInput.html',
controller: EmailInputController,
});
})(window.angular);
emailInput.html
<form name="$ctrl.myForm">
<label>Email:</label>
<input name="myEmailInput" type="email" ng-model="$ctrl.email" required maxlength="15">
<button type="button" ng-click="$ctrl.sendEmail()">Send Email</button>
<p>Your Email addres is {{$ctrl.email}}</p>
<div ng-messages="$ctrl.myForm.myEmailInput.$error" role="alert">
<div ng-message="required">Please enter an email address.</div>
<div ng-message="email">This field must be a valid email address.</div>
<div ng-message="maxlength">This field can be at most 15 characters long.</div>
</div>
<code>
{{$ctrl.myForm.myEmailInput | json}}
</code>
</form>

http://plnkr.co/edit/YQfGAsix1DON4ff3EWxz?p=preview
You can add watcher and remove it when not needed anymore.
var removeWatch = $scope.$watch('$ctrl.myForm', function () {
$log.debug("Email view value is: " + (ctrl.myForm.myEmailInput.$modelValue));
removeWatch();
});

app.directive('validateEmail', function() {
var EMAIL_REGEXP = /^[_a-z0-9]+(\.[_a-z0-9]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$/;
return {
require: 'ngModel',
restrict: '',
link: function(scope, elm, attrs, ctrl) {
// only apply the validator if ngModel is present and Angular has added the email validator
if (ctrl && ctrl.$validators.email) {
// this will overwrite the default Angular email validator
ctrl.$validators.email = function(modelValue) {
return ctrl.$isEmpty(modelValue) || EMAIL_REGEXP.test(modelValue);
};
}
}
};
});
And simply add
<input type='email' validate-email name='email' id='email' ng-model='email' required>

Related

custom directive is not validating form. why not?

What code changes need to be made to get the custom countparens directive below to provide the extra custom string validation shown below during form validation? The code below DOES successfully alert the user when the input field is empty, but it IS NOT alerting the user when the number of open parens ( and close parens ) is not equal.
I am using AngularJS. I used the documentation at this link (scroll to bottom) to design the code below.
Here is the html for the form:
<table>
<tr>
<td width=200>
<form name="userForm" ng-submit="rectangularForm(userForm.$valid)" novalidate>
<input type="text" name="fieldname" ng-model="nameofjsontype.fieldname" required />
<p ng-show="userForm.fieldname.$invalid && !userForm.fieldname.$pristine" class="help-block">Function is a required field.</p>
<span ng-show="userForm.nameofjsontype.fieldname.$error.countparens">The #( != #) !</span>
<br>
<button type="submit" ng-disabled="userForm.$invalid" >Click to submit</button>
</form>
</td>
</tr>
</table>
The javascript file containing the directive includes:
// create angular app
var myApp = angular.module('myApp', []);
// create angular controller
myApp.controller('myController', ['$scope', '$http', function($scope, $http) {
$scope.nameofjsontype = {type:"nameofjsontype", fieldname: 'some (value) here.'};
$scope.rectangularForm = function(isValid) {
// check to make sure the form is completely valid
if (isValid) {
var funcJSON = {type:"nameofjsontype", fieldname: $scope.nameofjsontype.fieldname};
$http.post('/server/side/controller', funcJSON).then(function(response) {
$scope.nameofjsontype = response.data;
});
}
};
}]);
myApp.directive('countparens', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$validators.countparens = function(modelValue, viewValue) {
if (ctrl.$isEmpty(modelValue)) {
// consider empty models to be valid
return true;
}
if (
($scope.nameofjsontype.fieldname.match(/\)/g).length) == ($scope.nameofjsontype.fieldname.match(/\(/g).length)
) {
// it is valid
return true;
}
// it is invalid
return false;
};
}
};
});
Your markup should be using userForm.fieldname.$error.countparens to show the error. The field bound to the userForm is NOT the same as your ngModel value. See plunker for what I mean
<span ng-show="userForm.fieldname.$error.countparens" class="help-block">The #( != #) !</span>
You are also not using your directive on your input element:
<input type="text" name="fieldname" ng-model="nameofjsontype.fieldname" required data-countparens=""/>
In your directive
you should be using modelValue and not the scope value when doing your matching AND
you need to cater for when there are no matches to ( or )
.
myApp.directive('countparens', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$validators.countparens = function(modelValue, viewValue) {
return ctrl.$isEmpty(modelValue) ||
((modelValue.match(/\)/g) || []).length == (modelValue.match(/\(/g) || []).length);
};
}
};
});

Add custom validation to AngularJS form

In below form I'm checking that an e-mail address is required :
http://jsfiddle.net/U3pVM/16994/
I want to extend the validation so that it check that the first two characters begin with 'DD' . It seems I need to add a custom directive but I'm unsure how to link the e-mail fields with the directive ?
fiddle code :
<form ng-app="myApp" ng-controller="validateCtrl"
name="myForm" novalidate>
<p>Email:<br>
<input type="email" name="email" ng-model="email" required>
<span style="color:red" ng-show="myForm.email.$dirty && myForm.email.$invalid">
<span ng-show="myForm.email.$error.required">Email is required.</span>
</span>
</p>
<p>
<input type="submit"
ng-disabled="myForm.user.$dirty && myForm.user.$invalid ||
myForm.email.$dirty && myForm.email.$invalid">
</p>
</form>
var app = angular.module('myApp', []);
app.controller('validateCtrl', function($scope) {
});
app.directive("myValidator", function(){
// requires an isloated model
return {
// restrict to an attribute type.
restrict: 'A',
// element must have ng-model attribute.
require: 'ngModel',
link: function(scope, ele, attrs, ctrl){
// add a parser that will process each time the value is
// parsed into the model when the user updates it.
ctrl.$parsers.unshift(function(value) {
if(value){
// test and set the validity after update.
var valid = value.charAt(0) == 'D' && value.charAt(1) == 'D';
ctrl.$setValidity('invalidAiportCode', valid);
}
return valid ? value : undefined;
});
}
}
});
Here's how I would do it, using an authentication example:
The simple markup:
<input type="email" ng-model="existingUser.email">
<button ng-click="login(existingUser)">Login</button>
The controller:
auth.controller('AuthCtrl', '$scope', 'validation', function($scope, validation) {
$scope.existingUser = {
email: '',
password: ''
}
$scope.login = function() {
validation.validateSignin($scope.existingUser)
.catch(function(err) {
// The validation didn't go through,
// display the error to the user
$scope.status.message = err;
})
.then(function(res) {
// Validation passed
if (res === true) {
// Do something
}
});
}
}
The factory:
auth.factory('validation', ['$q', function($q) {
return {
validateSignin: function(existingUser) {
var q = $q.defer();
if (existingUser.email.substring(0,2) !== 'DD') {
q.reject('The email must start with "DD"');
}
q.resolve(true);
return q.promise;
}
}
}]);
Let me explain what's happening, first I'm creating a factory which will carry out the validation. Then I'm creating a promise with a resolve and a reject method, the reject method will be called if the validation failed, and if it succeeded then the resolve will be called. Then in your controller you can do things based on the outcome.
Let me know if anything is unclear.

show and hide bootsrap tooltip through javascript

I have a stuff which uses ui.bootsrap tool-tip feature, the code is working fine, but I don't know to show and hide the tooltip through script, say I am having a form which some field validation, when I submit the form, if the validation for a component say (a text field for email) fails, then it should shows up a tool-tip it should not go unless the field is properly validated,
Can anyone please tell me some solution for this
script
var app = angular.module('someApp', ['ui.bootstrap']);
app.controller('MainCtrl', function ($scope) {
$scope.validate = function () {
var re = /^([\w-]+(?:\.[\w-]+)*)#((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
var emailValid = re.test($scope.userEmail);
if(!emailValid)
{
// I want to show the tool tip
}
};
})
html
<div ng-app="someApp" ng-controller="MainCtrl">
<form ng-submit="validate()">
<input type="text" ng-model='userEmail' rc-tooltip="Invalid Email...." tooltip-placement="bottom" />
<input type="submit" />
</form>
</div>
JSFiddle
Demo
Here is a simple email validation directive that uses bootstrap:
app.directive('email', function() {
return {
restrict: 'A',
require: 'ngModel',
compile: function(element, attr) {
element.tooltip({ placement: 'right', title:'Email is invalid', trigger:'manual'});
function emailValid(email) {
var re = /^([\w-]+(?:\.[\w-]+)*)#((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
var valid = re.test(email);
return valid;
}
return function(scope, element,attr, ngModel) {
ngModel.$validators.email = function(val) {
return emailValid(val);
}
scope.$watch(function() {
return ngModel.$error.email;
}, function(val) {
if (val)
element.tooltip('show');
else
element.tooltip('hide');
});
}
}
}
});
Usage
<input type="text" ng-model="email" email />
Its given hereBootstrap Tooltip
And by using data-toggale option using javascript ,you can use tooltip.
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
By above code you can assign tooltip then to show and hide you can use
$('#element').tooltip('show')
or
$('#element').tooltip('hide')

Custom directive for telephone format using angularjs

I am trying to write custom directive for USA telephone number using angularjs and need to preserve the data type of the field as integer.Here is the jsfiddle directive and need help to complete the directive.
If user enters a valid telephone no (exactly 10 numbers ie.1234567890) then input should split into 3 chunks as 123-456-7890 when the user moves to next control.otherewise I should show error message "not a valid number".
<div ng-app="myApp" ng-controller="myCtrl">
<form name="myForm">
<input type="text" ng-model="telephone" phoneformat name="input1" />
<span class="error" ng-show="myForm.input1.$error.telephone">Numbers only!</span>
<span class="error" ng-show="myForm.input1.$error.telephone">Exact 10 Numbers only!</span>
</form>
</div>
var myApp = angular.module("myApp", []);
var myCtrl = myApp.controller("myCtrl",["$scope", function($scope) {
$scope.telephone = "1234567890";
}]);
myApp.directive("phoneformat", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, element, attr, ngModelCtrl) {
var phoneformat = function () {
}
}
};
});
It looks like you want to leverage the $error property of the form to drive validation. To do this, you will need to call $setValidity in the ngModelCtrl that you have required into your directive:
var myApp = angular.module("myApp", []);
var myCtrl = myApp.controller("myCtrl",["$scope", function($scope) {
$scope.telephone = "1234567890";
}]);
myApp.directive("phoneformat", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, element, attr, ngModelCtrl) {
//Parsing is performed from angular from view to model (e.g. update input box)
//Sounds like you just want the number without the hyphens, so take them out with replace
var phoneParse = function (value) {
var numbers = value && value.replace(/-/g, "");
if (/^\d{10}$/.test(numbers)) {
return numbers;
}
return undefined;
}
//Formatting is done from view to model (e.g. when you set $scope.telephone)
//Function to insert hyphens if 10 digits were entered.
var phoneFormat = function (value) {
var numbers = value && value.replace(/-/g,"");
var matches = numbers && numbers.match(/^(\d{3})(\d{3})(\d{4})$/);
if (matches) {
return matches[1] + "-" + matches[2] + "-" + matches[3];
}
return undefined;
}
//Add these functions to the formatter and parser pipelines
ngModelCtrl.$parsers.push(phoneParse);
ngModelCtrl.$formatters.push(phoneFormat);
//Since you want to update the error message on blur, call $setValidity on blur
element.bind("blur", function () {
var value = phoneFormat(element.val());
var isValid = !!value;
if (isValid) {
ngModelCtrl.$setViewValue(value);
ngModelCtrl.$render();
}
ngModelCtrl.$setValidity("telephone", isValid);
//call scope.$apply() since blur event happens "outside of angular"
scope.$apply();
});
}
};
});
Working fiddle. This was just a quick way of demonstrating the parser and formatter pipelines that are used in ngModel, along with $setValidity -- which is used to populate the $error field(s).
Update: To use this same phone validation across multiple phones, use form with $error. Notice that each input gets a unique name that is used with myForm (name of form). Both use $error.telephone:
<form name="myForm">
Mobile Phone:
<input type="text" ng-model="mobilephone" phoneformat name="mobilephone" />
<span class="error" ng-show="myForm.mobilephone.$error.telephone">
Exact 10 Numbers only!
</span>
<br />
Home Phone:
<input type="text" ng-model="homephone" phoneformat name="homephone" />
<span class="error" ng-show="myForm.homephone.$error.telephone">
Exact 10 Numbers only!
</span>
</form>
Updated fiddle.
You might want to use http://angular-ui.github.io/ui-utils/ Mask directive.
Working Fiddle: http://jsfiddle.net/HB7LU/6581/
myApp.directive("phoneFormat", function () {
return {
restrict: "A",
link: function (scope, element, attr) {
element.bind('change', function() {
if ( this.value.length === 10 ) {
var number = this.value;
this.value = number.substring(0,3) + '-' + number.substring(3,6) + '-' + number.substring(6,10)
}
else {
document.querySelector('.helpblock').innerHTML = 'error in formatting';
}
});
}
};
});
Iv'e extended your original fiddle. here's the result:
http://jsfiddle.net/10k58awt/
You can find splittedNumber array (contains 3 parts of number) on form submission
js:
var myApp = angular.module("myApp", []);
var myCtrl = myApp.controller("myCtrl", ["$scope", function ($scope) {
$scope.telephone = "1234567890";
$scope.submit = function () {
var splittedNumber = [$scope.telephone.substring(0, 3), $scope.telephone.substring(3, 6), $scope.telephone.substring(6, 10)];
// Do something with splitted number
console.log(splittedNumber);
};
}]);
myApp.directive("phoneformat", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (phoneInput) {
phoneInput = phoneInput.trim();
if (phoneInput && phoneInput.length == 10 && !isNaN(phoneInput)) {
ctrl.$setValidity('phoneformat', true);
return phoneInput;
} else {
ctrl.$setValidity('phoneformat', false);
return undefined;
}
});
}
};
});
html:
<div ng-app="myApp" ng-controller="myCtrl">
<form name="myForm" novalidate ng-submit="myForm.$valid && submit()">
<input type="text" ng-model="telephone" phoneformat name="input1" /> <span class="error" ng-show="myForm.input1.$error.phoneformat">Invalid US Phone number!</span>
<div>
<button class="btn btn-primary" type="submit" ng-class="{'disabled': !myForm.$valid}">submit</button>
</div>
</form>
</div>

password-check directive in angularjs

I'm writing a password verify directive :
Directives.directive("passwordVerify",function(){
return {
require:"ngModel",
link: function(scope,element,attrs,ctrl){
ctrl.$parsers.unshift(function(viewValue){
var origin = scope.$eval(attrs["passwordVerify"]);
if(origin!==viewValue){
ctrl.$setValidity("passwordVerify",false);
return undefined;
}else{
ctrl.$setValidity("passwordVerify",true);
return viewValue;
}
});
}
};
});
html :
<input data-ng-model='user.password' type="password" name='password' placeholder='password' required>
<input data-ng-model='user.password_verify' type="password" name='confirm_password' placeholder='confirm password' required data-password-verify="user.password">
Given 2 password fields in a form, if both password values are equal then the field affected by the directive is valid.
The issue is that it works one way (i.e. when I type a password in the password-verify field). However, when the original password field is updated, the password-verify doesn't become valid.
Any idea how I could have a "two way binding verify?"
I use the following directive because I want to re-validate both input field regardless of whether value 1 or value 2 was changed:
directive:
'use strict';
angular.module('myApp').directive('equals', function() {
return {
restrict: 'A', // only activate on element attribute
require: '?ngModel', // get a hold of NgModelController
link: function(scope, elem, attrs, ngModel) {
if(!ngModel) return; // do nothing if no ng-model
// watch own value and re-validate on change
scope.$watch(attrs.ngModel, function() {
validate();
});
// observe the other value and re-validate on change
attrs.$observe('equals', function (val) {
validate();
});
var validate = function() {
// values
var val1 = ngModel.$viewValue;
var val2 = attrs.equals;
// set validity
ngModel.$setValidity('equals', ! val1 || ! val2 || val1 === val2);
};
}
}
});
usage
<input type="password" ng-model="value1" equals="{{value2}}" required>
<input type="password" ng-model="value2" equals="{{value1}}" required>
Creating a separate directive for this is not needed. There is already a build in Angular UI password validation tool. With this you could do:
<input name="password" required ng-model="password">
<input name="confirm_password"
ui-validate=" '$value==password' "
ui-validate-watch=" 'password' ">
Passwords match? {{!!form.confirm_password.$error.validator}}
This should solve it:
View:
<div ng-controller='Ctrl'>
<form name='form'>
<input data-ng-model='user.password' type="password" name='password' placeholder='password' required>
<div ng-show="form.password.$error.required">
Field required</div>
<input ng-model='user.password_verify' type="password" name='confirm_password' placeholder='confirm password' required data-password-verify="user.password">
<div ng-show="form.confirm_password.$error.required">
Field required!</div>
<div ng-show="form.confirm_password.$error.passwordVerify">
Fields are not equal!</div>
</form
</div>
Directive
var app = angular.module('myApp', []);
app.directive("passwordVerify", function() {
return {
require: "ngModel",
scope: {
passwordVerify: '='
},
link: function(scope, element, attrs, ctrl) {
scope.$watch(function() {
var combined;
if (scope.passwordVerify || ctrl.$viewValue) {
combined = scope.passwordVerify + '_' + ctrl.$viewValue;
}
return combined;
}, function(value) {
if (value) {
ctrl.$parsers.unshift(function(viewValue) {
var origin = scope.passwordVerify;
if (origin !== viewValue) {
ctrl.$setValidity("passwordVerify", false);
return undefined;
} else {
ctrl.$setValidity("passwordVerify", true);
return viewValue;
}
});
}
});
}
};
});
Yet another take on this is to match the model of one input to another input’s value.
app.directive('nxEqual', function() {
return {
require: 'ngModel',
link: function (scope, elem, attrs, model) {
if (!attrs.nxEqual) {
console.error('nxEqual expects a model as an argument!');
return;
}
scope.$watch(attrs.nxEqual, function (value) {
model.$setValidity('nxEqual', value === model.$viewValue);
});
model.$parsers.push(function (value) {
var isValid = value === scope.$eval(attrs.nxEqual);
model.$setValidity('nxEqual', isValid);
return isValid ? value : undefined;
});
}
};
});
So, if the password box’s model is login.password then you set the following attribute on the verification box: nx-equal="login.password", and test for formName.elemName.$error.nxEqual. Like so:
<form name="form">
<input type="password" ng-model="login.password">
<input type="password" ng-model="login.verify" nx-equal="login.password" name="verify">
<span ng-show="form.verify.$error.nxEqual">Must be equal!</span>
</form>
Extended version:
For a new project of mine I had to modify the above directive so that it would only display the nxEqual error when, and only when, the verification input had a value. Otherwise the nxEqual error should be muted. Here’s the extended version:
app.directive('nxEqualEx', function() {
return {
require: 'ngModel',
link: function (scope, elem, attrs, model) {
if (!attrs.nxEqualEx) {
console.error('nxEqualEx expects a model as an argument!');
return;
}
scope.$watch(attrs.nxEqualEx, function (value) {
// Only compare values if the second ctrl has a value.
if (model.$viewValue !== undefined && model.$viewValue !== '') {
model.$setValidity('nxEqualEx', value === model.$viewValue);
}
});
model.$parsers.push(function (value) {
// Mute the nxEqual error if the second ctrl is empty.
if (value === undefined || value === '') {
model.$setValidity('nxEqualEx', true);
return value;
}
var isValid = value === scope.$eval(attrs.nxEqualEx);
model.$setValidity('nxEqualEx', isValid);
return isValid ? value : undefined;
});
}
};
});
And you would use it like so:
<form name="form">
<input type="password" ng-model="login.password">
<input type="password" ng-model="login.verify" nx-equal-ex="login.password" name="verify">
<span ng-show="form.verify.$error.nxEqualEx">Must be equal!</span>
</form>
Try it: http://jsfiddle.net/gUSZS/
I have done it without directive.
<input type="password" ng-model="user.password" name="uPassword" required placeholder='Password' ng-minlength="3" ng-maxlength="15" title="3 to 15 characters" />
<span class="error" ng-show="form.uPassword.$dirty && form.uPassword.$error.minlength">Too short</span>
<span ng-show="form.uPassword.$dirty && form.uPassword.$error.required">Password required.</span><br />
<input type="password" ng-model="user.confirmpassword" name="ucPassword" required placeholder='Confirm Password' ng-minlength="3" ng-maxlength="15" title="3 to 15 characters" />
<span class="error" ng-show="form.ucPassword.$dirty && form.ucPassword.$error.minlength">Too short</span>
<span ng-show="form.ucPassword.$dirty && form.ucPassword.$error.required">Retype password.</span>
<div ng-show="(form.uPassword.$dirty && form.ucPassword.$dirty) && (user.password != user.confirmpassword)">
<span>Password mismatched</span>
</div>
https://github.com/wongatech/angular-confirm-field is a good project for this.
Example here http://wongatech.github.io/angular-confirm-field/
The code below shows 2 input fields with the implemented functionality
<input ng-confirm-field ng-model="emailconfirm" confirm-against="email" name="my-email-confirm"/>
<input ng-model="email" name="my-email" />
As of angular 1.3.0-beta12, invalid inputs don't write to ngModel, so you can't watch AND THEN validate as you can see here: http://plnkr.co/edit/W6AFHF308nyKVMQ9vomw?p=preview. A new validators pipeline was introduced and you can attach to this to achieve the same thing.
Actually, on that note I've created a bower component for common extra validators: https://github.com/intellix/angular-validators which includes this.
angular.module('validators').directive('equals', function() {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, elem, attrs, ngModel)
{
if (!ngModel) return;
attrs.$observe('equals', function() {
ngModel.$validate();
});
ngModel.$validators.equals = function(value) {
return value === attrs.equals;
};
}
};
});
angular.module('validators').directive('notEquals', function() {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, elem, attrs, ngModel)
{
if (!ngModel) return;
attrs.$observe('notEquals', function() {
ngModel.$validate();
});
ngModel.$validators.notEquals = function(value) {
return value === attrs.notEquals;
};
}
};
});
I've used this directive with success before:
.directive('sameAs', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (viewValue === scope[attrs.sameAs]) {
ctrl.$setValidity('sameAs', true);
return viewValue;
} else {
ctrl.$setValidity('sameAs', false);
return undefined;
}
});
}
};
});
Usage
<input ... name="password" />
<input type="password" placeholder="Confirm Password"
name="password2" ng-model="password2" ng-minlength="9" same-as='password' required>
I was dealing with the same issue and found a good blog post about it written by Piotr Buda. It's a good read and it explains the process very well. The code is as follows:
directives.directive("repeatPassword", function() {
return {
require: "ngModel",
link: function(scope, elem, attrs, ctrl) {
var otherInput = elem.inheritedData("$formController")[attrs.repeatPassword];
ctrl.$parsers.push(function(value) {
if(value === otherInput.$viewValue) {
ctrl.$setValidity("repeat", true);
return value;
}
ctrl.$setValidity("repeat", false);
});
otherInput.$parsers.push(function(value) {
ctrl.$setValidity("repeat", value === ctrl.$viewValue);
return value;
});
}
};
});
So you could do something like:
<input type="password" name="repeatPassword" id="repeatPassword" placeholder="repeat password" ng-model="user.repeatPassword" repeat-password="password" required>
Credit goes to the author
Is this not good enough:
<input type="password" ng-model="passwd1" />
<input type="password" ng-model="passwd2" />
<label ng-show="passwd1 != passwd2">Passwords do not match...</label>
<button ng-disabled="passwd1 != passwd2">Save</button>
Simple, and works just fine for me.
This solution is similar to the one given by Dominic Watson, which uses $validators and is the one I like best. The only changes are that you can watch an expression.
$validators A collection of validators that are applied whenever the
model value changes. The key value within the object refers to the
name of the validator while the function refers to the validation
operation. The validation operation is provided with the model value
as an argument and must return a true or false value depending on the
response of that validation
from https://code.angularjs.org/1.3.15/docs/api/ng/type/ngModel.NgModelController
I'm using angular 1.3. My directive looks something like this
angular.module('app').directive("passwordConfirm", function() {
"use strict";
return {
require : "ngModel",
restrict : "A",
scope : {
//We will be checking that our input is equals to this expression
passwordConfirm : '&'
},
link : function(scope, element, attrs, ctrl) {
//The actual validation
function passwordConfirmValidator(modelValue, viewValue) {
return modelValue == scope.passwordConfirm();
}
//Register the validaton when this input changes
ctrl.$validators.passwordConfirm = passwordConfirmValidator;
//Also validate when the expression changes
scope.$watch(scope.passwordConfirm, ctrl.$validate);
}
};
});
To use it
<input type="password" ng-model="user.password"/>
<input type="password" ng-model="user.confirmPassword"
password-confirm="user.password" />
In order to validation of form with two input field,i find most suitable way of
Directive
app.directive('passwordVerify', function() {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!attrs.passwordVerify) {
return;
}
scope.$watch(attrs.passwordVerify, function (value) {
if( value === ctrl.$viewValue && value !== undefined) {
ctrl.$setValidity('passwordVerify', true);
ctrl.$setValidity("parse",undefined);
}
else {
ctrl.$setValidity('passwordVerify', false);
}
});
ctrl.$parsers.push(function (value) {
var isValid = value === scope.$eval(attrs.passwordVerify);
ctrl.$setValidity('passwordVerify', isValid);
return isValid ? value : undefined;
});
}
};
});
HTML
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="form-group" ng-class="{ 'has-error': form.password.$dirty && form.password.$error.required || (form.password.$error.minlength || form.password.$error.maxlength)}">
<input type="password" name="password" ng-minlength="6" ng-maxlength="16" id="password" class="form-control" placeholder="Password" ng-model="user.password" required />
<span ng-show="form.password.$dirty && form.password.$error.required" class="help-block">Password is required</span>
<span ng-show="form.password.$error.minlength || form.password.$error.maxlength" class="help-block">Password must be 6-16 character long</span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="form-group" ng-class="{ 'has-error': (form.confirm_password.$dirty && form.confirm_password.$error.required) || form.confirm_password.$error.passwordVerify }">
<input type="password" name="confirm_password" id="confirm_password" class="form-control" placeholder="Confirm Password" ng-model="user.confirm_password" required password-verify="user.password" />
<span ng-show="form.confirm_password.$dirty && form.confirm_password.$error.required" class="help-block">Confirm Password is required</span>
<span ng-show="form.confirm_password.$error.passwordVerify" class="help-block">Please make sure passwords match & must be 6-16 character long</span>
</div>
</div>
</div>
This works both ways and it is simple and clean
JavaScript
var app = angular.module("app");
app.controller("SamePaswordController", function () {
this.password;
this.confirm;
this.save = function () {
alert("Saved!");
};
}
app.directive("match", function () {
return {
restrict:"A",
require:"ngModel",
link: function(scope, element, attrs, ctrl) {
function matchValidator(value) {
scope.$watch(attrs.match, function(newValue, oldValue) {
var isValid = value === scope.$eval(attrs.match);
ctrl.$setValidity('match', isValid);
});
return value;
}
ctrl.$parsers.push(matchValidator);
}
};
});
HTML: note the match directive
<form name="regForm" ng-controller="SamePaswordController as regCtrl"
ng-submit="regForm.$valid && regCtrl.save()" novalidate>
<input name="password" ng-model="regCtrl.password"
type="password" required placeholder="Password"/>
<input name="confirm" ng-model="regCtrl.confirm" match="regCtrl.password"
type="password" required placeholder="Confirm password"/>
<div> regForm is valid:{{regForm.$valid}}</div>
<input type="submit" value="Save"/>
</form>
You can clone the repo with this example
https://github.com/rogithub/roangularjs
Not a directive solution but is working for me:
<input ng-model='user.password'
type="password"
name='password'
placeholder='password'
required>
<input ng-model='user.password_verify'
type="password"
name='confirm_password'
placeholder='confirm password'
ng-pattern="getPattern()"
required>
And in the controller:
//Escape the special chars
$scope.getPattern = function(){
return $scope.user.password &&
$scope.user.password.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1');
}
http://plnkr.co/edit/QDTnipCsHdg56vgygsqC?p=preview
The following is my take on the problem. This directive would compare against a form value instead of the scope.
'use strict';
(function () {
angular.module('....').directive('equals', function ($timeout) {
return {
restrict: 'A',
require: ['^form', 'ngModel'],
scope: false,
link: function ($scope, elem, attrs, controllers) {
var validationKey = 'equals';
var form = controllers[0];
var ngModel = controllers[1];
if (!ngModel) {
return;
}
//run after view has rendered
$timeout(function(){
$scope.$watch(attrs.ngModel, validate);
$scope.$watch(form[attrs.equals], validate);
}, 0);
var validate = function () {
var value1 = ngModel.$viewValue;
var value2 = form[attrs.equals].$viewValue;
var validity = !value1 || !value2 || value1 === value2;
ngModel.$setValidity(validationKey, validity);
form[attrs.equals].$setValidity(validationKey,validity);
};
}
};
});
})();
in the HTML one now refers to the actual form instead of the scoped value:
<form name="myForm">
<input type="text" name="value1" equals="value2">
<input type="text" name="value2" equals="value1">
<div ng-show="myForm.$invalid">The form is invalid!</div>
</form>
In order to achieve validation when both inputs change, I use the following code (which was a combination of all others other answers):
angular.module('app.directives')
.directive('passwordVerify', [function () {
return {
require: '?ngModel',
restrict: 'A',
scope: {
origin: '=passwordVerify'
},
link: function (scope, element, attrs, ctrl) {
if(!ctrl) {
return;
}
function validate(value) {
ctrl.$setValidity('passwordMatch', scope.origin === value);
return value;
}
ctrl.$parsers.unshift(validate);
scope.$watch('origin', function(value) {
validate(ctrl.$viewValue);
});
}
};
}]);
First, I would like to thank Fredric for posting this excellent example. There is one tiny issue that I came across by coincidence. on the Fiddle you posted http://jsfiddle.net/gUSZS/
If you type in a password, and then type in the same password in the verify input element everything works fine, but try to add a space to the second box and angular will automatically trim that space. This means that the directive doesnt "see" the extra space. Now the passwords are different, but the form is still valid.
to fix this we need to add
ng-trim="false"
to the input elements. This doesnt work in angular 1.0.3 so if you want to try it in this fiddle you need to add 1.1.1 to the Fiddle (http://ajax.googleapis.com/ajax/libs/angularjs/1.1.1/angular.js)
But again, thanx Frederic, I will use your solution in my app!
Anton
P.S. I wanted to comment on Frederic's post, but Im new to this forum and dont seem to have enough credit. So it would be very much appreciated if some of you can up vote my comment if you like it :-)
No need for an extra directive, here's my take on this:
HTML:
<div class="form-group" data-ng-class="{ 'has-error': submitted && !form.new_passwd.$valid }">
<input type="password" name="new_passwd" class="form-control" data-ng-model="data.new_passwd" placeholder="New Password" required data-ng-pattern="passwdRegex">
<small class="help-block" data-ng-show="submitted && form.new_passwd.$error.required">New password is required!</small>
<small class="help-block" data-ng-show="submitted && !form.new_passwd.$error.required && form.new_passwd.$error.pattern">New password is not strong enough!</small>
</div>
<div class="form-group" data-ng-class="{ 'has-error': submitted && !form.new_passwd_conf.$valid }">
<input type="password" name="new_passwd_conf" class="form-control" data-ng-model="data.new_passwd_conf" placeholder="Confirm New Password" required data-ng-pattern="passwdConfRegex">
<small class="help-block" data-ng-show="submitted && form.new_passwd_conf.$error.required">New password confirmation is required!</small>
<small class="help-block" data-ng-show="submitted && !form.new_passwd_conf.$error.required && form.new_passwd_conf.$error.pattern">New password confirmation does not match!</small>
</div>
Javascript:
$scope.passwdRegex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,}$/;
$scope.$watch('data.new_passwd', function() {
$scope.passwdConfRegex = new RegExp(Regex.escape($scope.data.new_passwd));
});
where Regex.escape() can be found here.
Works like a charm!
To add to the large number of already existing solutions, this works well for me.
(Jan Laussmann answer stopped working with the latest AngularJS beta releases).
directive:
angular.module('myApp').directive('matchValidator', [function() {
return {
require: 'ngModel',
link: function(scope, elm, attr, ctrl) {
var pwdWidget = elm.inheritedData('$formController')[attr.matchValidator];
ctrl.$parsers.push(function(value) {
if (value === pwdWidget.$viewValue) {
ctrl.$setValidity('match', true);
return value;
}
if (value && pwdWidget.$viewValue) {
ctrl.$setValidity('match', false);
}
});
pwdWidget.$parsers.push(function(value) {
if (value && ctrl.$viewValue) {
ctrl.$setValidity('match', value === ctrl.$viewValue);
}
return value;
});
}
};
}])
usage
<input type="email" ng-model="value1" name="email" required>
<input type="email" ng-model="value2" name="emailConfirm" match-validator="email" required>
display error
<div ng-if="[[yourFormName]].emailConfirm.$error">
<div ng-if="[[yourFormName]].emailConfirm.$error.match">
Email addresses don't match.
</div>
</div>
<input name="password" type="text" required="" ng-model="password" placeholder="password" class="ng-dirty ng-valid ng-valid-required">
<input name="confirm_password" type="text" required="" ng-model="confirm_password" ui-validate=" '$value==password' " ui-validate-watch=" 'password' " placeholder="confirm password" class="ng-dirty ng-valid-required ng-invalid ng-invalid-validator">
<span ng-show="form.confirm_password.$error.validator">Passwords do not match!</span>
password errors: {
"required": false,
"validator": true
}
This worked for me.
Directive:
modulename.directive('passwordCheck', function () {
return {
restrict: 'A', // only activate on element attribute
require: '?ngModel', // get a hold of NgModelController
link: function (scope, elem, attrs, ngModel) {
if (!ngModel) return; // do nothing if no ng-model
var Value = null;
// watch own value and re-validate on change
scope.$watch(attrs.ngModel, function (val) {
Value = val;
validate();
});
// observe the other value and re-validate on change
attrs.$observe('passwordCheck', function () {
validate();
});
var validate = function () {
// values
var val1 = Value;
var val2 = attrs.passwordCheck;
// set validity
if (val1 != '' && val1 != undefined) {
ngModel.$setValidity('passwordCheck', val1 == val2);
}
else {
ngModel.$setValidity('passwordCheck', true);
}
};
}
}
});
HTML:
ng-model="confirmpassword.selected" type="password" name="confirmpassword"
password-check="{{password.selected}}"
ng-show="resetpasswordform.confirmpassword.$error.passwordCheck && submitted" Password does not match
I had the same problem when i was trying to build my own directive, and i fixed with this add
ctrl.$validate();
where ctrl is my ngModelController
this is my view
<input type="password" match="signupCtrl.registrationData.password" name="confirmPassword" class="form-control" placeholder="Confirm Password" data-ng-model="signupCtrl.registrationData.confirmPassword" required>
<span ng-messages="registerForm.confirmPassword.$error">
<span ng-message="match">The Password must match</span>
</span>
this is my directive
(function () {
'use strict';
angular.module('matchDirective', [
// Angular modules
// Custom modules
// 3rd Party Modules
]);
})();
(function () {
'use strict';
angular
.module('matchDirective')
.directive('match', match);
match.$inject = ['$window'];
function match($window) {
// Usage:
// <element match="source"></element>
// Creates:
//
var directive = {
link: link,
restrict: 'A',
require: 'ngModel',
};
return directive;
function link(scope, element, attrs, ctrl) {
scope.$watch(attrs['match'], function (newVal, oldVal) {
ctrl.$validators.match = function (modelValue, viewValue) {
if (newVal == modelValue) {
return true;
} else {
return false;
}
}
ctrl.$validate();
});
}
}
})();
Something like this works for me:
js:
.directive('sameAs', function() { return {
require : 'ngModel',
link : function(scope, elm, attrs, ngModelCtrl) {
ngModelCtrl.$validators.sameAs = function(modelValue, viewValue) {
var checkedVal = attrs.sameAs;
var thisInputVal = viewValue;
if (thisInputVal == checkedVal) {
return true; // valid
} else {
return false;
}
};
}
}; });
html:
<input type="password" name="password" id="password" ng-model="password" />
<input type="password" name="passwordRepeat" id="passwordRepeat"
ng-model="passwordRepeat" same-as="{{password}}" />
The Keep It Simple And Stupid(KISS) principle might be useful on this one. Its more faster and easier to check if both passwords match by doing the following:
<div ng-app="app" ng-controller="passwordCheck">
<form name="signUp" ng-submit="submitForm()" novalidate>
<input type="password" name="password" ng-model="password" required>
<input type="password" name="ConfirmPassword" ng-model="passwordconfirm" required>
<button type="submit"> Submit</button>
</form>
<hr>
<span>Do they match?</span> {{signUp.password.$viewValue == signUp.confirmPassword.$viewValue}}
</div>
And Before submitting the form, you can do this in your js
var app = angular.module("app", []);
app.controller("passwordCheck", function($scope) {
$scope.submitForm = function() {
if ($scope.signUp.$valid && $scope.signUp.password.$viewValue == $scope.signUp.confirmPassword.$viewValue) {
alert('Its a match!');
};
};
});
You can test it in JSfiddle as well.

Categories

Resources