mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-18 15:50:22 +00:00
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
This commit is contained in:
parent
17251372b1
commit
31b59efa96
2 changed files with 104 additions and 46 deletions
125
src/filters.js
125
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);
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < whole.length; i++) {
|
||||
if ((whole.length - i)%3 === 0 && i !== 0) {
|
||||
formatedText += ',';
|
||||
//determine fractionSize if it is not specified
|
||||
if (isUndefined(fractionSize)) {
|
||||
fractionSize = Math.min(Math.max(pattern[1], fractionLen), pattern[2]);
|
||||
}
|
||||
formatedText += whole.charAt(i);
|
||||
}
|
||||
if (fractionSize) {
|
||||
|
||||
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 = pos; i < whole.length; i++) {
|
||||
if ((whole.length - i)%lgroup === 0 && i !== 0) {
|
||||
formatedText += NUMBER_FORMATS.GROUP_SEP;
|
||||
}
|
||||
formatedText += whole.charAt(i);
|
||||
}
|
||||
|
||||
// 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 = '';
|
||||
|
|
|
|||
|
|
@ -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 () {
|
||||
|
|
|
|||
Loading…
Reference in a new issue