I wrote a custom validator you can find below, it works fine when a value is entered but when something is pre-loaded, on the edit form, it fails with ng-invalid-maxlength but ng-valid-negative.
When I remove ba-non-negative everything works fine. Can someone explain what's wrong?
Usage
<input type="text" ba-non-negative ng-model="bookCreateForm.quantity" maxlength="5"/>
Non Negative directive
'use strict';
angular.module('booksApp')
.directive('baNonNegative', function () {
return {
require: 'ngModel',
link: function (scope, elem, attr, ngModel) {
ngModel.$parsers.unshift(function (value) {
ngModel.$setValidity('negative', +value >= 0);
return +value;
});
ngModel.$formatters.unshift(function (value) {
ngModel.$setValidity('negative', +value >= 0);
return +value;
});
}
};
});
Edit -
Here's the link to jsfiddle - http://jsfiddle.net/shvz9g28/1/
I am using angular 1.3.5
In the formatters validate, if the return value is less than zero then return empty string
ngModel.$formatters.unshift(function (value) {
ngModel.$setValidity('negative', +value >= 0);
if (+value < 0) return '';
return +value;
});
HTML:
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.list_of_items = ['this', 'that', 'the other'];
$scope.bookCreateForm = {};
//$scope.bookCreateForm.quantity = -2;
});
app.directive('baNonNegative', function () {
return {
require: 'ngModel',
link: function (scope, elem, attr, ngModel) {
ngModel.$parsers.unshift(function (value) {
ngModel.$setValidity('negative', +value >= 0);
return +value;
});
ngModel.$formatters.unshift(function (value) {
ngModel.$setValidity('negative', +value >= 0);
if (+value < 0) {
ngModel.$commitViewValue();
return '';
}
return (+value) ? +value : '';
});
}
};
});
</script>
<input type="text" ba-non-negative ng-model="bookCreateForm.quantity" maxlength="5"/>
I was returning the value as number, angular has its own validator for maxlength that checks viewValue.length <= maxlength; which always returns false for numbers.
ctrl.$validators.maxlength = function(modelValue, viewValue) {
console.log(viewValue.length );
return ctrl.$isEmpty(modelValue) || viewValue.length <= maxlength;
};
Simple check
var a = 1;
a.length <= 5 //false
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 call an angular directive. That should call for each time and before all events eg:- ng-click..etc. when view value changed.and it should be the first call when view chaged.
Here is the fiddle.
angular.module('myApp', []);
angular
.module('myApp').directive('format', ['$filter', function ($filter) {
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)
});
elem.bind('change', function(event) {
var a = elem.val();
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) {
scope.$apply();
}
});
}
};
}]);
How can I call a directive for each time.
In order to do that on change of value, you can $watch on it inside your link function of directive. It watches your value and gives you callback when it changes. Like this:
scope.$watch(function() {
return elem.val()
}, function(newVal, oldVal) {
var a = newVal;
// what you do on blur, do it here
})
Here's the jsfiddle example
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 have text field in my application which should accept only positive integers only.(no decimal, no negatives). Basically I want to restrict the user to enter only between 1 to 9999 only.
<input type="text" min="0" max="99" number-mask="">
I found this from googling jsfiddle it accepts negative integers and it doesn't work with internet explorer.
I don't have mush experience writing directives. At the moment I am learning angular as well. (I use typscript to generate angular in my .net mvc project)
var app = angular.module('myApp', []);
app.directive('numberMask', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$(element).numeric();
}
}
});
In this code is it possible to check for negatives?
Something like
if(element <0 && element.lenth > 4)
.....
else
....
Thanks in Advance
angular.module('myapp', [])
.directive('numberMask', function() {
return function(scope, element, attrs) {
var min = parseInt(attrs.min, 10) || 0,
max = parseInt(attrs.max, 10) || 10,
value = element.val();
element.on('keyup', function(e) {
if (!between(element.val(), min, max)) {
element.val(value);
} else {
value = element.val();
}
});
function between(n, min, max) { return n >= min && n <= max; }
}
});
http://jsfiddle.net/9HgBY/
I modified Adrians answer to support use of ng-model. It most probably isn't the prettiest piece of code, but it gets the job done.
angular.module('myapp', [])
.directive('numberMask', function () {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, elem, attrs, ctrl) {
var oldValue = null;
scope.$watch(attrs.ngModel, function (newVal, oldVal) {
var min = parseInt(attrs.min) || 0;
var max = parseInt(attrs.max) || 10;
if (!between(newVal, min, max)) {
if (newVal > max)
ctrl.$setViewValue(max);
else if (newVal < min)
ctrl.$setViewValue(min);
else
ctrl.$setViewValue(oldValue);
ctrl.$render();
}else{
oldValue = newVal;
}
}, true);
function between(n, min, max) { return n >= min && n <= max; }
}
};
});
Here's Adrians fiddle with my additions http://jsfiddle.net/9HgBY/3/