I am trying to mask SSN number using angular js.
Expected:
Before mask (onFocus)
After mask (onBlur)
User can enter only numbers and SSN formatting done by filters. Below is sample code I written.
Directive:
app.directive('taxId', function ($filter, $browser) {
return {
require: 'ngModel',
link: function ($scope, $element, $attrs, ngModelCtrl) {
var listener = function () {
var value = $element.val().replace(/[^0-9]/g, '');
if (value.length > 9)
value = value.slice(0, 9);
var type = $attrs.taxId;
$element.val($filter('taxId')(value, type, false));
};
// This runs when we update the text field
ngModelCtrl.$parsers.push(function (viewValue) {
return viewValue.replace(/[^0-9]/g, '').slice(0, 10);
});
// This runs when the model gets updated on the scope directly and keeps our view in sync
ngModelCtrl.$render = function () {
$element.val($filter('taxId')(ngModelCtrl.$viewValue, $attrs.taxId, false));
};
$element.bind('change', listener);
$element.bind('keydown', function (event) {
var key = event.keyCode;
// If the keys include the CTRL, SHIFT, ALT, or META keys, or the arrow keys, do nothing.
// This lets us support copy and paste too
if (key == 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) {
return;
}
$browser.defer(listener); // Have to do this or changes don't get picked up properly
});
if ($attrs.taxId== "ssn") {
$element.bind('blur', function () {
$scope.$apply(function () {
});
});
$element.bind('focus', function () {
$scope.$apply(function () {
});
});
}
$element.bind('paste cut', function () {
$browser.defer(listener);
});
}
};
});
Filter:
app.filter('taxId', function () {
return function (taxId, type) {
if (!taxId) { return ''; }
var value = taxId.toString().replace(/^\+/, '');
if (value.match(/[^0-9]/)) {
return taxId;
}
if (type.toLowerCase() == "fein") {
if (value.length == 10) {
value = value.slice(0, 2) + "-" + value.slice(2, value.length - 1);
return value;
} else if (value.length > 2) {
return (value.slice(0, 2) + "-" + value.slice(2, value.length));
}
else {
return value.slice(0, 9);
}
}
else if(type.toLowerCase() == "ssn"){
if (value.length > 5) {
return (value.slice(0,3) + "-" + value.slice(3,5) + "-" + value.slice(5,value.length));
}
else if (value.length > 3) {
return (value.slice(0, 3) + "-" + value.slice(3, value.length));
}
else {
return value;
}
}
};
});
I am able to format SSN successfully. But masking I am not able to do. I went through below link about masking but couldn't help much. I need to write special functions to mask and unmask in directive at 'blur' and 'focus'.
In the end ng-model should contain value "999993213" but value in UI should show as "*--3213" (both format and mask).
Appreciate your inputs. Thank you.
Masking in AngularJS
This should work.
var app = angular.module('myapp', []);
app.controller('MainCtrl', function($scope) {
$scope.modelssn = '';
});
app.directive("ssnInput",function(){
return {
require:'ngModel',
link: function(scop, elem, attr, ngModel){
$(elem).mask("999-99-9999");
var temp;
var regxa = /^(\d{3}-?\d{2}-?\d{4})$/;
$(elem).focusin(function(){
$(elem).val(temp);
});
$(elem).on('blur',function(){
temp = $(elem).val();
if(regxa.test($(elem).val())){
$(elem).val("XXX-XX" + temp.slice(6));
}
});
}
}
});
<!DOCTYPE html>
<html ng-app="myapp">
<head>
<script src="https://code.jquery.com/jquery.js"></script>
<script src="https://cdn.rawgit.com/digitalBush/jquery.maskedinput/master/src/jquery.maskedinput.js"></script>
<script data-require="angular.js#1.5.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" data-semver="1.5.11"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
Enter SSN <input type="text" ng-model="modelssn" ssn-input >
<p>Real SSN {{modelssn}} </p>
</body>
</html>
Related
I want to remove formated mask(either '$' or 'S/.') to the value. I have two angular directives. those are 1st directive. and 2nd directive.
In the second directive the symbol is not at all effecting to ng-model but the first directive it is effecting to ng-model.The first directive is removing the symbol, But if the text box is contains ng-change functions, it is calling ng-change functions then it is going to directive. so the symbol is effecting to ng-model.
The second directive is the symbol is not at all effecting to ng-model. Like this I need to the first directive too.
Second directive:-
app.directive('realTimeCurrency', function ($filter, $locale) {
var decimalSep = $locale.NUMBER_FORMATS.DECIMAL_SEP;
var toNumberRegex = new RegExp('[^0-9\\' + decimalSep + ']', 'g');
var trailingZerosRegex = new RegExp('\\' + decimalSep + '0+$');
var filterFunc = function (value) {
return $filter('currency')(value);
};
function getCaretPosition(input){
if (!input) return 0;
if (input.selectionStart !== undefined) {
return input.selectionStart;
} else if (document.selection) {
// Curse you IE
input.focus();
var selection = document.selection.createRange();
selection.moveStart('character', input.value ? -input.value.length : 0);
return selection.text.length;
}
return 0;
}
function setCaretPosition(input, pos){
if (!input) return 0;
if (input.offsetWidth === 0 || input.offsetHeight === 0) {
return; // Input's hidden
}
if (input.setSelectionRange) {
input.focus();
input.setSelectionRange(pos, pos);
}
else if (input.createTextRange) {
// Curse you IE
var range = input.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}
}
function toNumber(currencyStr) {
return parseFloat(currencyStr.replace(toNumberRegex, ''), 10);
}
return {
restrict: 'A',
require: 'ngModel',
link: function postLink(scope, elem, attrs, modelCtrl) {
modelCtrl.$formatters.push(filterFunc);
modelCtrl.$parsers.push(function (newViewValue) {
var oldModelValue = modelCtrl.$modelValue;
var newModelValue = toNumber(newViewValue);
modelCtrl.$viewValue = filterFunc(newModelValue);
var pos = getCaretPosition(elem[0]);
elem.val(modelCtrl.$viewValue);
var newPos = pos + modelCtrl.$viewValue.length -
newViewValue.length;
if ((oldModelValue === undefined) || isNaN(oldModelValue)) {
newPos -= 3;
}
setCaretPosition(elem[0], newPos);
return newModelValue;
});
}
};
});
First directive:-
app.directive('format', ['$filter', '$compile', function($filter, $compile) {
return {
require: 'ngModel',
scope: {
val: '=val'
},
link: function(scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$formatters.unshift(function(a) {
if (attrs.symbol == '$')
return $filter(attrs.format)(ctrl.$modelValue, '$')
else
return $filter(attrs.format)(ctrl.$modelValue)
});
scope.$watch(function() {
return elem.val()
}, function(newVal, oldVal) {
var a = newVal;
var plainNumber = a.split('.').filter(function(e) {
return (e.length > 0);
}).join('.');
var i = 0;
if (isNaN(parseFloat(plainNumber))) {
i = (attrs.symbol == '$') ? 1 : 3;
}
var num = plainNumber.substring(i, plainNumber.length).replace(/,/g, '');
if (attrs.symbol == '$')
elem.val($filter('currency')(num, attrs.symbol));
else
elem.val($filter('currency')(num));
var n = parseFloat(num);
scope.val = Number(n);
if (!scope.$$phase) {
$compile(elem.contents())(scope)
}
});
}
};
// controller
}])
If I understood your question right, you want to remove $ and/or S\. from a string which represents a number.
Hope this will work just fine:
plainNumber.replace(/(\S\/\.|\$)/g, '');
I want to check if an input is katakana characters or not.
This code works for one element :
var KANA_FULL_SIZE_REGEXP = /^([ァ-ン。、ー「」.\s]+)$/;
var KANA_HAFL_SIZE_REGEXP = /^([ァ-ン゙゚。「」、・ー\s]+)$/;
var KANA_ALL_SIZE_REGEXP = /^([ァ-ンァ-ン゙゚。`-「」.。、ー「」・\s]+)$/;
app.directive('kataKana', function() {
return {
// restrict: 'A',
// scope: {},
require: '?ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
if(!ngModelCtrl) {
return;
}
if (ngModelCtrl.$isEmpty(element.val())) {
scope.kanaerror = false;
}
element.bind('keypress', function(event) {
if(event.keyCode === 32) {
event.preventDefault();
}
});
/**
* var setObjFill
* setObjFill == 0 check katakana full size and half size
* setObjFill == 1 check katakana only full size
* setObjFill == 2 check katakana only half size
*/
element.bind('keyup', function(event) {
if (!element.val() || element.val() == null || element.val() == '') {
scope.kanaerror = false;
} else {
var setObjFill = attrs.kataKana.split(',');
if(setObjFill == 0) {
if (KANA_ALL_SIZE_REGEXP.test(element.val())) {
scope.kanaerror = false;
} else {
scope.kanaerror = true;
}
} else if (setObjFill == 1) {
console.log('ee');
if (KANA_FULL_SIZE_REGEXP.test(element.val())) {
scope.kanaerror = false;
} else {
scope.kanaerror = true;
}
} else {
if (KANA_HAFL_SIZE_REGEXP.test(element.val())) {
scope.kanaerror = false;
} else {
scope.kanaerror = true;
}
}
}
scope.$apply();
});
}
};
});
in html:
<input id="txt_kana" kata-kana="1" name="txt_kana">
The problem
If in that form we use kata-kana="1" twice, my code always return true or false for two elements using kata-kana="1". Because my code return scope.kanaerror. But I don't know how to resolve that problem.
I guess you want to check if the input is Katakana japanese language by watching scope.kanaerror which is a share scope. if you have multiple inputs but your scope.kanaerror is only one, then scope is not issolated that why you always get true or false(i guess).
I suggest you use ng-model for your input and check the model itself for each input if you have them more than two.
<input id="txt_kana" ng-model='input1' kata-kana name="txt_kana">
<input id="txt_kana" ng-model='input2' kata-kana name="txt_kana">
And in your directive:
app.directive('kataKana', function() {
return {
// restrict: 'A',
// scope: {},
require: '?ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
var KANA_ALL_SIZE_REGEXP = /\d+/g;
var KANA_FULL_SIZE_REGEXP = /[A-Z]/g;
var KANA_HAFL_SIZE_REGEXP = /[a-z]/g;
ngModelCtrl.$parsers.push(function(viewValue) {
// this is to limit input charactor
element.bind('keypress', function(event) {
if (event.keyCode === 5) {
event.preventDefault();
return false;
}
});
if (!viewValue) {
return false; // return to modelValue for controller
}
if (KANA_ALL_SIZE_REGEXP.test(viewValue)) {
return 'helo'; // return to modelValue for controller and interpolation
} else if (KANA_FULL_SIZE_REGEXP.test(viewValue)) {
return 'hi'; //return to modelValue for controller
} else if (KANA_HAFL_SIZE_REGEXP.test(viewValue)) {
return 'hey'; //return to modelValue for controller
}
})
}
};
});
I refactor your code but as I cannot type japanese so I change Regex. I hope you get the idea. in my code I make use the ngModelcontroler.$parsers and viewValue to return modelValue which use by controller and interpolation. Here is a working plunker. hope that help.
Change the line
// scope: {},
to
scope: true,
This will give each instance it's own scope that is inherited from the parent scope. At the moment they are all sharing the parent scope and when one attaches kanaerror to the scope it is on all.
My solution is return true or false not scope. And it's working.
link: function(scope, element, attrs, ngModelCtrl) {
if(!ngModelCtrl) {
return;
}
/**
* var setObjFill
* setObjFill == 0 check katakana full size and half size
* setObjFill == 1 check katakana only full size
* setObjFill == 2 check katakana only half size
*/
ngModelCtrl.$validators.katakana = function(model, value) {
if (value == '' || value == undefined){
return true;
}
var setObjFill = attrs.kataKana.split(',');
if (setObjFill == 0) {
var test = KANA_ALL_SIZE_REGEXP.test(value);
} else if (setObjFill == 1) {
var test = KANA_FULL_SIZE_REGEXP.test(value);
} else {
var test = KANA_HAFL_SIZE_REGEXP.test(value);
}
return test;
};
}
I am new to angularjs the below directive is to support decimal and commas, it works fine when a change is made to the field how ever the data in the fields are not validated when the page loads
var app = angular.module('myApply.directives', [], function () {
});
app.directive('numericDecimalInput', function($filter, $browser, $locale,$rootScope) {
return {
require: 'ngModel',
priority: 1,
link: function($scope, $element, $attrs, ngModelCtrl) {
var replaceRegex = new RegExp($locale.NUMBER_FORMATS.GROUP_SEP, 'g');
var fraction = $attrs.fraction || 0;
var listener = function() {
var value = $element.val().replace(replaceRegex, '');
$element.val($filter('number')(value, fraction));
};
var validator=function(viewValue) {
ngModelCtrl.$setValidity('outOfMax', true);
ngModelCtrl.$setValidity(ngModelCtrl.$name+'Numeric', true);
ngModelCtrl.$setValidity('rangeValid', true);
if(!_.isUndefined(viewValue))
{
var newVal = viewValue.replace(replaceRegex, '');
var newValAsNumber = newVal * 1;
// check if new value is numeric, and set control validity
if (isNaN(newValAsNumber)) {
ngModelCtrl.$setValidity(ngModelCtrl.$name + 'Numeric', false);
} else
{
if (newVal < 0) {
ngModelCtrl.$setValidity(ngModelCtrl.$name + 'Numeric', false);
}
else {
newVal = newValAsNumber.toFixed(fraction);
ngModelCtrl.$setValidity(ngModelCtrl.$name + 'Numeric', true);
if (!(_.isNull($attrs.maxamt) || _.isUndefined($attrs.maxamt))) {
var maxAmtValue = Number($attrs.maxamt) || Number($scope.$eval($attrs.maxamt));
if (newVal > maxAmtValue) {
ngModelCtrl.$setValidity('outOfMax', false);
} else {
ngModelCtrl.$setValidity('outOfMax', true);
}
if(!(_.isNull($attrs.minamt) || _.isUndefined($attrs.minamt)))
{
var minAmtValue = Number($attrs.minamt)|| Number($scope.$eval($attrs.minamt));
if((newVal > maxAmtValue) || (newVal < minAmtValue)){
ngModelCtrl.$setValidity('rangeValid', false);
}
else
{
ngModelCtrl.$setValidity('rangeValid', true);
}
}
}
else if((!(_.isNull($attrs.minamt) || _.isUndefined($attrs.minamt))))
{
var minAmtValue = Number($attrs.minamt)|| Number($scope.$eval($attrs.minamt));
if(newVal < minAmtValue)
{
ngModelCtrl.$setValidity('outOfMin', false);
}
else
{
ngModelCtrl.$setValidity('outOfMin', true);
}
}
else {
ngModelCtrl.$setValidity('outOfMax', true);
}
}
}
return newVal;
}
};
// This runs when the model gets updated on the scope directly and keeps our view in sync
ngModelCtrl.$render = function() {
ngModelCtrl.$setValidity('outOfMax', true);
ngModelCtrl.$setValidity(ngModelCtrl.$name+'Numeric', true);
$element.val($filter('number')(ngModelCtrl.$viewValue, fraction));
};
$element.bind('change', listener);
$element.bind('keydown', function(event) {
var key = event.keyCode;
// If the keys include the CTRL, SHIFT, ALT, or META keys, home, end, or the arrow keys, do nothing.
// This lets us support copy and paste too
if (key == 91 || (15 < key && key < 19) || (35 <= key && key <= 40))
return;
});
$element.bind('paste cut', function() {
$browser.defer(listener);
});
ngModelCtrl.$parsers.push(validator);
ngModelCtrl.$formatters.push(validator);
}
};
});
Could some one please let me know as what I am missing .
Thanking you in advance.
Have you declared your app (doesn't show this in your code)? ie:
var app = angular.module('app', []);
Do you have ng-app in your HTML document anywhere?
<html lang="en-GB" ng-app="app">
Check out the documentation to get started with modules (Angular is very well documented, well worth reading): https://docs.angularjs.org/guide/module
app.directive('numericDecimalInput', function($filter, $browser, $locale,$rootScope) {
return {
require: 'ngModel',
priority: 1,
link: function($scope, $element, $attrs, ngModelCtrl) {
...
validator($element.val());
}
};
});
I am using infinite scroll directive and this is the code:
angApp.directive('infiniteScroll', [
'$rootScope', '$window', '$timeout', function ($rootScope, $window, $timeout) {
return {
link: function (scope, elem, attrs) {
var checkWhenEnabled, handler, scrollDistance, scrollEnabled;
$window = angular.element($window);
scrollDistance = 0;
if (attrs.infiniteScrollDistance != null) {
scope.$watch(attrs.infiniteScrollDistance, function (value) {
return scrollDistance = parseInt(value, 10);
});
}
scrollEnabled = true;
checkWhenEnabled = false;
if (attrs.infiniteScrollDisabled != null) {
scope.$watch(attrs.infiniteScrollDisabled, function (value) {
scrollEnabled = !value;
if (scrollEnabled && checkWhenEnabled) {
checkWhenEnabled = false;
return handler();
}
});
}
handler = function () {
var elementBottom, remaining, shouldScroll, windowBottom;
console.log($window);
windowBottom = $window.height() + $window.scrollTop();
elementBottom = elem.offset().top + elem.height();
remaining = elementBottom - windowBottom;
shouldScroll = remaining <= $window.height() * scrollDistance;
if (shouldScroll && scrollEnabled) {
if ($rootScope.$$phase) {
return scope.$eval(attrs.infiniteScroll);
} else {
return scope.$apply(attrs.infiniteScroll);
}
} else if (shouldScroll) {
return checkWhenEnabled = true;
}
};
$window.on('scroll', handler);
scope.$on('$destroy', function () {
return $window.off('scroll', handler);
});
return $timeout((function () {
if (attrs.infiniteScrollImmediateCheck) {
if (scope.$eval(attrs.infiniteScrollImmediateCheck)) {
return handler();
}
} else {
return handler();
}
}), 0);
}
};
}
]);
The problem with this code is sometimes it works sometimes it doesn't. If I do a hard refresh by pressing Ctrl + F5 it most certainly throws the below error.
This is the error:
I am using Firefox 29.0.1. What am I missing?
Normally this should work, but there might be some problem with requireJS. Using $() instead should ensure you are using Jquery. You might need the order plug-in for requireJS since normally RequireJS loads and evaluates scripts in an undetermined order.
So I've been following this tutorial here and it shows you how to validate dates compared with each other. I'm getting an error in the first block of code which I've commented and it says "Unable to get property 'element' of undefined or null reference" which originates from this line of code customValidation.formValidator = $(event.data.source).closest('form').data('validator') does anyone know of a work around for this so I don't get an error. I'm using the latest unobtrusive validation
window.customValidation = window.customValidation ||
{
relatedControlValidationCalled: function (event) {
if (!customValidation.activeValidator) {
customValidation.formValidator = $(event.data.source).closest('form').data('validator');
}
// code error below
customValidation.formValidator.element($(event.data.target));
},
relatedControlCollection: [],
formValidator: undefined,
addDependatControlValidaitonHandler: function (element, dependentPropertyName) {
var id = $(element).attr('id');
if ($.inArray(id, customValidation.relatedControlCollection) < 0) {
customValidation.relatedControlCollection.push(id);
$(element).on(
'blur',
{ source: $(element), target: $('#' + dependentPropertyName) },
customValidation.relatedControlValidationCalled);
}
}
};
adapter:
$.validator.unobtrusive.adapters.add('comparedates', ['otherpropertyname', 'allowequality'],
function (options) {
options.rules['comparedates'] = options.params;
if (options.message) {
options.messages['comparedates'] = options.message;
}
}
);
validator method:
$.validator.addMethod('comparedates', function (value, element, params) {
var otherFieldValue = $('input[name="' + params.otherpropertyname + '"]').val();
if (otherFieldValue && value) {
var currentValue = Date.parse(value);
var otherValue = Date.parse(otherFieldValue);
if ($(element).attr('name').toLowerCase().indexOf('begin') >= 0) {
if (params.allowequality) {
if (currentValue > otherValue) {
return false;
}
} else {
if (currentValue >= otherValue) {
return false;
}
}
} else {
if (params.allowequality) {
if (currentValue < otherValue) {
return false;
}
} else {
if (currentValue <= otherValue) {
return false;
}
}
}
}
customValidation.addDependatControlValidaitonHandler(element, params.otherpropertyname);
return true;
}, '');
Maybe you are loading this code too early, before the form is in the DOM. Make sure your code is protected by $(document).ready(your code here);