angularjs Date Input only - javascript

I am trying to validate input for date (dd/mm/yyy) and then if it is not date don't allow to enter - such as strings or anything
Below is my code but not working - means allowing users to enter anything
angular.module('app')
.directive('onlyDates', function () {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, element, attr, ctrl) {
function inputValue(val) {
if (val) {
var reg = /^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((19|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\/02\/((19|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/g;
var res = reg.test(val);
if (!res) {
ctrl.$setViewValue(val);
ctrl.$render();
return NaN;
}
return val;
}
return undefined;
}
ctrl.$parsers.push(inputValue);
}
};
});

you could try using ng-change and some function that can check "as you type" that what you are entering matches your pattern, and if not, delete the last char entered.
<input type="text" ng-model="txtValue" ng-change="TextBoxChangedCheck()">
$scope.TextBoxChangedCheck = function () {
//// enter your text validation here
};

Related

Using an AngularJS directive, how do I bind to keyup event on input field and revert model to previous value if it fails a regular expression check?

Doing a quick POC in AngularJS to only allow specific input into a text box.
The goal is to check the value every time the user types a new character, if it fails the regular expression check, we need to either reject the character or roll it back to the previous value.
The way I see it, here are my 2 options:
1. Bind to keypress event, check what the new value would be against a regex, and return false if it fails, preventing the character from being accepted into the text box
2. Bind to keyup event, check what the new value is against a regex, and if it fails, revert it to the previous value
How can I accomplish this from my directive?
var currencyRegEx = /^\$?\-?([1-9]{1}[0-9]{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))$|^\-?\$?([1-9]{1}\d{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))$|^\(\$?([1-9]{1}\d{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))\)$/;
app.directive("currencyInput", function () {
return {
restrict: "A",
scope: {
},
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
$(element).bind('keypress', function (event) {
// TODO: Get what new value would be
var newValue = "...";
return currencyRegEx.test(newValue);
});
$(element).bind('keyup', function (event) {
var newValue = $(this).val();
if (!currencyRegEx.test(newValue)) {
// TODO: Revert to previous value
}
});
}
}
});
<input type="text" class="form-control" ng-model="item.paymentAmount" currency-input />
EDIT w/ SOLUTION
Here is the current solution we have in place in order to prevent non-digit input and rollback invalid currency value.
First, we created a new property "scope.prevValue" to hold the last valid value entered by the user. Then, on "keypress" we check to make sure the user typed a digit, comma, or period. Finally, on "keyup", we check the new value against the currency regex and rollback if needed.
var currencyRegEx = /^\$?\-?([1-9]{1}[0-9]{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))$|^\-?\$?([1-9]{1}\d{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))$|^\(\$?([1-9]{1}\d{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))\)$/;
var digitRegex = /^[0-9]*$/;
app.directive("currencyInput", function () {
return {
restrict: "A",
scope: {},
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
scope.prevValue = '';
$(element).on('keypress', function(event) {
var validAlphaChars = ['.', ','];
var enteredCharacter = String.fromCharCode(event.charCode != null ? event.charCode : event.keyCode);
if (validAlphaChars.indexOf(enteredCharacter) < 0 && !digitRegex.test(enteredCharacter)) {
return false;
}
});
$(element).on('keyup', function (event) {
var newValue = $(element).val();
if (newValue.length > 0 && !currencyRegEx.test(newValue)) {
$(element).val(scope.prevValue);
return false;
} else {
scope.prevValue = $(element).val();
}
});
}
});
EDIT w/ SOLUTION #2 (using Steve_at_IDV's approach on accepted answer)
var currencyRegEx = /^\$?\-?([1-9]{1}[0-9]{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))$|^\-?\$?([1-9]{1}\d{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))$|^\(\$?([1-9]{1}\d{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))\)$/;
app.directive("currencyInput", function () {
return {
restrict: "A",
scope: {},
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
ngModelCtrl.$parsers.push(function (value) {
if (value.length > 0 && value != '.' && !currencyRegEx.test(value)) {
var prevValue = ngModelCtrl.$modelValue;
ngModelCtrl.$setViewValue(prevValue)
ngModelCtrl.$render();
return prevValue;
}
return value;
});
}
}
});
This would be a good time to use ngModelCtrl.$parsers instead of binding to keypresses manually. Try something like this in your link function:
ngModelCtrl.$parsers.push( function (value) {
// do some validation logic...it fails
if (validationFails) {
var prevValue = ctrl.$modelValue;
ctrl.$setViewValue(prevValue); // set view
ctrl.$render(); // render view
return prevValue; // set model
}
// otherwise we're good!
return value;
} );
Here is a Plunker which demonstrates. The input field will reject a lowercase z from being entered.
See the $parsers section of https://docs.angularjs.org/api/ng/type/ngModel.NgModelController for more info.
Firstly, I think you shouldn't modify the input of the user. I personnaly find it bad on a UX point of view. It's better to indicate that the input is in an error state by bordering in red for example.
Secondly, there is a directive that can fit your need, ng-pattern.
<input type="text"
class="form-control"
ng-model="item.paymentAmount"
ng-pattern="currencyRegEx" />
Some similar questions :
Angularjs dynamic ng-pattern validation
How to only allow the numbers 0-5 in <input> fields with AngularJS?

