4

I'm looking for a way to format an input element to format for currency. Specifically I want the commas in there for thousands.

The initial solution was to just format the value in the controller and return that to the view. But then to do my calculations I have to strip all of that out again. Then I came across $formatters and $parsers for ngModelController. Essentially it allows you to create pipeline functions. The first formats the value for the user. The second parses the value to be used in the controller. Precisely what I want.

My solution only works on page load though. Check out my fiddle.

myApp.directive('thousandsformatter', function ($filter) {
    return {
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$formatters.push(function (data) {
                var formatted = $filter('currency')(data);
                console.log(formatted);
                //convert data from model format to view format
                return formatted; //converted
            });
            ctrl.$parsers.push(function (data) {
                console.log(data);
                //convert data from view format to model format
                return data; //converted
            });
        }
    };
});

How can I get this to update as the user types?

EnigmaRM
  • 7,523
  • 11
  • 46
  • 72
  • 1
    You may want to look at angular-ui. I think they have a directive for a input "mask". http://angular-ui.github.io/ui-utils/ – Tim Withers Nov 12 '13 at 18:09
  • You want a filter, specifically the number one already does the commas and decimal place truncation so it's a good place to start. ah actually there's a currency one too http://docs.angularjs.org/api/ng.filter:currency – shaunhusain Nov 12 '13 at 18:14
  • How do you use a filter on an input element? I know how to do it within scope bindings {{ }} – EnigmaRM Nov 12 '13 at 18:16
  • @TimWithers that looks like a good option. Is there a currency mask? Demo on their site doesn't show one. – EnigmaRM Nov 12 '13 at 18:22
  • http://angular-ui.github.io/ui-utils/#/mask - Type in $9,999.99 in the "Mask Definition" and then you can type in just 1500 in the "Mask Input" – Tim Withers Nov 12 '13 at 18:25
  • @TimWithers This works, but what about when the user wants to enter 15000, or even higher. It seems to break then. – EnigmaRM Nov 12 '13 at 18:29
  • Yeah, but you could look at the code and tweak it yourself. Shouldn't be too hard once you have the base. – Tim Withers Nov 12 '13 at 18:33
  • 1
    Here is my best attempt http://jsfiddle.net/469MC/12/. I feel like the idea in general is too invasive, I wouldn't want a field to keep updating (moving commas) as I type. I think it makes sense for masks, where the field size is fix, but I think just leaving it as an `input type="number"` and displaying the formatted result in a `span` nearby would be the best idea. – Danny Nov 12 '13 at 18:45
  • @Danny I agree with you. But those that sign my check disagree. It seems like your solutions is just about spot on. Wouldn't the `$parsers.push` remove the formatting for within the controller? Within `` it'd show $1,500. But in `{{ }}` it'd be 1500. – EnigmaRM Nov 12 '13 at 18:56
  • I think `$setViewValue` sets the value shown within the controller so `{{ }}` is $1,500.00. Honestly, I have very little experience with AngularJS, but modeled my answer based on the the answer from http://stackoverflow.com/questions/15242592/angular-js-how-to-autocapitalize-an-input-field – Danny Nov 12 '13 at 19:01

1 Answers1

4

I've made an adjust in the parse function and now it's working like you want.

myApp.directive('thousandsformatter', function ($filter) {
    var precision = 2;
    return {
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$formatters.push(function (data) {
                var formatted = $filter('currency')(data);
                console.log(formatted);
                //convert data from model format to view format
                return formatted; //converted
            });
            ctrl.$parsers.push(function (data) {
                var plainNumber = data.replace(/[^\d|\-+|\+]/g, '');
                var length = plainNumber.length;
                var intValue = plainNumber.substring(0,length-precision);
                var decimalValue = plainNumber.substring(length-precision,length)
                var plainNumberWithDecimal = intValue + '.' + decimalValue;
                //convert data from view format to model format
                var formatted = $filter('currency')(plainNumberWithDecimal);
                element.val(formatted);

                return Number(plainNumberWithDecimal);
            });
        }
    };
});

Check out my solution on fiddle.

Hope it help.

Cheers