diff options
| author | Di Peng | 2011-07-23 17:05:20 -0700 |
|---|---|---|
| committer | Igor Minar | 2011-07-26 14:16:57 -0700 |
| commit | 31b59efa961e8729a0153d7d6ea5e300d7db17f2 (patch) | |
| tree | 74088ab4040111710b7c55a1c6049801730fb654 | |
| parent | 17251372b1dedb42d27b01375125cee4f5edcce7 (diff) | |
| download | angular.js-31b59efa961e8729a0153d7d6ea5e300d7db17f2.tar.bz2 | |
feat(number/currency filter): format numbers and currency using pattern
both numbers and currency need to be formatted using a generic pattern
which can be replaced for a different pattern when angular is working in
a non en-US locale
for now only en-US locale is supported, but that will change in the
future
| -rw-r--r-- | src/filters.js | 121 | ||||
| -rw-r--r-- | test/FiltersSpec.js | 25 |
2 files changed, 102 insertions, 44 deletions
diff --git a/src/filters.js b/src/filters.js index 8fac671d..40bf2157 100644 --- a/src/filters.js +++ b/src/filters.js @@ -35,9 +35,11 @@ * @function * * @description - * Formats a number as a currency (ie $1,234.56). + * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default + * symbol for current locale is used. * * @param {number} amount Input to filter. + * @param {string=} symbol Currency symbol or identifier to be displayed. * @returns {string} Formated number. * * @css ng-format-negative @@ -47,24 +49,28 @@ <doc:example> <doc:source> <input type="text" name="amount" value="1234.56"/> <br/> - {{amount | currency}} + default currency symbol ($): {{amount | currency}}<br/> + custom currency identifier (USD$): {{amount | currency:"USD$"}} </doc:source> <doc:scenario> it('should init with 1234.56', function(){ expect(binding('amount | currency')).toBe('$1,234.56'); + expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56'); }); it('should update', function(){ input('amount').enter('-1234'); - expect(binding('amount | currency')).toBe('$-1,234.00'); + expect(binding('amount | currency')).toBe('($1,234.00)'); + expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)'); expect(element('.doc-example-live .ng-binding').attr('className')). toMatch(/ng-format-negative/); }); </doc:scenario> </doc:example> */ -angularFilter.currency = function(amount){ +angularFilter.currency = function(amount, currencySymbol){ this.$element.toggleClass('ng-format-negative', amount < 0); - return '$' + angularFilter.number.apply(this, [amount, 2]); + if (isUndefined(currencySymbol)) currencySymbol = NUMBER_FORMATS.CURRENCY_SYM; + return formatNumber(amount, 2, 1).replace(/\u00A4/g, currencySymbol); }; /** @@ -74,9 +80,9 @@ angularFilter.currency = function(amount){ * @function * * @description - * Formats a number as text. + * Formats a number as text. * - * If the input is not a number empty string is returned. + * If the input is not a number empty string is returned. * * @param {number|string} number Number to format. * @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to. @@ -92,59 +98,104 @@ angularFilter.currency = function(amount){ </doc:source> <doc:scenario> it('should format numbers', function(){ - expect(binding('val | number')).toBe('1,234.57'); + expect(binding('val | number')).toBe('1,234.568'); expect(binding('val | number:0')).toBe('1,235'); expect(binding('-val | number:4')).toBe('-1,234.5679'); }); it('should update', function(){ input('val').enter('3374.333'); - expect(binding('val | number')).toBe('3,374.33'); + expect(binding('val | number')).toBe('3,374.333'); expect(binding('val | number:0')).toBe('3,374'); expect(binding('-val | number:4')).toBe('-3,374.3330'); }); </doc:scenario> </doc:example> */ -angularFilter.number = function(number, fractionSize){ - if (isNaN(number) || !isFinite(number)) { - return ''; - } - fractionSize = isUndefined(fractionSize)? 2 : fractionSize; +// PATTERNS[0] is an array for Decimal Pattern, PATTERNS[1] is an array Currency Pattern +// Following is the order in each pattern array: +// 0: minInteger, +// 1: minFraction, +// 2: maxFraction, +// 3: positivePrefix, +// 4: positiveSuffix, +// 5: negativePrefix, +// 6: negativeSuffix, +// 7: groupSize, +// 8: lastGroupSize +var NUMBER_FORMATS = { + DECIMAL_SEP: '.', + GROUP_SEP: ',', + PATTERNS: [[1, 0, 3, '', '', '-', '', 3, 3],[1, 2, 2, '\u00A4', '', '(\u00A4', ')', 3, 3]], + CURRENCY_SYM: '$' +}; +var DECIMAL_SEP = '.'; + +angularFilter.number = function(number, fractionSize) { + if (isNaN(number) || !isFinite(number)) return ''; + return formatNumber(number, fractionSize, 0); +} + +function formatNumber(number, fractionSize, type) { var isNegative = number < 0, - pow = Math.pow(10, fractionSize), - whole = '' + number, + type = type || 0, // 0 is decimal pattern, 1 is currency pattern + pattern = NUMBER_FORMATS.PATTERNS[type]; + + number = Math.abs(number); + var numStr = number + '', formatedText = '', - i, fraction; + parts = []; - if (whole.indexOf('e') > -1) return whole; + if (numStr.indexOf('e') !== -1) { + var formatedText = numStr; + } else { + var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; - number = Math.round(number * pow) / pow; - fraction = ('' + number).split('.'); - whole = fraction[0]; - fraction = fraction[1] || ''; - if (isNegative) { - formatedText = '-'; - whole = whole.substring(1); - } + //determine fractionSize if it is not specified + if (isUndefined(fractionSize)) { + fractionSize = Math.min(Math.max(pattern[1], fractionLen), pattern[2]); + } + var pow = Math.pow(10, fractionSize); + number = Math.round(number * pow) / pow; + var fraction = ('' + number).split(DECIMAL_SEP); + var whole = fraction[0]; + fraction = fraction[1] || ''; + + var pos = 0, + lgroup = pattern[8], + group = pattern[7]; + + if (whole.length >= (lgroup + group)) { + pos = whole.length - lgroup; + for (var i = 0; i < pos; i++) { + if ((pos - i)%group === 0 && i !== 0) { + formatedText += NUMBER_FORMATS.GROUP_SEP; + } + formatedText += whole.charAt(i); + } + } - for (i = 0; i < whole.length; i++) { - if ((whole.length - i)%3 === 0 && i !== 0) { - formatedText += ','; + for (i = pos; i < whole.length; i++) { + if ((whole.length - i)%lgroup === 0 && i !== 0) { + formatedText += NUMBER_FORMATS.GROUP_SEP; + } + formatedText += whole.charAt(i); } - formatedText += whole.charAt(i); - } - if (fractionSize) { + + // format fraction part. while(fraction.length < fractionSize) { fraction += '0'; } - formatedText += '.' + fraction.substring(0, fractionSize); + if (fractionSize) formatedText += NUMBER_FORMATS.DECIMAL_SEP + fraction.substr(0, fractionSize); } - return formatedText; -}; + parts.push(isNegative ? pattern[5] : pattern[3]); + parts.push(formatedText); + parts.push(isNegative ? pattern[6] : pattern[4]); + return parts.join(''); +} function padNumber(num, digits, trim) { var neg = ''; diff --git a/test/FiltersSpec.js b/test/FiltersSpec.js index 6bcdb511..f87d0317 100644 --- a/test/FiltersSpec.js +++ b/test/FiltersSpec.js @@ -28,16 +28,16 @@ describe('filter', function() { }); describe('currency', function() { - it('should do basic filter', function() { + it('should do basic currency filtering', function() { var html = jqLite('<span/>'); var context = {$element:html}; var currency = bind(context, filter.currency); expect(currency(0)).toEqual('$0.00'); expect(html.hasClass('ng-format-negative')).toBeFalsy(); - expect(currency(-999)).toEqual('$-999.00'); + expect(currency(-999)).toEqual('($999.00)'); expect(html.hasClass('ng-format-negative')).toBeTruthy(); - expect(currency(1234.5678)).toEqual('$1,234.57'); + expect(currency(1234.5678, "USD$")).toEqual('USD$1,234.57'); expect(html.hasClass('ng-format-negative')).toBeFalsy(); }); }); @@ -46,13 +46,14 @@ describe('filter', function() { it('should do basic filter', function() { var context = {jqElement:jqLite('<span/>')}; var number = bind(context, filter.number); - expect(number(0, 0)).toEqual('0'); - expect(number(0)).toEqual('0.00'); - expect(number(-999)).toEqual('-999.00'); - expect(number(1234.5678)).toEqual('1,234.57'); + expect(number(-999)).toEqual('-999'); + expect(number(123)).toEqual('123'); + expect(number(1234567)).toEqual('1,234,567'); + expect(number(1234)).toEqual('1,234'); + expect(number(1234.5678)).toEqual('1,234.568'); expect(number(Number.NaN)).toEqual(''); - expect(number("1234.5678")).toEqual('1,234.57'); + expect(number("1234.5678")).toEqual('1,234.568'); expect(number(1/0)).toEqual(""); expect(number(1, 2)).toEqual("1.00"); expect(number(.1, 2)).toEqual("0.10"); @@ -64,11 +65,17 @@ describe('filter', function() { expect(number(.99, 2)).toEqual("0.99"); expect(number(.999, 3)).toEqual("0.999"); expect(number(.9999, 3)).toEqual("1.000"); - expect(number(1e50, 0)).toEqual("1e+50"); expect(number(1234.567, 0)).toEqual("1,235"); expect(number(1234.567, 1)).toEqual("1,234.6"); expect(number(1234.567, 2)).toEqual("1,234.57"); }); + + it('should filter exponential numbers', function() { + var context = {jqElement:jqLite('<span/>')}; + var number = bind(context, filter.number); + expect(number(1e50, 0)).toEqual('1e+50'); + expect(number(-2e50, 2)).toEqual('-2e+50'); + }); }); describe('json', function () { |