AngularJS Restrict input to numbers does not work

I have an input box on my html page with gets databound to an ng-model. I use a directive to restrict the input field to numbers only. I cannot used the default HTML5 restrictions like type="number". The problem I have is that if a non digit char is entered twice, e.g to press 123 and then two times k, the k is added to 123 resulting in 123k. However when I press another key, the k gets removed by the directive.
Can someone help me out to fix the problem that the letter appears if you press the same key twice
The directive I use:
angular.module('app').
directive('onlyDigits', function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, ngModel) {
if (!ngModel) return;
ngModel.$parsers.unshift(function (inputValue) {
var digits = inputValue.split('').filter(function (s) { return (!isNaN(s) && s != ' '); }).join('');
ngModel.$viewValue = digits;
ngModel.$render();
return digits;
});
}
};
});
This is what we use to achieve numbers only in a directive:
directives.directive('numbersOnly', function () {
'use strict';
return {
require: 'ngModel',
link: function (scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
if (inputValue === undefined) {
return '';
}
var transformedInput = inputValue.replace(/[^0-9]/g, '');
if (transformedInput !== inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
};
});

javascript + regular expression

I have a problem. I want to use a regular expression that let me introduce just numbers and "." (for decimals) on an input field, no letters and other special character.
I'm trying this:
option 1 => var restrict="[^\d+]"
option 2 => var restrict="[^\d+]"
iAttrs.restrict = ^(?![0-9]+([.]?[0-9]+))$
value.toLowerCase().replace(new RegExp(iAttrs.restrict, 'g'), '');
This regular expression is an angular directive
appModule.directive('restrict', function($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, iElement, iAttrs, controller) {
scope.$watch(iAttrs.ngModel, function(value) {
if (!value) {
return;
}
value = value.toString();
$parse(iAttrs.ngModel).assign(scope, value.toLowerCase().replace(new RegExp(iAttrs.restrict, 'g'), ''));
});
}
}
});
It must remove the wrong characters written on the input. But the problem is
option 1 ==> don't let me write "." character
option 2 ==> don't let me write nothing (when I have a default value example: "300.21" that must appear on the input field ... after restrict directive finish, nothing is written on the input.
Can somebody help me?
Thanks
Updated based on comments:
As the case for decimal point is very particular, I created a new directive for that.
Directive Code:
angular.module("app",[]).directive('allowOnlyDecimal', function($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, iElement, iAttrs, controller) {
// used to find out if more than one . is available
function nth_ocurrence(str, needle, nth) {
for (i=0;i<str.length;i++) {
if (str.charAt(i) == needle) {
if (!--nth) {
return i;
}
}
}
return false;
}
scope.$watch(iAttrs.ngModel, function(value) {
if (!value) {
return;
}
value = value.toString();
var temp = value.replace(/[^(\d\.)]/gi, '');
// this removes any special character and alphabets
if(nth_ocurrence(temp,".",2)) {
temp = temp.substr(0, nth_ocurrence(temp,".",2));
// removes if user enters more than one decimal point
}
scope[iAttrs.ngModel] = temp;
});
}
};
}).controller("MainController", function($scope) {
$scope.someInput = 100.400;
});
In your HTML:
<input type="text" ng-model="someInput" allow-only-decimal>
WORKING DEMO
OLD ANSWER:
This could be more generic approach which you can use for most of the restrict functionality using regex.
HTML :
<input type="text" ng-model="someInput" restrict="[^(\d\.)]">
JS:
angular.module("app",[]).directive('restrict', function($parse, $timeout) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, iElement, iAttrs, controller) {
scope.$watch(iAttrs.ngModel, function(value) {
if (!value) {
return;
}
value = value.toString();
$timeout(function() {
scope[iAttrs.ngModel] = value.replace(new RegExp(iAttrs.restrict,'gi'), '');
},10);
});
}
};
}).controller("MainController", function($scope) {
$scope.someInput = 100.400;
});
You can use a simple regex like this:
^\d+\.*\d*$
Working demo

