I have a form where the user needs to enter 2 times his email.
I found on the internet (but not able to find the link anymore) some angular code doing what I wanted.
The only issue is when the 2 emails are the same, one input still got the class
.ng-invalid.ng-dirty
So I can't submit the form.
My code is the following:
css:
.ng-invalid.ng-dirty{
border-color: #FA787E;
}
.ng-valid.ng-dirty{
border-color: #78FA89;
}
html:
<div ng-app="myApp">
<form name="formTemplate" novalidate>
<input id="email1" type="email" ng-model="currentForm.email1" name="email1" required>
<span ng-show="formTemplate.email1.$error.required && formTemplate.email1.$dirty">required</span>
<span ng-show="!formTemplate.email1.$error.required && formTemplate.email1.$error.email1 && formTemplate.email1.$dirty">invalid email</span>
<input id="email2" type="email" name="email2" ng-model="currentForm.email2" same-email required>
<span ng-show="formTemplate.email2.$error.required && formTemplate.email2.$dirty">Please confirm your email.</span>
<span ng-show="!formTemplate.email2.$error.required && formTemplate.email2.$error.noMatch && formTemplate.email1.$dirty">Emails do not match.</span>
</form>
</div>
javascript:
var app = angular.module('myApp', ['UserValidation']);
angular.module('UserValidation', []).directive('sameEmail', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue, $scope) {
var noMatch = viewValue != scope.formTemplate.email1.$viewValue;
ctrl.$setValidity('noMatch', !noMatch)
})
}
}
})
Here a jsfiddle: http://jsfiddle.net/malamine_kebe/pq6fw04v/
Im update your jsfiddle. You don't return value from ctrl.$parsers.unshift.
ctrl.$parsers.unshift(function (viewValue, $scope) {
var noMatch = viewValue != scope.formTemplate.email1.$viewValue;
ctrl.$setValidity('noMatch', !noMatch);
return noMatch;
})
Updated jsfiddle
Related
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>
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
I have signup form in my ionic app (the code available here), the signup view has it own controller. the password confirm has a Directive to check the match with the main password. the register button is disabled till the form is valid. but the form never becomes valid. the password_c input is always invalid.
<ion-view view-title="Register Form">
<ion-content>
<form ng-submit="signup()" name="regForm" novalidate>
<div>
<label for="email" .....></label>
<label for="password" ......></label>
<label for="password_c">
<input type="password" id="password_c" name="password_c" ng-model="registerForm.password_c" valid-password-c required>
<span ng-show="regForm.password_c.$error.required && regForm.password_c.$dirty">Please confirm your password.</span>
<span ng-show="!regForm.password_c.$error.required && regForm.password_c.$error.noMatch && regForm.password.$dirty">Passwords do not match.</span>
</label>
<button type="submit" ng-disabled="!regForm.$valid">
</div>
</form>
</ion-content>
</ion-view>
and this is the directive:
.directive('validPasswordC', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue, $scope) {
var noMatch = viewValue != scope.regForm.password.$viewValue
ctrl.$setValidity('noMatch', !noMatch)
})
}
}
})
I do console.log the ng-show condition; when the passwords matchs, the condition becomes undefined.
console.log(!regForm.password_c.$error.required && regForm.password_c.$error.noMatch && regForm.password.$dirty)
separately; first the .required become undefined then .noMatch
I can't test it now, but I believe $parsers expects you to return a value unless you have a parser error.
Try to add a return in your code:
ctrl.$parsers.unshift(function (viewValue) {
var noMatch = viewValue != scope.regForm.password.$viewValue;
ctrl.$setValidity('noMatch', !noMatch);
return viewValue;
});
I have an html form that looks like this :
<div class="row col-lg-offset-3">
<div class="form-group col-lg-6" ng-class="{ 'has-error': userForm.Age.$invalid && userForm.Age.$dirty}" show-errors >
<label class="control-label">Age</label>
<input type="text" class="form-control" name="Age" ng-model="user.Age" ng-required='!user.phonenumber' placeholder="Age"/>
</div>
</div>
Directive:
(function(){
angular.module('myApp', [])
.controller('studentDataController', function($scope) {})
.directive('showErrors', function() {
return {
restrict: 'A',
require: '^form',
link: function (scope, el, attrs, formCtrl) {
var inputEl = el[0].querySelector("[Age]");
var inputNgEl = angular.element(inputEl);
var inputValue = inputNgEl.attr('Age');
var isValid = (inputValue >= 3 && inputValue < 100);
inputNgEl.bind('blur', function() {
el.toggleClass('has-error', isValid);
})
}
}
});
})();
I am trying to validate input for Age field when it blurs out.Age value should be between 3 to 99.i.i.e check if the value is valid or invalid when user is done typing and leaves the text field.Then if the value is invalid, apply the has- class
The directive though is not working. Did I miss anything ?
If really have to do that via custom directive please see below:
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app
.directive('ngAge', NgLength);
function NgLength() {
return {
restrict: 'A',
require: 'ngModel',
link: function($scope, $element, $attrs, ngModel) {
$scope.$watch($attrs.ngModel, function(value) {
var isValid = (value > 3 && value < 100);
ngModel.$setValidity($attrs.ngModel, isValid);
});
}
}
}
/* Put your css in here */
.has-error {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<form name="userForm" ng-class="{ 'has-error': userForm.Age.$invalid && userForm.Age.$dirty}">
<label class="control-label">Age</label>
<input type="text" class="form-control" name="Age" ng-model="user.Age" ng-age placeholder="Age" />
</form>
</body>
AngularJS 1.3.x introduces $validators pipeline - it is much simpler to write custom validation rules with them.
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.
var app = angular.module('app', []);
app
.controller('MainCtrl', function($scope) {
$scope.name = 'World';
}).directive('ngAge', function NgLength() {
return {
restrict: 'A',
require: 'ngModel',
link: function($scope, $element, $attrs, ngModel) {
ngModel.$validators.validAge = function(modelValue, viewValue) {
var age = modelValue || viewValue;
return age > 3 && age < 100
};
}
}
});
.has-error {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.min.js"></script>
<body ng-app="app">
<form name="userForm" ng-class="{ 'has-error': userForm.Age.$invalid && userForm.Age.$dirty}">
<label class="control-label">Age</label>
<input type="text" class="form-control" name="Age" ng-model="user.Age" ng-age placeholder="Age" />
</form>
</body>
You can use max, min directive. Please sample below
var app = angular.module('plunker', []);
.has-error {
background-color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link data-require="bootstrap-css#*" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<html ng-app="plunker">
<body>
<div class="row col-lg-offset-3">
<form name="userForm" class="form-group col-lg-6" ng-class="{ 'has-error': userForm.Age.$invalid && userForm.Age.$dirty}" show-errors>
<label class="control-label">Age</label>
<input type="number" class="form-control" name="Age" ng-model="user.Age" ng-required='!user.phonenumber' placeholder="Age" max="100" min="3" />
</form>
</div>
I have a form with input fields and have a custom directive for each input field to validate the data entered by user.Requirement is that when user leaves the input field with invalid data, error message to be displayed.However, few of the fields are optional that if User skips with out entering any data for these fields, no Validation is required.
I tried to implement this using Blur event in the directive, but this is causing the vaidation called even in case of no data entered.
Please advise if Watch function can be used here and any sample snippet here would be appreciated. I have written code in a fiddle which is similar to my directive for one of the input field that checks minimum length (although there is a built-in directive, taken this as an example).This fiddle can be accessed at http://jsfiddle.net/4xbv0tgL/49/
<div ng-app="myApp" ng-controller="myCtrl">
<form name="myForm">
Num1: <input type="text" ng-model="num1" my-min-length="5" name="num1" />
<span class="error" ng-hide="myForm.num1.$valid"
ng-show="myForm.num1.$error">Invalid Number!</span>
<br />
Num2: <input type="text" ng-model="num2" my-min-length="5" name="num2" />
<span class="error" ng-hide="myForm.num2.$valid"
ng-show="myForm.num2.$error">Invalid Number!</span>
</form>
</div>
var myApp = angular.module("myApp", []);
var myCtrl = myApp.controller("myCtrl",["$scope", function($scope) {
$scope.num1 = "12345";
$scope.num2 = "55555";
}]);
myApp.directive("myMinLength", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, element, attr, ngModelCtrl) {
var minlength = Number(attr.myMinLength);
var inputparse = function (inputtext) {
if ((inputtext.length >= minlength) && (!isNaN(inputtext))) {
return inputtext;
}
return undefined;
}
ngModelCtrl.$parsers.push(inputparse);
element.bind("blur", function () {
var value = inputparse(element.val());
var isValid = !!value;
if (isValid) {
ngModelCtrl.$setViewValue(value);
ngModelCtrl.$render();
}
ngModelCtrl.$setValidity("myMinLength", isValid);
scope.$apply();
}
);
}
};
});
I think you are over complicating life for yourself. Why don't you just use multiple directives for multiple checks?
<div ng-app="myApp"
ng-controller="myCtrl">
<form name="myForm"
novalidate>
Num1:
<input type="text"
ng-model="num1"
ng-minlength="5"
integer
name="num1"
required/>
<span class="error"
ng-show="myForm.num1.$invalid&&myForm.num1.$touched">Invalid Number!</span>
</form>
</div>
And here's the integer directive:
var INTEGER_REGEXP = /^[0-9]*$/;
myApp.directive('integer', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
if (viewValue === "") {
ctrl.$setValidity('integer', true);
return viewValue;
} else if (INTEGER_REGEXP.test(viewValue)) {
ctrl.$setValidity('integer', true);
return viewValue;
} else {
ctrl.$setValidity('integer', false);
return viewValue;
}
});
}
};
});
Just be sure that you are using angular 1.3 or a newer version. Because $touched and $untouched don't exist in 1.2.
I first using setValidity to make a directive in Angular.but not as my expected,here is my code:
angular.module('myApp', [])
.controller('ctrl',function($scope){
$scope.pw='';
})
.directive('pwCheck', function(){
return {
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
elem.on('keyup', function () {
scope.$apply(function () {
var len = elem.val().length;
if(len===0){
ctrl.$setValidity('zero',true);
} else if(len>1 && len<6){
ctrl.$setValidity('one',true);
} else {
ctrl.$setValidity('two',true);
}
});
});
}
};
});
HTML:
<body ng-controller="ctrl">
<form id="myForm" name="myForm">
<input type="text" ng-model="pw" pw-check />
{{myForm.$error}}
<div class="msg-block" ng-show="myForm.$error">
<span class="msg-error" ng-show="myForm.pw.$error.zero">
Input a password.
</span>
<span class="msg-error" ng-show="myForm.pw.$error.one">
Passwords too short.
</span>
<span class="msg-error" ng-show="myForm.pw.$error.two">
Great.
</span>
</div>
</form>
</body>
Online Demo:
http://jsbin.com/cefecicu/1/edit
I think you need:
//Reset your validity
ctrl.$setValidity('zero',true);
ctrl.$setValidity('one',true);
ctrl.$setValidity('two',true);
if(len===0){
ctrl.$setValidity('zero',false);
} else if(len>=1 && len<6){ //use len>=1 instead
ctrl.$setValidity('one',false);
} else {
ctrl.$setValidity('two',false);
}
Using false to indicate errors (not valid):
And give a name to your input:
<input type="text" ng-model="pw" name="pw" pw-check />
http://jsbin.com/cefecicu/11/edit