Reputation: 2117
I found this directive for filtering a field for currency, so a user just needs to type and the decimal is implied.
It works well, except that a user is able to put in a letter if they press a letter twice. I don't want them to be able to put in a letter at all.
I tried changing the type="number"
but then it would only allow 5 digits before disappearing inexplicably. You can see this at this plunker:
http://plnkr.co/edit/Y1olj7Cdz7XKqRmE7BFb?p=preview
So either I need to change something (the regex?) in this line:
var plainNumber = viewValue.replace(/[^\d|\-+]/g, '');
or somehow fix the type="number" to keep input past five digits. Anybody know what to do here?
Upvotes: 1
Views: 2652
Reputation: 6173
I'm unfamiliar with AngularJS, but I can tell your regex is not exactly what you intended.
You have this: [^\d|\-+]
This is a negated char class. All non-digits, non +
s, non -
s, AND non |
s will match. Also note that \d
contains more than [0-9]
, because it will likely match Persian digits and such.
I would use: [^-+0-9]
. By putting the -
directly after the ^
it does not need to be escaped.
The problem with your solution is you're waiting for the characters to be typed before you remove them. The keystrokes may happen faster than your field can purge them.
Despite having no experience whatsoever in Angular or jQuery,
It allows 0-9 to be typed freely. When a +
or -
is typed, it is never added to the end. Instead, it appears at the beginning, overwriting any existing sign. Maybe you should just run the snippet and see yourself:
var app = angular.module('App', []);
app.controller('MainCtrl', function($scope) {});
app.directive('inputRestrictor', [function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ngModelCtrl) {
var pattern = /[^+0-9-]/g;
function fromUser(text) {
if (!text)
return text;
var rep = /[+]/g;
var rem = /[-]/g;
rep.exec(text);
rem.exec(text);
var indexp = rep.lastIndex;
var indexm = rem.lastIndex;
text = text.replace(/[+-]/g, '');
if (indexp > 0 || indexm > 0) {
if (indexp > indexm) text = "+" + text;
else text = "-" + text;
}
var transformedInput = text.replace(pattern, '');
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
return transformedInput;
}
ngModelCtrl.$parsers.push(fromUser);
}
};
}]);
<!DOCTYPE html>
<html>
<head>
<script data-require="[email protected]" data-semver="1.3.0-rc.2" src=" https://code.angularjs.org/1.3.0-rc.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="App">
<div ng-controller="MainCtrl">
<input type="text" ng-model="inputValue" input-restrictor>
<input type="submit" ng-click="getdata()" />
</div>
</body>
</html>
Upvotes: 1
Reputation: 513
What I got from your issue are :
When binding the input value with ng-model
, actually there are two main values behind the scenes internally: $viewValue
and $modelValue
. And a simple relationship between them is like this:
input DOM value -> $viewValue -> $modelValue
The $parsers
only work between $viewValue
and $modelValue
. So the problem is when you insert a new parser
into the $parsers
, the modified value will only affect the $modleValue
as you simply return a new value in the parse function.
$parsers
are :
Array of functions to execute, as a pipeline, whenever the control reads value from the DOM. The functions are called in array order, each passing its return value through to the next. The last return value is forwarded to the $validators collection
more on : https://docs.angularjs.org/api/ng/type/ngModel.NgModelController#$parsers
And the returned value in the parser function WON'T affect the $viewValue
so what you need to do is to also change that value and reflect on the input DOM element.
Code example:
app.directive('format', ['$filter', function ($filter) {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$parsers.unshift(function (viewValue) {
var plainNumber = viewValue.replace(/[^\d|\-+]/g, '');
//use angular internal 'number' filter
plainNumber = $filter('number')(plainNumber/100,2);
//update the $viewValue
ctrl.$setViewValue(plainNumber);
//reflect on the DOM element
ctrl.$render();
//return the modified value to next parser
return plainNumber;
});
}
};
}]);
A working plnkr is here: http://plnkr.co/edit/aoBWhJhlJLiEhBm5hRNX?p=preview
At last, the reason why when you type 6th digit and it disappear is:
As you type 6 digits, the browser will convert and format the number you type to this kind of format: '1,234.56' (pay attention to the COMMA ',', the separator may defer on different browsers) , and you will see a warning given by angular in the console :
The specified value "1,234.56" is not a valid number. The value must match to the following regular expression: -?(\d+|\d+.\d+|.\d+)([eE][-+]?\d+)?
which is caused by the restriction from the type='number'
on the input element, angular will perform extra internal validation to match the rule defined by angular itself.
Upvotes: 1