I'm having an issue formatting an input field, while leaving the underlying scope variable non-formatted.
What I want to achieve is a text field to display currency. It should format itself on the fly, while handling wrong input. I got that working, but my problem is that I want to store the non-formatted value in my scope variable. The issue with input is that it requires a model which goes both ways, so changing the input field updates the model, and the other way around.
I came upon $parsers
and $formatters
which appears to be what I am looking for. Unfortunately they are not affecting each other (which might actually be good to avoid endless loops).
I've created a simple jsFiddle: http://jsfiddle.net/cruckie/yE8Yj/ and the code is as follows:
HTML:
<div data-ng-app="app" data-ng-controller="Ctrl">
<input type="text" data-currency="" data-ng-model="data" />
<div>Model: {{data}}</div>
</div>
JS:
var app = angular.module("app", []);
function Ctrl($scope) {
$scope.data = 1234567;
}
app.directive('currency', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attr, ctrl) {
ctrl.$formatters.push(function(modelValue) {
return modelValue.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ',');
});
ctrl.$parsers.push(function(viewValue) {
return parseFloat(viewValue.replace(new RegExp(",", "g"), ''));
});
}
};
});
Again, this is just a simple example. When it loads everything looks as it's supposed to. The input field is formatted and the variable is not. However, when changing the value in the input field it no longer formats itself - the variable however gets updated correctly.
Is there a way to ensure the text field being formatted while the variable is not? I guess what I am looking for is a filter for text fields, but I can't seen to find anything on that.
Best regards
The fiddle is using an old version of angular(1.0.7).
On updating to a recent version, 1.2.6, the $render function of the ngModelCtrl is never called, meaning if the model value is changed in the controller,
the number is never formatted as required in the view.
Here is the updated fiddle http://jsfiddle.net/KPeBD/78/
I have refactored the original directive, so that it uses $parses and $formatters instead of listening to keyboard events. There is also no need to use $browser.defer
See working demo here http://jsfiddle.net/davidvotrubec/ebuqo6Lm/
Github code is here [https://github.com/ST-Software/STAngular/blob/master/src/directives/SgNumberInput]
Indeed the
$parsers
and$formatters
are "independent" as you say (probably for loops, again as you say). In our application we explicitly format with theonchange
event (inside thelink
function), roughly as:See your updated fiddle for the detailed and working example: http://jsfiddle.net/yE8Yj/1/
I like binding to the
onchange
event, because I find it annoying to change the input as the user is typing.I've revised a little what Wade Tandy had done, and added support for several features:
set validity to false when input is not numeric, this is done in the parser:
You can see my revised version here - http://jsfiddle.net/KPeBD/64/
Based on the answer from Wade Tandy here is a new jsfiddle with following improvements:
I have also replaces all
String.replace(regex)
bysplit().join()
, because this allows me to use variables in the expression.http://jsfiddle.net/KPeBD/283/
Here's a fiddle that shows how I implemented the exact same behavior in my application. I ended up using
ngModelController#render
instead of$formatters
, and then adding a separate set of behavior that triggered onkeydown
andchange
events.http://jsfiddle.net/KPeBD/2/