Why AngularJS currency filter formats negative numbers with parenthesis?
Asked Answered
V

7

31

Live Demo

Why this:

# Controller
$scope.price = -10;

# View
{{ price | currency }}

results in ($10.00) rather than -$10.00?

Vassalage answered 3/7, 2013 at 6:37 Comment(1)
This is default behaviour. This may help you if you want to change groups.google.com/forum/?hl=en#!searchin/angular/goldshtein/…Assamese
C
32

This is a popular way to represent negative currencies. Wikipedia:

In bookkeeping, amounts owed are often represented by red numbers, or a number in parentheses, as an alternative notation to represent negative numbers.

You can see in the Angular source code where they do this (negSuf/negPre):

function $LocaleProvider(){
  this.$get = function() {
    return {
      id: 'en-us',

      NUMBER_FORMATS: {
        DECIMAL_SEP: '.',
        GROUP_SEP: ',',
        PATTERNS: [
          { // Decimal Pattern
            minInt: 1,
            minFrac: 0,
            maxFrac: 3,
            posPre: '',
            posSuf: '',
            negPre: '-',
            negSuf: '',
            gSize: 3,
            lgSize: 3
          },{ //Currency Pattern
            minInt: 1,
            minFrac: 2,
            maxFrac: 2,
            posPre: '\u00A4',
            posSuf: '',
            negPre: '(\u00A4',
            negSuf: ')',
            gSize: 3,
            lgSize: 3
          }
        ],
        CURRENCY_SYM: '$'
      },
Consignor answered 3/7, 2013 at 6:48 Comment(3)
is there an extensibility point available for overriding this behavior?Hughey
@DMactheDestroyer you can use a decorator, like described in my answerBitty
@marc, you're right, and thanks! This is far more elegant than a custom filter.Hughey
B
78

I know this is an old question, but the accepted answer is only answering why this happens, without a concrete solution to the problem. I think the "most correct way" of doing this, is to use a decorator like so:

angular
    .module('app')
    .config(['$provide', function($provide) {
        $provide.decorator('$locale', ['$delegate', function($delegate) {
          if($delegate.id == 'en-us') {
            $delegate.NUMBER_FORMATS.PATTERNS[1].negPre = '-\u00A4';
            $delegate.NUMBER_FORMATS.PATTERNS[1].negSuf = '';
          }
          return $delegate;
        }]);
      }]);

This is only called once, valid for any filter which depends on it, and you don't need a custom filter for your currency formatting.

Bitty answered 8/5, 2015 at 11:8 Comment(5)
Great answer, works as expected. This should be voted up! Thanks!!Reyna
when this gets minified I'm having errors 'Unknown provider: aProvider <- a <- $locale <- currencyFilter` Any ideas?Declamatory
@JonHarding it seems I missed to inject the delegation properly; according to this the decorator's $delegate can be provided with an injection-array. I'll edit the answer accordingly.Bitty
@KishoreRelangi yes, just replace negPre = '-\u00A4' with negPre = '\u00A4-'Bitty
Great solution! ThanksErfert
C
32

This is a popular way to represent negative currencies. Wikipedia:

In bookkeeping, amounts owed are often represented by red numbers, or a number in parentheses, as an alternative notation to represent negative numbers.

You can see in the Angular source code where they do this (negSuf/negPre):

function $LocaleProvider(){
  this.$get = function() {
    return {
      id: 'en-us',

      NUMBER_FORMATS: {
        DECIMAL_SEP: '.',
        GROUP_SEP: ',',
        PATTERNS: [
          { // Decimal Pattern
            minInt: 1,
            minFrac: 0,
            maxFrac: 3,
            posPre: '',
            posSuf: '',
            negPre: '-',
            negSuf: '',
            gSize: 3,
            lgSize: 3
          },{ //Currency Pattern
            minInt: 1,
            minFrac: 2,
            maxFrac: 2,
            posPre: '\u00A4',
            posSuf: '',
            negPre: '(\u00A4',
            negSuf: ')',
            gSize: 3,
            lgSize: 3
          }
        ],
        CURRENCY_SYM: '$'
      },
Consignor answered 3/7, 2013 at 6:48 Comment(3)
is there an extensibility point available for overriding this behavior?Hughey
@DMactheDestroyer you can use a decorator, like described in my answerBitty
@marc, you're right, and thanks! This is far more elegant than a custom filter.Hughey
H
18

It works better for me by checking negative number:

var app = angular.module('myApp');

app.filter('customCurrency', ["$filter", function ($filter) {       
    return function(amount, currencySymbol){
        var currency = $filter('currency');         

        if(amount < 0){
           return currency(amount, currencySymbol).replace("-", "(") + ')'
        }

        return currency(amount, currencySymbol);
    };
}]);
Hefner answered 12/3, 2014 at 10:16 Comment(0)
A
10

Do you mean display -$10.00 rather than ($10.00)?

The default, at least angularJs version 1.2.1 is to display with parentheses. Eg.: ($10.00)).

If so, this is my situation. I created a custom filter for that:

var app = angular.module('myApp');

app.filter('customCurrency', ["$filter", function ($filter) {       
  return function(amount, currencySymbol){
     var currency = $filter('currency');         

     if(amount.charAt(0) === "-"){
        return currency(amount, currencySymbol).replace("(", "-").replace(")", ""); 
     }

     return currency(amount, currencySymbol);
  };

}]);

So it delegates to the built-in currency filter and "decorates" or "un-decorates" the parentheses.

I couldn't find a way to change $LocaleProvider on the fly. If someone knows please let me know.

cheers Leonardo Correa

Attraction answered 6/2, 2014 at 3:13 Comment(4)
If you pass in numbers, this line will help: amount = amount + '';. It will convert the number to be a string (with no change made to the actual number. Otherwise charAt causes errors on numbers.Airs
@Attraction how do you use this in this example? would you change {{ product.discount | currency:undefined:0 }} to {{ product.discount | customCurrency:undefined:0 }} (I'm new to angularJS 1.5)Insomnolence
@Airs where in the function would you put amount = amount + "; ?Insomnolence
@Insomnolence I would put it on line 5 of the function above (just above var currency), this just converts the number to be a string so that charAt works.Airs
C
6

Update: Angular 1.4 no longer uses parentheses to indicate negative values but now uses the "-" symbol. Here is a link to a discussion: https://github.com/angular/angular.js/issues/12870

I used the decorator as described by marc to return the .negPre and .negSuf to use the parens.

Cabby answered 5/10, 2016 at 14:49 Comment(0)
G
2

If you don't mind keeping the parenthesis and just want a quick and easy way to achieve this
eg: -($250.00) try the following:

<ul ng-repeat="data in customers">
  <li>
    Balance: 
    <span ng-if="data.balance<0">-</span>
    <span ng-if="data.balance>0">+</span>
    {{data.balance | currency}}
  </li>
</ul>  

If you want to remove the (), then you can create your own filter or try the other answers.

Goodly answered 7/12, 2015 at 19:39 Comment(0)
V
1

Edit your angular.js file around line number -36180 change negPre and negSuf by removing - and putting parenthesis

For Example

Change From :

"NUMBER_FORMATS": {
"CURRENCY_SYM": "$",
"DECIMAL_SEP": ".",
"GROUP_SEP": ",",
"PATTERNS": [
  {
    "gSize": 3,
    "lgSize": 3,
    "maxFrac": 3,
    "minFrac": 0,
    "minInt": 1,
    "negPre": "-",
    "negSuf": "",
    "posPre": "",
    "posSuf": ""
  },
  {
    "gSize": 3,
    "lgSize": 3,
    "maxFrac": 2,
    "minFrac": 2,
    "minInt": 1,
    "negPre": "-\u00a4",
    "negSuf": "",
    "posPre": "\u00a4",
    "posSuf": ""
  }
]

}

To

"NUMBER_FORMATS": {
"CURRENCY_SYM": "$",
"DECIMAL_SEP": ".",
"GROUP_SEP": ",",
"PATTERNS": [
  {
    "gSize": 3,
    "lgSize": 3,
    "maxFrac": 3,
    "minFrac": 0,
    "minInt": 1,
    "negPre": "-",
    "negSuf": "",
    "posPre": "",
    "posSuf": ""
  },
  {
    "gSize": 3,
    "lgSize": 3,
    "maxFrac": 2,
    "minFrac": 2,
    "minInt": 1,
    "negPre": "(\u00a4",
    "negSuf": ")",
    "posPre": "\u00a4",
    "posSuf": ""
  }
]

}

Villein answered 25/10, 2018 at 17:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.