I have a sign up form for an application, and angular js is responsible for its validation.
I ran into an issue when Angular js wasn't accepting an email address which has apostrophe in it. "Pear'lpeerh.shin#xyz.com" .
I found out that angularJs doesnt like unicode characters in email address.
Has anyone else came across an issue like this, I am interested in knowing my options to get away with this bug in angularJs.
Any inputs are appreciated. Thanks !
If having html5 <input type=email /> is not critical, you can use <input type=text /> and pattern validation
<input type="text" ng-model="field" ng-pattern="EMAIL_REGEXP" />
and you can use regex that #Andy Joslin posted in his answer
$scope.EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+#[a-z0-9-]+(\.[a-z0-9-]+)*$/i;
AngularJS uses this regular expression to test email: https://github.com/angular/angular.js/blob/master/src/ng/directive/input.js#L4
What you could do is write a directive that checks it yourself. Just copy the one from AngularJS and use your own regexp: https://github.com/angular/angular.js/blob/master/src/ng/directive/input.js#L606-L614
myApp.directive('nanuEmail', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attrs, model) {
//change this:
var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
var emailValidator = function(value) {
if (!value || EMAIL_REGEXP.test(value)) {
model.$setValidity('email', true);
return value;
} else {
model.$setValidity('email', false);
return undefined;
}
model.$parsers.push(emailValidator);
model.$formatters.push(emailValidator);
}
};
});
Then you can just do:
<input nanu-email ng-model="userEmail">
I just updated the regex in the angular.js file (added " ' " in the expression) and it worked, without making any other changes.
EMAIL_REGEXP = /^[A-Za-z0-9._%+-']+#[A-Za-z0-9.-]+.[A-Za-z]{2,4}$/ . Thanks Vittore, for giving me the idea to update REGEX. :)
why do you return undefined?
Refactoring of the function:
var verificationEmail = function (viewValue) {
if ((typeof viewValue != "undefined") && viewValue != "") {
return regex.test(viewValue);
}
};
Angular do not support the apostrophe(') in email Id. If you need to validate the apostrophe in Angular, you need to change the regular expression from:
(/^[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/)
To:
/^[A-Za-z0-9._%+'-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/.
It will work perfectly.
Related
Basically what I need to be able to register two types of code. The code type A has only numbers and the code Type B has numbers, one hyphen, one alphabetic value and one numeric value.
Hypothetical situation:
I register the code type A, the custom directive validate all values.
I need to register the code type B, I check the option 'Validate code
Type B', the pattern is changed in order to make the validation, then all values entered are validated.
Code - Type A
12345678
32445678
56535678
Code with complement number - Type B
32445678-a1
32445678-a2
65434567-b1
The form
<form>
<span>Insert code:</span><br/>
<input type="text" data-ng-model="code" code-type> <br/>
<input type="checkbox" data-ng-model="validateCodeTypeB" /> Validate code Type B
</form>
Note: When the 'validateCodeTypeB' option is checked the regex will be changed too. But I don't know how to implement the second regex in order to validate the code Type B.
See the example
And the directive
app.directive('codeType', function () {
return {
require: 'ngModel',
link: function (scope, element, attr, ngModelCtrl) {
function codeTypeA(text) {
if (text) {
var transformedInput = text.replace(/[^0-9]/g, '');
if (transformedInput !== text) {
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
}
return transformedInput;
}
return undefined;
}
ngModelCtrl.$parsers.push(codeTypeA);
}
}
});
Here I have made you plnkr for your solution:
if(!scope.codeType){
transformedInput = text.replace(/[^0-9]/g, ''); // For CodeTypeA
} else {
transformedInput = text.replace(/[^a-z]/g,''); // For CodeTypeB, change it to your own regex
}
https://plnkr.co/edit/IfIeXhy7vZacLi2jKsMs?p=preview
but regex is different for CodetypeB , change it to your regex for codetypeB and its good to go.
So I am new to Angular.
What I want to do is to force an input field (text) to be as the following:
*Uppercase only, or change the letters to uppercase automatically
*Max 30 characters
*No special characters
If someone tries to insert a special character it won't be displayed at all.
So it's not only validation.
It's accepting those rules on the input.
And I want all of this to be done on that field on a specific condition: let's say when the placeholder is equal to "Nickname" which is in my model formField.CustomFieldName
Thank you in advance
There are at least two ways you can do. Use ng-keypress to either check each character you enter or check regular expression on the input.
I am not going to give the entire solution but you can go from here.
<div ng-app="myApp">
<div ng-controller="myController">
<input type="text" ng-model="yourInput" ng-keypress="yourKeypressFunction($event)">
</div>
</div>
In your js:
var myApp = angular.module('myApp',[]);
myApp.controller('myController', ['$scope', function($scope) {
$scope.yourInput = "";
$scope.yourKeypressFunction = function(event) {
console.log(event); // check for event.which, if it is not the char you want, return;
console.log($scope.yourInput); // check for regular expression
}
}]);
https://docs.angularjs.org/api/ng/directive/ngKeypress
Try onchange event and write a method in the component that you are looking to do, or if you are using observables then try to use the observable methods to transform your input to your requirement.
Check this link http://blog.angular-university.io/introduction-to-angular-2-forms-template-driven-vs-model-driven/
This has good explanation how to deal with forms and input fields
you can do it like this using jQuery for restriction of characters
$("#youTextFieldId").keypress(function(e)
{
var code = e.which || e.keyCode;
// 65 - 90 for A-Z and 97 - 122 for a-z 95
if (!((code >= 65 && code <= 90) || !(code >= 97 && code <= 122) )
{
e.preventDefault();
}
});
and for your text limit
<input id="youTextFieldId" type="text" maxlength="30" style="text-transform:uppercase">
try to use ng-pattern=you pattern with maxlength in your text field. for eg:
<input type="text" ng-pattern="/^[a-zA-Z0-9]*$/" maxlength="30">
The /^[a-zA-Z0-9]*$/ will allow you to enter only character and numbers only. not any special characters.
Use https://github.com/candreoliveira/ngMask with a little study of documentation your pattern is very easy to create and it will also force that the user inputs the desired pattern only and on top of that you can just write maxlength="30" ,input type="text"
Look out at this example for better understanding in the demo of the ngMask ngMask String Example Demo
So this is the solution to this problem.
I hope it helps someone.
I made a fiddle for this: http://jsfiddle.net/m152d0t9/
var app = angular.module("myApp", []);
function Fiddle($scope) {}
app.directive('customInputFormat', function() {
return {
restrict: 'A',
require: '?ngModel',
scope: {
customInputFormat: '='
},
link: function(scope, elm, attr, ctrl) {
if (!ctrl) {
return;
}
scope.$watch(attr.ngModel, function(newVal, oldVal) {
if (!scope.customInputFormat) {
return;
}
if (newVal && newVal.length) {
var newStr = newVal.replace(/[^a-zA-Z0-9\s]*/g, '').toUpperCase();
if (newStr.length > 20) {
newStr = newStr.substring(0, 20);
}
ctrl.$setViewValue(newStr);
ctrl.$render();
}
})
}
};
});
And for the HTML:
<ul ng-controller="Fiddle">
<li>
<label data-ng-init="condition = true;" for="foobar"> Input text</label>
<input custom-input-format="condition" id="foobar" name="foobar" type="text" ng-model="foo" required />
</li>
</ul>
Here's a jsfiddle example of what I'm trying to accomplish.
I'm trying to build a US phone number input where the view displays as (333) 555-1212, but the model binds to the numeric integer 3335551212.
My intention is to add custom validators to NgModelController which is why I have require: ng-model; there are simpler solutions without the isolate scope and NgModelController, but I need both.
You'll see an immediate error in the console: Error: Multiple directives [ngModel, ngModel] asking for 'ngModel' controller on: <input ng-model="user.mobile numeric" name="telephone" type="tel"> -- thought I was using an isolate scope here...
Thank you for looking #mimir137 but I appear to have solved it:
http://jsfiddle.net/hr121r18/8/
The directive was using replace: true, which ends up with this structure:
<form ng-controller="FooCtrl" class="ng-scope">
<p>Enter US phone number</p>
<input ng-model="user.mobile numeric" name="telephone" type="tel">
</form>
Both the template and the markup called for ng-model which led to the symptomatic error in the problem description. Once I removed that, it leads to this markup (note the wrapper element phone-number):
<form ng-controller="FooCtrl" class="ng-valid ng-scope ng-dirty ng-valid-parse" abineguid="BC0D9644F7434BBF80094FF6ABDF4418">
<p>Enter US phone number</p>
<phone-number ng-model="user.mobile" class="ng-untouched ng-valid ng-isolate-scope ng-dirty ng-valid-parse">
<input ng-model="numeric" name="telephone" type="tel" class="ng-valid ng-dirty ng-touched">
</phone-number>
</form>
But removing this required changes to $render; the elem passed into the link function is now phone-number and so you need to dig to grab the input inside it and set the value on that:
ngModel.$render = function () {
elem.find('input').val($filter('phonenumber')(ngModel.$viewValue));
};
There were a few other issues. $render() also needed to be called from the watcher.
Final:
var app = angular.module('myApp', []);
// i want to bind user.mobile to the numeric version of the number, e.g. 3335551212, but
// display it in a formatted version of a us phone number (333) 555-1212
// i am trying to make the directive's scope.numeric to have two-way binding with the controller's
// $scope.user.mobile (using isolate scope, etc.).
app.controller('FooCtrl', function ($scope) {
$scope.user = {
mobile: 3335551212
};
});
app.directive('phoneNumber', ['$filter', function ($filter) {
return {
restrict: 'E',
template: '<input ng-model="numeric" name="telephone" type="tel">',
require: 'ngModel',
scope: {
numeric: '=ngModel'
},
link: function (scope, elem, attrs, ngModel) {
// update $viewValue on model change
scope.$watch('numeric', function () {
ngModel.$setViewValue(scope.numeric);
ngModel.$render();
});
// $modelValue convert to $viewValue as (999) 999-9999
ngModel.$formatters.push(function (modelValue) {
return $filter('phonenumber')(String(modelValue).replace(/[^0-9]+/, ''));
});
// $viewValue back to model
ngModel.$parsers.push(function (viewValue) {
var n = viewValue;
if (angular.isString(n)) {
n = parseInt(n.replace(/[^0-9]+/g, ''));
}
return n;
});
// render $viewValue through filter
ngModel.$render = function () {
elem.find('input').val($filter('phonenumber')(ngModel.$viewValue));
};
}
};
}]);
app.filter('phonenumber', function () {
return function (number) {
if (!number) {
return '';
}
number = String(number);
var formattedNumber = number;
var c = (number[0] === '1') ? '1 ' : '';
number = number[0] === '1' ? number.slice(1) : number;
var area = number.substring(0, 3),
exchange = number.substring(3, 6),
subscriber = number.substring(6, 10);
if (exchange) {
formattedNumber = (c + '(' + area + ') ' + exchange);
}
if (subscriber) {
formattedNumber += ('-' + subscriber);
}
return formattedNumber;
}
});
HTML
<form ng-controller="FooCtrl">
<p>Enter US phone number</p>
<phone-number ng-model='user.mobile'></phone-number>
</form>
I created this fiddle that gets rid of most of your errors coming up in the console. Hopefully this will at least be able to put you on the right track.
I changed the template so that you can see that the filter is actually working.
It now has the typical {{ngModel | FilterName}} in plain text underneath the textbox.
The only real issue is displaying it in the textbox. I'm sure you will have no problem with that. I will check in the morning just in case you still have questions regarding this.
Edit: Alright it appears you have solved it already. Great job!
I have a simple html form containing regular text input. ng-minlength, ng-maxlength and ng-pattern angular built-in form input directives are set on the input.
Problem: ng-pattern check is applied before the length check by ng-minlength and ng-maxlength.
Question: how can I change the default check order: i.e. first check for the length, then apply pattern check?
Example:
<body ng-app>
<div>
<form name="myForm">
Name: <input name="name" type="text" ng-model="name" ng-minlength="3" ng-maxlength="16" ng-pattern="/^\w+$/"/>
<div ng-show="myForm.name.$dirty && myForm.name.$invalid">
<span ng-show="myForm.name.$error.pattern">Pattern error</span>
<span ng-show="myForm.name.$error.minlength || myForm.name.$error.maxlength">Length error</span>
</div>
<br/>
<input type="submit" value="Submit">
</form>
</div>
</body>
Current behavior:
enter "#" - see "Pattern error"
enter "###" - see "Pattern error"
Desired behavior:
enter "#" - see "Length error"
enter "###" - see "Pattern error"
FYI, related jsfiddle.
Thanks in advance.
Write your own directive:
var mod = angular.module("myApp", []);
mod.directive("nameValidation", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, element, attrs, ngModelCtrl) {
var validate = function (value) {
var minLen = parseInt(attrs.myMinlength, 10),
maxLen = parseInt(attrs.myMaxlength, 10),
pattern = attrs.myPattern,
match = pattern.match(/^\/(.*)\/([gim]*)$/),
lenErr = false;
if (match) {
pattern = new RegExp(match[1], match[2]);
}
if (!ngModelCtrl.$isEmpty(value)) {
ngModelCtrl.$setValidity("pattern", true);
if ((minLen && value.length < minLen) || (maxLen && value.length > maxLen)) {
ngModelCtrl.$setValidity("length", false);
lenErr = true;
}
else {
ngModelCtrl.$setValidity("length", true);
lenErr = false;
}
if (!lenErr) {
if (match && !pattern.test(value)) {
ngModelCtrl.$setValidity("pattern", false);
}
else {
ngModelCtrl.$setValidity("pattern", true);
}
}
}
else {
ngModelCtrl.$setValidity("length", true);
ngModelCtrl.$setValidity("pattern", true);
}
}
ngModelCtrl.$parsers.push(validate);
ngModelCtrl.$formatters.push(validate);
}
}
});
Then in your HTML, include the app and use the directive:
<body ng-app="myApp">
<div>
<form name="myForm">
Name: <input name="name" type="text" ng-model="name" name-validation="" my-minlength="3" my-maxlength="16" my-pattern="/^\w+$/"/>
<div ng-show="myForm.name.$dirty && myForm.name.$invalid">
<span ng-show="myForm.name.$error.pattern">Pattern error</span>
<span ng-show="myForm.name.$error.length">Length error</span>
</div>
<br/>
<input type="submit" value="Submit">
</form>
</div>
</body>
The directive uses my-minlength, my-maxlength, and my-pattern for the three values. If length fails, that will trip first. If not, then pattern will show as error if it doesn't match. Consider renaming this directive if you want to use it other places besides name as minlength, maxlength, and pattern can be passed to it via attributes. If they are left off, they will be ignored.
See jsfiddle: http://jsfiddle.net/4zpxk/6/
I searched in angular code why this behavior. Then in the function 'textInputType' that it's the specific function that handles text inputs for the angular 'input' directive I found this at the end of this function, where we can see three blocks of code.
// pattern validator
if (pattern){
//validator logic
}
// min length validator
if (attr.ngMinlength) {
//validator logic
}
// max length validator
if (attr.ngMaxlength) {
//validator logic
}
So, no matter if you change the declaration order of your ng-* attributes in the html input element you will always get same result but if you change the order of the blocks, I mean, put the min length validator block before pattern validator block you will have the result that you expect.
This is a solution for your problem but you have to make a litte change in angular code and I don't know if you really like this. But you got a very common situation where order of the declaration of validation concepts matters, so, something more must be done to handle this. Thanks
You cannot change the default check order unfortunately.
One solution is to write a custom validator, not that difficult. Based on this answer, I came up with this code (fiddle)
Usage: There is an array of validation functions in the scope, they get passed to our custom directive "validators" as:
<input name="name" type="text" ng-model="name" validators="nameValidators"/>
A validator function would look like (e.g. for the minlength constraint):
function minlength(value, ngModel) {
if( value == null || value == "" || value.length >= 3 ) {
ngModel.$setValidity('minlength', true);
return value;
}
else {
ngModel.$setValidity('minlength', false);
return;
}
}
Important points are: it takes the value and the ngModel as arguments, performs the test (here value.length >= 3) and calls ngModel.$setValidity() as appropriate.
The directive registers the given functions with ngModel.$parsers:
app.directive("validators", function($parse) {
return {
restrict: "A",
require: "ngModel",
link: function(scope, el, attrs, ngModel) {
var getter = $parse(attrs.validators),
validators = getter(scope),
i;
for( i=0; i < validators.length; i++ ) {
ngModel.$parsers.push((function(index) {
return function(value) {
return validators[index](value, ngModel);
};
})(i));
}
}
};
});
Many details can be tweaked and improved, but the outline works (again link to fiddle). Now the order of validation is explicitly set by the order of the validator functions in the nameValidators array.
If you use ng-messages you should be able to set the order via the order of ng-message elements, e.g:
<div ng-messages="field.$error">
<ul class="validation-errors">
<li ng-message="required">This has the highest prio</li>
<li ng-message="min">Second in command</li>
<li ng-message="max">I'm last</li>
</ul>
</div>
Also the docs on this: https://docs.angularjs.org/api/ngMessages/directive/ngMessages
i just changed the order of your directives, pattern first
<input name="name" type="text" ng-model="name" ng-pattern="/^\w+$/" ng-minlength="3" ng-maxlength="16"/>
EDIT: uuum, tested your fiddel without changes and it shows your desired behavior ...
directives are compiled by priority, bbut i don't know how to set angulars directives priority ... sorry, should have tested this first
I am currently using Backbone.validate to do form validations.
I need to be able to validate pairs of fields, meaning if you start filling in 'field1' then you have to fill in 'field2' as well, and the other way around. If you leave them blank then the validation passes.
I have backbone.validate setup and working for single fields.
is there anything that i can use that is already part of validation to declare this? i cannot seem to be able to find anything related.
Thanks!
So here is how i solved it using Backbone.validation
https://github.com/thedersen/backbone.validation
validateFlickr: function (value, attr, computedState) {
return this.validatePairs(value, attr, computedState, "flickr");
},
validatePairs: function (value, attr, computedState, name) {
var totalNotBlank =
(computedState["app_id_" + name] != "" ? 1 : 0)
+ (computedState["app_secret_" + name] != "" ? 1 : 0);
if (totalNotBlank == 1) {
return "Need to enter " + name + " App ID and App Secret";
}
},
And the validation hash:
validation: {
app_id_flickr: "validateFlickr",
app_secret_flickr: "validateFlickr"
}
There is nothing built-in to manage this. But you can use the validator API to add your custom methods:
JS
$.validator.addMethod( "custom-rule", function( value ) {
return true; // put your logic here
}, function() {
return "error message";
});
HTML:
<input type="text" name="foo" class="custom-rule">