Why is my directive messing up my validations?

I ripped this directive off from this SO question.
var directive = function() {
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, element, attr, ctrl) {
function inputValue(val) {
if (val) {
var digits = val.replace(/[^0-9]/g, '');
if (digits !== val) {
ctrl.$setViewValue(digits);
ctrl.$render();
}
return parseInt(digits, 10);
}
return undefined;
}
ctrl.$parsers.push(inputValue);
}
};
};
module.exports = directive;
I want to limit my input to only accept numbers, but now my other validators, like min and max, are not working. My input is now always in an error state. What part of this directive is effecting the validation lifecycle in angular?

angularjs: parsing date input

I need a directive which will parse user input to date and validate it. So I wrote the following:
myDirectives.directive('myDate', function($filter) {
'use strict';
return {
require:'ngModel',
restrict:'A',
link:function (scope, elem, attrs, ctrl) {
var dateFormat = attrs.myDate ? attrs.myDate : 'shortDate';
ctrl.$formatters.unshift(function(modelValue) {
return $filter('date')(modelValue, dateFormat);
});
ctrl.$parsers.unshift(function(viewValue) {
var date = new Date(viewValue);
if (isNaN(date)) {
ctrl.$setValidity('date', false);
return undefined;
} else {
var dateString = $filter('date')(date, dateFormat);
if (dateString !== viewValue) {
ctrl.$setViewValue(dateString);
}
ctrl.$setValidity('date', true);
return date;
}
});
}
};
});
Parsing need to occur only after when input loses focus, so I use another directive, which I found here. The problem is
ctrl.$setViewValue(dateString);
won't work, because as indicated in angularjs documentation, setViewValue() must be called from within a DOM event handler. What should I do to reflect back parsing result?
Instead of
ctrl.$setViewValue(dateString);
I needed to write
elem.val(dateString);
and the problem was solved. So, my directive now looks like below:
myDirectives.directive('myDate', function ($filter) {
'use strict';
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, elem, attrs, ctrl) {
var dateFormat = attrs.myDate ? attrs.myDate : 'shortDate';
ctrl.$formatters.unshift(function (modelValue) {
return (modelValue) ? $filter('date')(modelValue, dateFormat) : '';
});
ctrl.$parsers.unshift(function (viewValue) {
var date = new Date(viewValue);
if (isNaN(date)) {
ctrl.$setValidity('date', false);
return undefined;
} else {
var dateString = $filter('date')(date, dateFormat);
if (dateString !== viewValue) {
elem.val(dateString);
}
ctrl.$setValidity('date', true);
return date;
}
});
elem.unbind('input').unbind('keydown').unbind('change');
elem.bind('blur', function () {
scope.$apply(function () {
ctrl.$setViewValue(elem.val()); //this method name is misleading;
//it actually sets model value?!
});
});
}
};
});
Note, that I incorporated the code from another directive which was responsible for pushing view value to model, when focus is lost.
I've created a date parser that converts string to Date objects. You can also provide date formats you're using, as well as your date locale. It returns a Date object, valid or otherwise depending on the input.
There's also a directive that implements this parser so you can use it on an input field, for example.
https://github.com/dnasir/angular-dateParser
This should work:
var setter = $parse('ngModel').assign;
setter($scope,valueToSet);
$scope.$apply() // This may be needed depending on where its done.
Refenece: http://docs.angularjs.org/api/ng.$parse
on html:
<input class="form-control""
type="date"
string-to-date
ng-model="val.VAL">
On Controller create a directive
.directive('stringToDate', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$parsers.push(function(value) {
return new Date(value);
});
ngModel.$formatters.push(function(value) {
var fechaactual = new Date(value);
return fechaactual;
});
}
};
})
note that the directive call stringToDate and you have to call it as string-to-date in the html. Dont not why :)

Categories

Resources