Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Money formatting directive in Angular

I need a directive for filtering a field for currency, so a user just needs to type and the decimal is implied.

Needs:

  1. Format decimal field as user types -

Start at the hundredths place as the user types. So they would type "4" and see "0.04", type "42" and see "0.42", type 298023 and see "2980.23"

  1. Field must be a number
  2. Must allow negatives -
  3. Allow 0.00 as a number input
  4. Ideally would use type="number" but "type=text" is okay
  5. You should be able to clear the field to be empty.

The ng-currency filter does not fulfill these requirements as is. Please see behaviour in plunkers to see what I mean.

My First Plunker has `input = text' and allows negative numbers. One problem is that you cannot type a negative as the very first number. When you clear the field, it returns to '0.00' but it should completely clear.

   app.directive('format', ['$filter', function ($filter) {
 return {
            require: 'ngModel', //there must be ng-model in the html
            link: function (scope, elem, attr, ctrl) {
                if (!ctrl) return;

                ctrl.$parsers.unshift(function (viewValue, modelValue) {
                    var plainNumber = viewValue.replace(/[^-+0-9]/g,'');
                    var newVal = plainNumber.charAt(plainNumber.length-1);
                    var positive = plainNumber.charAt(0) != '-';
                    if(isNaN(plainNumber.charAt(plainNumber.length-1))){
                      plainNumber = plainNumber.substr(0,plainNumber.length-1)
                    }
                    //use angular internal 'number' filter
                    plainNumber = $filter('number')(plainNumber / 100, 2).replace(/,/g, '');
                    if(positive && newVal == '-'){
                      plainNumber = '-' + plainNumber;
                    }
                    else if(!positive && newVal == '+'){
                      plainNumber = plainNumber.substr(1);
                    }
                    plainNumber.replace('.', ',');

                    //update the $viewValue
                    ctrl.$setViewValue(plainNumber);
                    //reflect on the DOM element
                    ctrl.$render();
                    //return the modified value to next parser
                    return plainNumber;
                });
            }
        };

}]);

My Second Plunker has input = text and allows for negative input. Like the first plunker, it won't allow a negative as the first character, only after numbers are typed. The second is that it starts at the tenths place instead of the hundredths. (if you type '3' you should see '0.03' but here it shows '0.3')

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; // plus sign?
                    else text = "-" + text;
                }

                var transformedInput = text.replace(pattern, '');
                transformedInput = transformedInput.replace(/([0-9]{1,2}$)/, ".$1")
                ngModelCtrl.$setViewValue(transformedInput);
                ngModelCtrl.$render();
                return transformedInput;
            }

            ngModelCtrl.$parsers.push(fromUser);
        }
    };
}]);

How can I reconcile these solutions or tailor one to meet the requirements? I want to avoid extra libraries or add-ons. I have been told that the best approach would be to study the source for the currency filter, and recreate that filter with the additional requirements.I would love to do this, but I really don't have the skills for it right now. These two directives are what I have.

like image 972
jenryb Avatar asked Jun 27 '16 20:06

jenryb


People also ask

How do you write money format?

Examples: 500 or 500,00 or 500.00 = five hundred dollars and no cents. 500,15 or 500.15 = five hundred dollars and fifteen cents. 500,150 or 500.150 or 500,150.00 or 500.150,00 = five hundred thousand, one hundred fifty dollars and no cents.

What is the use of Currencypipe in angular?

CurrencyPipelink. Transforms a number to a currency string, formatted according to locale rules that determine group sizing and separator, decimal-point character, and other locale-specific configurations.

How do you convert numbers to currency format?

On the Home tab, click the Dialog Box Launcher next to Number. Tip: You can also press Ctrl+1 to open the Format Cells dialog box. In the Format Cells dialog box, in the Category list, click Currency or Accounting. In the Symbol box, click the currency symbol that you want.


2 Answers

.:: Updated Answer - July 14 ::.


Check this simple directive:

app.directive('price', [function () {
    return {
        require: 'ngModel',
        link: function (scope, element, attrs, ngModel) {
            attrs.$set('ngTrim', "false");

            var formatter = function(str, isNum) {
                str = String( Number(str || 0) / (isNum?1:100) );
                str = (str=='0'?'0.0':str).split('.');
                str[1] = str[1] || '0';
                return str[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') + '.' + (str[1].length==1?str[1]+'0':str[1]);
            }
            var updateView = function(val) {
                scope.$applyAsync(function () {
                    ngModel.$setViewValue(val || '');
                    ngModel.$render();
                });
            }
            var parseNumber = function(val) {
                var modelString = formatter(ngModel.$modelValue, true);
                var sign = {
                    pos: /[+]/.test(val),
                    neg: /[-]/.test(val)
                }
                sign.has = sign.pos || sign.neg;
                sign.both = sign.pos && sign.neg;

                if (!val || sign.has && val.length==1 || ngModel.$modelValue && Number(val)===0) {
                    var newVal = (!val || ngModel.$modelValue && Number()===0?'':val);
                    if (ngModel.$modelValue !== newVal)
                        updateView(newVal);

                    return '';
                }
                else {
                    var valString = String(val || '');
                    var newSign = (sign.both && ngModel.$modelValue>=0 || !sign.both && sign.neg?'-':'');
                    var newVal = valString.replace(/[^0-9]/g,'');
                    var viewVal = newSign + formatter(angular.copy(newVal));

                    if (modelString !== valString)
                        updateView(viewVal);

                    return (Number(newSign + newVal) / 100) || 0;
                }
            }
            var formatNumber = function(val) {
                if (val) {
                    var str = String(val).split('.');
                    str[1] = str[1] || '0';
                    val = str[0] + '.' + (str[1].length==1?str[1]+'0':str[1]);
                }
                return parseNumber(val);
            }

            ngModel.$parsers.push(parseNumber);
            ngModel.$formatters.push(formatNumber);
        }
    };
}]);

And use it like this:

<input type="text" ng-model="number" price >

See it live in this PLUNKER (July 14)

like image 129
MeTe-30 Avatar answered Oct 18 '22 17:10

MeTe-30


i think this can full fill your requirement

https://github.com/FCSAmerica/angular-fcsa-number

you can restrict the input that allows only numbers with decimals by default angular input validations or by using char code.

like image 42
Sa E Chowdary Avatar answered Oct 18 '22 16:10

Sa E Chowdary