Monday, 19 September 2016

Directive to Parse and Format Number from and to Currency

This post will show an example of an AngularJS directive that will automatically format a plain number inserted into an input field into a currency format. As the number is typed into the input field, the value will be formatted into its local currency representation. The directive also converts a number value from model to its currency format in the view.
$formatters, $parsers and $filter are used as part of the codes.
myApp.directive('formatCurrency', ['$filter', '$locale', function ($filter, $locale) {
    return {
        require: '?ngModel',
        link: function (scope, elem, attrs, ctrl) {
            if (!ctrl) return;

            // $formatters is used to process value from code to view
            ctrl.$formatters.unshift(function (modelValue) {
                var formattedValue;
                if (modelValue) {
                    formattedValue = $filter('currency', null, 2)(modelValue);  // use $filter to do some formatting
                } else {
                    formattedValue = '';
                }
                return formattedValue;
            });

            // $parsers is used to process value from view to code
            ctrl.$parsers.unshift(function (viewValue) {
                var plainNumber;
                var formattedValue;
                
                var decimalSeparatorIndex = viewValue.lastIndexOf($locale.NUMBER_FORMATS.DECIMAL_SEP);  // $locale.NUMBER_FORMATS.DECIMAL_SEP variable is the decimal separator for the current culture
                if (decimalSeparatorIndex > 0) {
                    // if input has decimal part
                    var wholeNumberPart = viewValue.substring(0, decimalSeparatorIndex);
                    var decimalPart = viewValue.substr(decimalSeparatorIndex + 1, 2);
                    plainNumber = parseFloat(wholeNumberPart.replace(/[^\d]/g, '') + '.' + decimalPart).toFixed(2); // remove any non number characters and round to two decimal places

                    formattedValue = $filter('currency', null, 2)(plainNumber);
                    formattedValue = formattedValue.substring(0, formattedValue.lastIndexOf($locale.NUMBER_FORMATS.DECIMAL_SEP) + 1);
                    formattedValue = formattedValue + decimalPart;
                } else {
                    // input does not have decimal part
                    plainNumber = parseFloat(viewValue.replace(/[^\d]/g, ''));
                    formattedValue = $filter('currency', null, 0)(plainNumber);     // the 0 argument for no decimal does not work (issue with Angular)

                    if (formattedValue) {
                        // remove the decimal part
                        formattedValue = formattedValue.substring(0, formattedValue.lastIndexOf($locale.NUMBER_FORMATS.DECIMAL_SEP));
                    } else {
                        formattedValue = viewValue;
                    }
                }

                elem.val(formattedValue);
                return plainNumber;
            });
        }
    };
}]);

To use it on an input field:
<input type="text" ng-model="vm.myVariable" format-currency/>

For a working example, please see this on Plunker.

No comments: