feat(common): drop use of the Intl API to improve browser support (#18284)
BREAKING CHANGE: Because of multiple bugs and browser inconsistencies, we have dropped the intl api in favor of data exported from the Unicode Common Locale Data Repository (CLDR). Unfortunately we had to change the i18n pipes (date, number, currency, percent) and there are some breaking changes. 1. I18n pipes * Breaking change: - By default Angular now only contains locale data for the language `en-US`, if you set the value of `LOCALE_ID` to another locale, you will have to import new locale data for this language because we don't use the intl API anymore. * Features: - you don't need to use the intl polyfill for Angular anymore. - all i18n pipes now have an additional last parameter `locale` which allows you to use a specific locale instead of the one defined in the token `LOCALE_ID` (whose value is `en-US` by default). - the new locale data extracted from CLDR are now available to developers as well and can be used through an API (which should be especially useful for library authors). - you can still use the old pipes for now, but their names have been changed and they are no longer included in the `CommonModule`. To use them, you will have to import the `DeprecatedI18NPipesModule` after the `CommonModule` (the order is important): ```ts import { NgModule } from '@angular/core'; import { CommonModule, DeprecatedI18NPipesModule } from '@angular/common'; @NgModule({ imports: [ CommonModule, // import deprecated module after DeprecatedI18NPipesModule ] }) export class AppModule { } ``` Dont forget that you will still need to import the intl API polyfill if you want to use those deprecated pipes. 2. Date pipe * Breaking changes: - the predefined formats (`short`, `shortTime`, `shortDate`, `medium`, ...) now use the patterns given by CLDR (like it was in AngularJS) instead of the ones from the intl API. You might notice some changes, e.g. `shortDate` will be `8/15/17` instead of `8/15/2017` for `en-US`. - the narrow version of eras is now `GGGGG` instead of `G`, the format `G` is now similar to `GG` and `GGG`. - the narrow version of months is now `MMMMM` instead of `L`, the format `L` is now the short standalone version of months. - the narrow version of the week day is now `EEEEE` instead of `E`, the format `E` is now similar to `EE` and `EEE`. - the timezone `z` will now fallback to `O` and output `GMT+1` instead of the complete zone name (e.g. `Pacific Standard Time`), this is because the quantity of data required to have all the zone names in all of the existing locales is too big. - the timezone `Z` will now output the ISO8601 basic format, e.g. `+0100`, you should now use `ZZZZ` to get `GMT+01:00`. | Field type | Format | Example value | v4 | v5 | |------------|---------------|-----------------------|----|---------------| | Eras | Narrow | A for AD | G | GGGGG | | Months | Narrow | S for September | L | MMMMM | | Week day | Narrow | M for Monday | E | EEEEE | | Timezone | Long location | Pacific Standard Time | z | Not available | | Timezone | Long GMT | GMT+01:00 | Z | ZZZZ | * Features - new predefined formats `long`, `full`, `longTime`, `fullTime`. - the format `yyy` is now supported, e.g. the year `52` will be `052` and the year `2017` will be `2017`. - standalone months are now supported with the formats `L` to `LLLLL`. - week of the year is now supported with the formats `w` and `ww`, e.g. weeks `5` and `05`. - week of the month is now supported with the format `W`, e.g. week `3`. - fractional seconds are now supported with the format `S` to `SSS`. - day periods for AM/PM now supports additional formats `aa`, `aaa`, `aaaa` and `aaaaa`. The formats `a` to `aaa` are similar, while `aaaa` is the wide version if available (e.g. `ante meridiem` for `am`), or equivalent to `a` otherwise, and `aaaaa` is the narrow version (e.g. `a` for `am`). - extra day periods are now supported with the formats `b` to `bbbbb` (and `B` to `BBBBB` for the standalone equivalents), e.g. `morning`, `noon`, `afternoon`, .... - the short non-localized timezones are now available with the format `O` to `OOOO`. The formats `O` to `OOO` will output `GMT+1` while the format `OOOO` will be `GMT+01:00`. - the ISO8601 basic time zones are now available with the formats `Z` to `ZZZZZ`. The formats `Z` to `ZZZ` will output `+0100`, while the format `ZZZZ` will be `GMT+01:00` and `ZZZZZ` will be `+01:00`. * Bug fixes - the date pipe will now work exactly the same across all browsers, which will fix a lot of bugs for safari and IE. - eras can now be used on their own without the date, e.g. the format `GG` will be `AD` instead of `8 15, 2017 AD`. 3. Currency pipe * Breaking change: - the default value for `symbolDisplay` is now `symbol` instead of `code`. This means that by default you will see `$4.99` for `en-US` instead of `USD4.99` previously. * Deprecation: - the second parameter of the currency pipe (`symbolDisplay`) is no longer a boolean, it now takes the values `code`, `symbol` or `symbol-narrow`. A boolean value is still valid for now, but it is deprecated and it will print a warning message in the console. * Features: - you can now choose between `code`, `symbol` or `symbol-narrow` which gives you access to more options for some currencies (e.g. the canadian dollar with the code `CAD` has the symbol `CA$` and the symbol-narrow `$`). 4. Percent pipe * Breaking change - if you don't specify the number of digits to round to, the local format will be used (and it usually rounds numbers to 0 digits, instead of not rounding previously), e.g. `{{ 3.141592 | percent }}` will output `314%` for the locale `en-US` instead of `314.1592%` previously. Fixes #10809, #9524, #7008, #9324, #7590, #6724, #3429, #17576, #17478, #17319, #17200, #16838, #16624, #16625, #16591, #14131, #12632, #11376, #11187 PR Close #18284
This commit is contained in:

committed by
Miško Hevery

parent
a73389bc71
commit
079d884b6c
@ -7,66 +7,118 @@
|
||||
*/
|
||||
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||
import {DateFormatter} from './intl';
|
||||
import {formatDate} from '../i18n/format_date';
|
||||
import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
import {isNumeric} from './number_pipe';
|
||||
|
||||
const ISO8601_DATE_REGEX =
|
||||
export const ISO8601_DATE_REGEX =
|
||||
/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
|
||||
// 1 2 3 4 5 6 7 8 9 10 11
|
||||
|
||||
// clang-format off
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a date according to locale rules.
|
||||
* @howToUse `date_expression | date[:format]`
|
||||
* @howToUse `date_expression | date[:format[:timezone[:locale]]]`
|
||||
* @description
|
||||
*
|
||||
* Where:
|
||||
* - `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
||||
* (https://www.w3.org/TR/NOTE-datetime).
|
||||
* - `format` indicates which date/time components to include. The format can be predefined as
|
||||
* shown below or custom as shown in the table.
|
||||
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. `Sep 3, 2010, 12:05:08 PM` for `en-US`)
|
||||
* - `'short'`: equivalent to `'yMdjm'` (e.g. `9/3/2010, 12:05 PM` for `en-US`)
|
||||
* - `'fullDate'`: equivalent to `'yMMMMEEEEd'` (e.g. `Friday, September 3, 2010` for `en-US`)
|
||||
* - `'longDate'`: equivalent to `'yMMMMd'` (e.g. `September 3, 2010` for `en-US`)
|
||||
* - `'mediumDate'`: equivalent to `'yMMMd'` (e.g. `Sep 3, 2010` for `en-US`)
|
||||
* - `'shortDate'`: equivalent to `'yMd'` (e.g. `9/3/2010` for `en-US`)
|
||||
* - `'mediumTime'`: equivalent to `'jms'` (e.g. `12:05:08 PM` for `en-US`)
|
||||
* - `'shortTime'`: equivalent to `'jm'` (e.g. `12:05 PM` for `en-US`)
|
||||
* shown below (all examples are given for `en-US`) or custom as shown in the table.
|
||||
* - `'short'`: equivalent to `'M/d/yy, h:mm a'` (e.g. `6/15/15, 9:03 AM`)
|
||||
* - `'medium'`: equivalent to `'MMM d, y, h:mm:ss a'` (e.g. `Jun 15, 2015, 9:03:01 AM`)
|
||||
* - `'long'`: equivalent to `'MMMM d, y, h:mm:ss a z'` (e.g. `June 15, 2015 at 9:03:01 AM GMT+1`)
|
||||
* - `'full'`: equivalent to `'EEEE, MMMM d, y, h:mm:ss a zzzz'` (e.g. `Monday, June 15, 2015 at
|
||||
* 9:03:01 AM GMT+01:00`)
|
||||
* - `'shortDate'`: equivalent to `'M/d/yy'` (e.g. `6/15/15`)
|
||||
* - `'mediumDate'`: equivalent to `'MMM d, y'` (e.g. `Jun 15, 2015`)
|
||||
* - `'longDate'`: equivalent to `'MMMM d, y'` (e.g. `June 15, 2015`)
|
||||
* - `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` (e.g. `Monday, June 15, 2015`)
|
||||
* - `'shortTime'`: equivalent to `'h:mm a'` (e.g. `9:03 AM`)
|
||||
* - `'mediumTime'`: equivalent to `'h:mm:ss a'` (e.g. `9:03:01 AM`)
|
||||
* - `'longTime'`: equivalent to `'h:mm:ss a z'` (e.g. `9:03:01 AM GMT+1`)
|
||||
* - `'fullTime'`: equivalent to `'h:mm:ss a zzzz'` (e.g. `9:03:01 AM GMT+01:00`)
|
||||
* - `timezone` to be used for formatting. It understands UTC/GMT and the continental US time zone
|
||||
* abbreviations, but for general use, use a time zone offset, for example,
|
||||
* `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
|
||||
* If not specified, the local system timezone of the end-user's browser will be used.
|
||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||
* default)
|
||||
*
|
||||
*
|
||||
* | Component | Symbol | Narrow | Short Form | Long Form | Numeric | 2-digit |
|
||||
* |-----------|:------:|--------|--------------|-------------------|-----------|-----------|
|
||||
* | era | G | G (A) | GGG (AD) | GGGG (Anno Domini)| - | - |
|
||||
* | year | y | - | - | - | y (2015) | yy (15) |
|
||||
* | month | M | L (S) | MMM (Sep) | MMMM (September) | M (9) | MM (09) |
|
||||
* | day | d | - | - | - | d (3) | dd (03) |
|
||||
* | weekday | E | E (S) | EEE (Sun) | EEEE (Sunday) | - | - |
|
||||
* | hour | j | - | - | - | j (1 PM) | jj (1 PM) |
|
||||
* | hour12 | h | - | - | - | h (1) | hh (01) |
|
||||
* | hour24 | H | - | - | - | H (13) | HH (13) |
|
||||
* | minute | m | - | - | - | m (5) | mm (05) |
|
||||
* | second | s | - | - | - | s (9) | ss (09) |
|
||||
* | timezone | z | - | - | z (Pacific Standard Time)| - | - |
|
||||
* | timezone | Z | - | Z (GMT-8:00) | - | - | - |
|
||||
* | timezone | a | - | a (PM) | - | - | - |
|
||||
* | Field Type | Format | Description | Example Value |
|
||||
* |--------------------|-------------|---------------------------------------------------------------|------------------------------------------------------------|
|
||||
* | Era | G, GG & GGG | Abbreviated | AD |
|
||||
* | | GGGG | Wide | Anno Domini |
|
||||
* | | GGGGG | Narrow | A |
|
||||
* | Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 |
|
||||
* | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 |
|
||||
* | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 |
|
||||
* | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 |
|
||||
* | Month | M | Numeric: 1 digit | 9, 12 |
|
||||
* | | MM | Numeric: 2 digits + zero padded | 09, 12 |
|
||||
* | | MMM | Abbreviated | Sep |
|
||||
* | | MMMM | Wide | September |
|
||||
* | | MMMMM | Narrow | S |
|
||||
* | Month standalone | L | Numeric: 1 digit | 9, 12 |
|
||||
* | | LL | Numeric: 2 digits + zero padded | 09, 12 |
|
||||
* | | LLL | Abbreviated | Sep |
|
||||
* | | LLLL | Wide | September |
|
||||
* | | LLLLL | Narrow | S |
|
||||
* | Week of year | w | Numeric: minimum digits | 1... 53 |
|
||||
* | | ww | Numeric: 2 digits + zero padded | 01... 53 |
|
||||
* | Week of month | W | Numeric: 1 digit | 1... 5 |
|
||||
* | Day of month | d | Numeric: minimum digits | 1 |
|
||||
* | | dd | Numeric: 2 digits + zero padded | 1 |
|
||||
* | Week day | E, EE & EEE | Abbreviated | Tue |
|
||||
* | | EEEE | Wide | Tuesday |
|
||||
* | | EEEEE | Narrow | T |
|
||||
* | | EEEEEE | Short | Tu |
|
||||
* | Period | a, aa & aaa | Abbreviated | am/pm or AM/PM |
|
||||
* | | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem |
|
||||
* | | aaaaa | Narrow | a/p |
|
||||
* | Period* | B, BB & BBB | Abbreviated | mid. |
|
||||
* | | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night |
|
||||
* | | BBBBB | Narrow | md |
|
||||
* | Period standalone* | b, bb & bbb | Abbreviated | mid. |
|
||||
* | | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night |
|
||||
* | | bbbbb | Narrow | md |
|
||||
* | Hour 1-12 | h | Numeric: minimum digits | 1, 12 |
|
||||
* | | hh | Numeric: 2 digits + zero padded | 01, 12 |
|
||||
* | Hour 0-23 | H | Numeric: minimum digits | 0, 23 |
|
||||
* | | HH | Numeric: 2 digits + zero padded | 00, 23 |
|
||||
* | Minute | m | Numeric: minimum digits | 8, 59 |
|
||||
* | | mm | Numeric: 2 digits + zero padded | 08, 59 |
|
||||
* | Second | s | Numeric: minimum digits | 0... 59 |
|
||||
* | | ss | Numeric: 2 digits + zero padded | 00... 59 |
|
||||
* | Fractional seconds | S | Numeric: 1 digit | 0... 9 |
|
||||
* | | SS | Numeric: 2 digits + zero padded | 00... 99 |
|
||||
* | | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 |
|
||||
* | Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 |
|
||||
* | | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 |
|
||||
* | | Z, ZZ & ZZZ | ISO8601 basic format | -0800 |
|
||||
* | | ZZZZ | Long localized GMT format | GMT-8:00 |
|
||||
* | | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 |
|
||||
* | | O, OO & OOO | Short localized GMT format | GMT-8 |
|
||||
* | | OOOO | Long localized GMT format | GMT-08:00 |
|
||||
*
|
||||
* In javascript, only the components specified will be respected (not the ordering,
|
||||
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
||||
*
|
||||
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
|
||||
*
|
||||
* When the expression is a ISO string without time (e.g. 2016-09-19) the time zone offset is not
|
||||
* applied and the formatted text will have the same day, month and year of the expression.
|
||||
*
|
||||
* WARNINGS:
|
||||
* - this pipe has only access to en-US locale data by default. If you want to localize the dates
|
||||
* in another language, you will have to import data for other locales.
|
||||
* See the {@linkDocs guide/i18n#i18n-pipes "I18n guide"} to know how to import additional locale
|
||||
* data.
|
||||
* - Fields suffixed with * are only available in the extra dataset.
|
||||
* See the {@linkDocs guide/i18n#i18n-pipes "I18n guide"} to know how to import extra locale
|
||||
* data.
|
||||
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
||||
* Instead users should treat the date as an immutable object and change the reference when the
|
||||
* pipe needs to re-run (this is to avoid reformatting the date on every change detection run
|
||||
* which would be an expensive operation).
|
||||
* - this pipe uses the Internationalization API. Therefore it is only reliable in Chrome and Opera
|
||||
* browsers.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
@ -77,41 +129,29 @@ const ISO8601_DATE_REGEX =
|
||||
* {{ dateObj | date }} // output is 'Jun 15, 2015'
|
||||
* {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM'
|
||||
* {{ dateObj | date:'shortTime' }} // output is '9:43 PM'
|
||||
* {{ dateObj | date:'mmss' }} // output is '43:11'
|
||||
* {{ dateObj | date:'hh:mm:ss a' }} // output is '09:43:11 PM'
|
||||
* ```
|
||||
*
|
||||
* {@example common/pipes/ts/date_pipe.ts region='DatePipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
// clang-format on
|
||||
@Pipe({name: 'date', pure: true})
|
||||
export class DatePipe implements PipeTransform {
|
||||
/** @internal */
|
||||
static _ALIASES: {[key: string]: string} = {
|
||||
'medium': 'yMMMdjms',
|
||||
'short': 'yMdjm',
|
||||
'fullDate': 'yMMMMEEEEd',
|
||||
'longDate': 'yMMMMd',
|
||||
'mediumDate': 'yMMMd',
|
||||
'shortDate': 'yMd',
|
||||
'mediumTime': 'jms',
|
||||
'shortTime': 'jm'
|
||||
};
|
||||
constructor(@Inject(LOCALE_ID) private locale: string) {}
|
||||
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||
|
||||
transform(value: any, pattern: string = 'mediumDate'): string|null {
|
||||
let date: Date;
|
||||
|
||||
if (isBlank(value) || value !== value) return null;
|
||||
transform(value: any, format = 'mediumDate', timezone?: string, locale?: string): string|null {
|
||||
if (value == null || value === '' || value !== value) return null;
|
||||
|
||||
if (typeof value === 'string') {
|
||||
value = value.trim();
|
||||
}
|
||||
|
||||
let date: Date;
|
||||
if (isDate(value)) {
|
||||
date = value;
|
||||
} else if (isNumeric(value)) {
|
||||
} else if (!isNaN(value - parseFloat(value))) {
|
||||
date = new Date(parseFloat(value));
|
||||
} else if (typeof value === 'string' && /^(\d{4}-\d{1,2}-\d{1,2})$/.test(value)) {
|
||||
/**
|
||||
@ -123,7 +163,7 @@ export class DatePipe implements PipeTransform {
|
||||
* is applied
|
||||
* Note: ISO months are 0 for January, 1 for February, ...
|
||||
*/
|
||||
const [y, m, d] = value.split('-').map((val: string) => parseInt(val, 10));
|
||||
const [y, m, d] = value.split('-').map((val: string) => +val);
|
||||
date = new Date(y, m - 1, d);
|
||||
} else {
|
||||
date = new Date(value);
|
||||
@ -138,19 +178,12 @@ export class DatePipe implements PipeTransform {
|
||||
}
|
||||
}
|
||||
|
||||
return DateFormatter.format(date, this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||
return formatDate(date, format, locale || this.locale, timezone);
|
||||
}
|
||||
}
|
||||
|
||||
function isBlank(obj: any): boolean {
|
||||
return obj == null || obj === '';
|
||||
}
|
||||
|
||||
function isDate(obj: any): obj is Date {
|
||||
return obj instanceof Date && !isNaN(obj.valueOf());
|
||||
}
|
||||
|
||||
function isoStringToDate(match: RegExpMatchArray): Date {
|
||||
/** @internal */
|
||||
export function isoStringToDate(match: RegExpMatchArray): Date {
|
||||
const date = new Date(0);
|
||||
let tzHour = 0;
|
||||
let tzMin = 0;
|
||||
@ -158,18 +191,18 @@ function isoStringToDate(match: RegExpMatchArray): Date {
|
||||
const timeSetter = match[8] ? date.setUTCHours : date.setHours;
|
||||
|
||||
if (match[9]) {
|
||||
tzHour = toInt(match[9] + match[10]);
|
||||
tzMin = toInt(match[9] + match[11]);
|
||||
tzHour = +(match[9] + match[10]);
|
||||
tzMin = +(match[9] + match[11]);
|
||||
}
|
||||
dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
|
||||
const h = toInt(match[4] || '0') - tzHour;
|
||||
const m = toInt(match[5] || '0') - tzMin;
|
||||
const s = toInt(match[6] || '0');
|
||||
dateSetter.call(date, +(match[1]), +(match[2]) - 1, +(match[3]));
|
||||
const h = +(match[4] || '0') - tzHour;
|
||||
const m = +(match[5] || '0') - tzMin;
|
||||
const s = +(match[6] || '0');
|
||||
const ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
|
||||
timeSetter.call(date, h, m, s, ms);
|
||||
return date;
|
||||
}
|
||||
|
||||
function toInt(str: string): number {
|
||||
return parseInt(str, 10);
|
||||
function isDate(value: any): value is Date {
|
||||
return value instanceof Date && !isNaN(value.valueOf());
|
||||
}
|
||||
|
145
packages/common/src/pipes/deprecated/date_pipe.ts
Normal file
145
packages/common/src/pipes/deprecated/date_pipe.ts
Normal file
@ -0,0 +1,145 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||
import {ISO8601_DATE_REGEX, isoStringToDate} from '../date_pipe';
|
||||
import {invalidPipeArgumentError} from '../invalid_pipe_argument_error';
|
||||
import {DateFormatter} from './intl';
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a date according to locale rules.
|
||||
* @howToUse `date_expression | date[:format]`
|
||||
* @description
|
||||
*
|
||||
* Where:
|
||||
* - `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
||||
* (https://www.w3.org/TR/NOTE-datetime).
|
||||
* - `format` indicates which date/time components to include. The format can be predefined as
|
||||
* shown below or custom as shown in the table.
|
||||
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. `Sep 3, 2010, 12:05:08 PM` for `en-US`)
|
||||
* - `'short'`: equivalent to `'yMdjm'` (e.g. `9/3/2010, 12:05 PM` for `en-US`)
|
||||
* - `'fullDate'`: equivalent to `'yMMMMEEEEd'` (e.g. `Friday, September 3, 2010` for `en-US`)
|
||||
* - `'longDate'`: equivalent to `'yMMMMd'` (e.g. `September 3, 2010` for `en-US`)
|
||||
* - `'mediumDate'`: equivalent to `'yMMMd'` (e.g. `Sep 3, 2010` for `en-US`)
|
||||
* - `'shortDate'`: equivalent to `'yMd'` (e.g. `9/3/2010` for `en-US`)
|
||||
* - `'mediumTime'`: equivalent to `'jms'` (e.g. `12:05:08 PM` for `en-US`)
|
||||
* - `'shortTime'`: equivalent to `'jm'` (e.g. `12:05 PM` for `en-US`)
|
||||
*
|
||||
*
|
||||
* | Component | Symbol | Narrow | Short Form | Long Form | Numeric | 2-digit |
|
||||
* |-----------|:------:|--------|--------------|-------------------|-----------|-----------|
|
||||
* | era | G | G (A) | GGG (AD) | GGGG (Anno Domini)| - | - |
|
||||
* | year | y | - | - | - | y (2015) | yy (15) |
|
||||
* | month | M | L (S) | MMM (Sep) | MMMM (September) | M (9) | MM (09) |
|
||||
* | day | d | - | - | - | d (3) | dd (03) |
|
||||
* | weekday | E | E (S) | EEE (Sun) | EEEE (Sunday) | - | - |
|
||||
* | hour | j | - | - | - | j (13) | jj (13) |
|
||||
* | hour12 | h | - | - | - | h (1 PM) | hh (01 PM)|
|
||||
* | hour24 | H | - | - | - | H (13) | HH (13) |
|
||||
* | minute | m | - | - | - | m (5) | mm (05) |
|
||||
* | second | s | - | - | - | s (9) | ss (09) |
|
||||
* | timezone | z | - | - | z (Pacific Standard Time)| - | - |
|
||||
* | timezone | Z | - | Z (GMT-8:00) | - | - | - |
|
||||
* | timezone | a | - | a (PM) | - | - | - |
|
||||
*
|
||||
* In javascript, only the components specified will be respected (not the ordering,
|
||||
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
||||
*
|
||||
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
|
||||
*
|
||||
* When the expression is a ISO string without time (e.g. 2016-09-19) the time zone offset is not
|
||||
* applied and the formatted text will have the same day, month and year of the expression.
|
||||
*
|
||||
* WARNINGS:
|
||||
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
||||
* Instead users should treat the date as an immutable object and change the reference when the
|
||||
* pipe needs to re-run (this is to avoid reformatting the date on every change detection run
|
||||
* which would be an expensive operation).
|
||||
* - this pipe uses the Internationalization API. Therefore it is only reliable in Chrome and Opera
|
||||
* browsers.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* Assuming `dateObj` is (year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11)
|
||||
* in the _local_ time and locale is 'en-US':
|
||||
*
|
||||
* ```
|
||||
* {{ dateObj | date }} // output is 'Jun 15, 2015'
|
||||
* {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM'
|
||||
* {{ dateObj | date:'shortTime' }} // output is '9:43 PM'
|
||||
* {{ dateObj | date:'mmss' }} // output is '43:11'
|
||||
* ```
|
||||
*
|
||||
* {@example common/pipes/ts/date_pipe.ts region='DatePipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Pipe({name: 'date', pure: true})
|
||||
export class DeprecatedDatePipe implements PipeTransform {
|
||||
/** @internal */
|
||||
static _ALIASES: {[key: string]: string} = {
|
||||
'medium': 'yMMMdjms',
|
||||
'short': 'yMdjm',
|
||||
'fullDate': 'yMMMMEEEEd',
|
||||
'longDate': 'yMMMMd',
|
||||
'mediumDate': 'yMMMd',
|
||||
'shortDate': 'yMd',
|
||||
'mediumTime': 'jms',
|
||||
'shortTime': 'jm'
|
||||
};
|
||||
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||
|
||||
transform(value: any, pattern: string = 'mediumDate'): string|null {
|
||||
if (value == null || value === '' || value !== value) return null;
|
||||
|
||||
let date: Date;
|
||||
|
||||
if (typeof value === 'string') {
|
||||
value = value.trim();
|
||||
}
|
||||
|
||||
if (isDate(value)) {
|
||||
date = value;
|
||||
} else if (!isNaN(value - parseFloat(value))) {
|
||||
date = new Date(parseFloat(value));
|
||||
} else if (typeof value === 'string' && /^(\d{4}-\d{1,2}-\d{1,2})$/.test(value)) {
|
||||
/**
|
||||
* For ISO Strings without time the day, month and year must be extracted from the ISO String
|
||||
* before Date creation to avoid time offset and errors in the new Date.
|
||||
* If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new
|
||||
* date, some browsers (e.g. IE 9) will throw an invalid Date error
|
||||
* If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the
|
||||
* timeoffset
|
||||
* is applied
|
||||
* Note: ISO months are 0 for January, 1 for February, ...
|
||||
*/
|
||||
const [y, m, d] = value.split('-').map((val: string) => parseInt(val, 10));
|
||||
date = new Date(y, m - 1, d);
|
||||
} else {
|
||||
date = new Date(value);
|
||||
}
|
||||
|
||||
if (!isDate(date)) {
|
||||
let match: RegExpMatchArray|null;
|
||||
if ((typeof value === 'string') && (match = value.match(ISO8601_DATE_REGEX))) {
|
||||
date = isoStringToDate(match);
|
||||
} else {
|
||||
throw invalidPipeArgumentError(DeprecatedDatePipe, value);
|
||||
}
|
||||
}
|
||||
|
||||
return DateFormatter.format(
|
||||
date, this._locale, DeprecatedDatePipe._ALIASES[pattern] || pattern);
|
||||
}
|
||||
}
|
||||
|
||||
function isDate(value: any): value is Date {
|
||||
return value instanceof Date && !isNaN(value.valueOf());
|
||||
}
|
27
packages/common/src/pipes/deprecated/index.ts
Normal file
27
packages/common/src/pipes/deprecated/index.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Provider} from '@angular/core';
|
||||
import {DeprecatedDatePipe} from './date_pipe';
|
||||
import {DeprecatedCurrencyPipe, DeprecatedDecimalPipe, DeprecatedPercentPipe} from './number_pipe';
|
||||
|
||||
export {
|
||||
DeprecatedCurrencyPipe,
|
||||
DeprecatedDatePipe,
|
||||
DeprecatedDecimalPipe,
|
||||
DeprecatedPercentPipe,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A collection of deprecated i18n pipes that require intl api
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export const COMMON_DEPRECATED_I18N_PIPES: Provider[] =
|
||||
[DeprecatedDecimalPipe, DeprecatedPercentPipe, DeprecatedCurrencyPipe, DeprecatedDatePipe];
|
@ -5,12 +5,7 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export enum NumberFormatStyle {
|
||||
Decimal,
|
||||
Percent,
|
||||
Currency,
|
||||
}
|
||||
import {NumberFormatStyle} from '../../i18n/locale_data_api';
|
||||
|
||||
export class NumberFormatter {
|
||||
static format(num: number, locale: string, style: NumberFormatStyle, opts: {
|
164
packages/common/src/pipes/deprecated/number_pipe.ts
Normal file
164
packages/common/src/pipes/deprecated/number_pipe.ts
Normal file
@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
||||
import {NUMBER_FORMAT_REGEXP, parseIntAutoRadix} from '../../i18n/format_number';
|
||||
import {NumberFormatStyle} from '../../i18n/locale_data_api';
|
||||
import {invalidPipeArgumentError} from '../invalid_pipe_argument_error';
|
||||
import {NumberFormatter} from './intl';
|
||||
|
||||
function formatNumber(
|
||||
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
|
||||
digits?: string | null, currency: string | null = null,
|
||||
currencyAsSymbol: boolean = false): string|null {
|
||||
if (value == null) return null;
|
||||
|
||||
// Convert strings to numbers
|
||||
value = typeof value === 'string' && !isNaN(+value - parseFloat(value)) ? +value : value;
|
||||
if (typeof value !== 'number') {
|
||||
throw invalidPipeArgumentError(pipe, value);
|
||||
}
|
||||
|
||||
let minInt: number|undefined;
|
||||
let minFraction: number|undefined;
|
||||
let maxFraction: number|undefined;
|
||||
if (style !== NumberFormatStyle.Currency) {
|
||||
// rely on Intl default for currency
|
||||
minInt = 1;
|
||||
minFraction = 0;
|
||||
maxFraction = 3;
|
||||
}
|
||||
|
||||
if (digits) {
|
||||
const parts = digits.match(NUMBER_FORMAT_REGEXP);
|
||||
if (parts === null) {
|
||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||
}
|
||||
if (parts[1] != null) { // min integer digits
|
||||
minInt = parseIntAutoRadix(parts[1]);
|
||||
}
|
||||
if (parts[3] != null) { // min fraction digits
|
||||
minFraction = parseIntAutoRadix(parts[3]);
|
||||
}
|
||||
if (parts[5] != null) { // max fraction digits
|
||||
maxFraction = parseIntAutoRadix(parts[5]);
|
||||
}
|
||||
}
|
||||
|
||||
return NumberFormatter.format(value as number, locale, style, {
|
||||
minimumIntegerDigits: minInt,
|
||||
minimumFractionDigits: minFraction,
|
||||
maximumFractionDigits: maxFraction,
|
||||
currency: currency,
|
||||
currencyAsSymbol: currencyAsSymbol,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a number according to locale rules.
|
||||
* @howToUse `number_expression | number[:digitInfo]`
|
||||
*
|
||||
* Formats a number as text. Group sizing and separator and other locale-specific
|
||||
* configurations are based on the active locale.
|
||||
*
|
||||
* where `expression` is a number:
|
||||
* - `digitInfo` is a `string` which has a following format: <br>
|
||||
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>
|
||||
* - `minIntegerDigits` is the minimum number of integer digits to use. Defaults to `1`.
|
||||
* - `minFractionDigits` is the minimum number of digits after fraction. Defaults to `0`.
|
||||
* - `maxFractionDigits` is the maximum number of digits after fraction. Defaults to `3`.
|
||||
*
|
||||
* For more information on the acceptable range for each of these numbers and other
|
||||
* details see your native internationalization library.
|
||||
*
|
||||
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||
* and may require a polyfill. See [Browser Support](guide/browser-support) for details.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example common/pipes/ts/number_pipe.ts region='NumberPipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Pipe({name: 'number'})
|
||||
export class DeprecatedDecimalPipe implements PipeTransform {
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||
|
||||
transform(value: any, digits?: string): string|null {
|
||||
return formatNumber(
|
||||
DeprecatedDecimalPipe, this._locale, value, NumberFormatStyle.Decimal, digits);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a number as a percentage according to locale rules.
|
||||
* @howToUse `number_expression | percent[:digitInfo]`
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Formats a number as percentage.
|
||||
*
|
||||
* - `digitInfo` See {@link DecimalPipe} for detailed description.
|
||||
*
|
||||
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||
* and may require a polyfill. See [Browser Support](guide/browser-support) for details.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example common/pipes/ts/number_pipe.ts region='PercentPipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Pipe({name: 'percent'})
|
||||
export class DeprecatedPercentPipe implements PipeTransform {
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||
|
||||
transform(value: any, digits?: string): string|null {
|
||||
return formatNumber(
|
||||
DeprecatedPercentPipe, this._locale, value, NumberFormatStyle.Percent, digits);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a number as currency using locale rules.
|
||||
* @howToUse `number_expression | currency[:currencyCode[:symbolDisplay[:digitInfo]]]`
|
||||
* @description
|
||||
*
|
||||
* Use `currency` to format a number as currency.
|
||||
*
|
||||
* - `currencyCode` is the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, such
|
||||
* as `USD` for the US dollar and `EUR` for the euro.
|
||||
* - `symbolDisplay` is a boolean indicating whether to use the currency symbol or code.
|
||||
* - `true`: use symbol (e.g. `$`).
|
||||
* - `false`(default): use code (e.g. `USD`).
|
||||
* - `digitInfo` See {@link DecimalPipe} for detailed description.
|
||||
*
|
||||
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||
* and may require a polyfill. See [Browser Support](guide/browser-support) for details.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example common/pipes/ts/number_pipe.ts region='CurrencyPipe'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Pipe({name: 'currency'})
|
||||
export class DeprecatedCurrencyPipe implements PipeTransform {
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||
|
||||
transform(
|
||||
value: any, currencyCode: string = 'USD', symbolDisplay: boolean = false,
|
||||
digits?: string): string|null {
|
||||
return formatNumber(
|
||||
DeprecatedCurrencyPipe, this._locale, value, NumberFormatStyle.Currency, digits,
|
||||
currencyCode, symbolDisplay);
|
||||
}
|
||||
}
|
@ -6,8 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {NgLocalization, getPluralCategory} from '../localization';
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
import {NgLocalization, getPluralCategory} from '../i18n/localization';
|
||||
|
||||
import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
const _INTERPOLATION_REGEXP: RegExp = /#/g;
|
||||
@ -15,13 +17,15 @@ const _INTERPOLATION_REGEXP: RegExp = /#/g;
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Maps a value to a string that pluralizes the value according to locale rules.
|
||||
* @howToUse `expression | i18nPlural:mapping`
|
||||
* @howToUse `expression | i18nPlural:mapping[:locale]`
|
||||
* @description
|
||||
*
|
||||
* Where:
|
||||
* - `expression` is a number.
|
||||
* - `mapping` is an object that mimics the ICU format, see
|
||||
* http://userguide.icu-project.org/formatparse/messages
|
||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||
* default)
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
@ -33,14 +37,14 @@ const _INTERPOLATION_REGEXP: RegExp = /#/g;
|
||||
export class I18nPluralPipe implements PipeTransform {
|
||||
constructor(private _localization: NgLocalization) {}
|
||||
|
||||
transform(value: number, pluralMap: {[count: string]: string}): string {
|
||||
transform(value: number, pluralMap: {[count: string]: string}, locale?: string): string {
|
||||
if (value == null) return '';
|
||||
|
||||
if (typeof pluralMap !== 'object' || pluralMap === null) {
|
||||
throw invalidPipeArgumentError(I18nPluralPipe, pluralMap);
|
||||
}
|
||||
|
||||
const key = getPluralCategory(value, Object.keys(pluralMap), this._localization);
|
||||
const key = getPluralCategory(value, Object.keys(pluralMap), this._localization, locale);
|
||||
|
||||
return pluralMap[key].replace(_INTERPOLATION_REGEXP, value.toString());
|
||||
}
|
||||
|
@ -6,63 +6,15 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
||||
import {NumberFormatStyle, NumberFormatter} from './intl';
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||
import {formatNumber} from '../i18n/format_number';
|
||||
import {NumberFormatStyle, findCurrencySymbol, getLocaleCurrencyName, getLocaleCurrencySymbol} from '../i18n/locale_data_api';
|
||||
import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
||||
|
||||
function formatNumber(
|
||||
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
|
||||
digits?: string | null, currency: string | null = null,
|
||||
currencyAsSymbol: boolean = false): string|null {
|
||||
if (value == null) return null;
|
||||
|
||||
// Convert strings to numbers
|
||||
value = typeof value === 'string' && isNumeric(value) ? +value : value;
|
||||
if (typeof value !== 'number') {
|
||||
throw invalidPipeArgumentError(pipe, value);
|
||||
}
|
||||
|
||||
let minInt: number|undefined = undefined;
|
||||
let minFraction: number|undefined = undefined;
|
||||
let maxFraction: number|undefined = undefined;
|
||||
if (style !== NumberFormatStyle.Currency) {
|
||||
// rely on Intl default for currency
|
||||
minInt = 1;
|
||||
minFraction = 0;
|
||||
maxFraction = 3;
|
||||
}
|
||||
|
||||
if (digits) {
|
||||
const parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||
if (parts === null) {
|
||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||
}
|
||||
if (parts[1] != null) { // min integer digits
|
||||
minInt = parseIntAutoRadix(parts[1]);
|
||||
}
|
||||
if (parts[3] != null) { // min fraction digits
|
||||
minFraction = parseIntAutoRadix(parts[3]);
|
||||
}
|
||||
if (parts[5] != null) { // max fraction digits
|
||||
maxFraction = parseIntAutoRadix(parts[5]);
|
||||
}
|
||||
}
|
||||
|
||||
return NumberFormatter.format(value as number, locale, style, {
|
||||
minimumIntegerDigits: minInt,
|
||||
minimumFractionDigits: minFraction,
|
||||
maximumFractionDigits: maxFraction,
|
||||
currency: currency,
|
||||
currencyAsSymbol: currencyAsSymbol,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a number according to locale rules.
|
||||
* @howToUse `number_expression | number[:digitInfo]`
|
||||
* @howToUse `number_expression | number[:digitInfo[:locale]]`
|
||||
*
|
||||
* Formats a number as text. Group sizing and separator and other locale-specific
|
||||
* configurations are based on the active locale.
|
||||
@ -73,13 +25,12 @@ function formatNumber(
|
||||
* - `minIntegerDigits` is the minimum number of integer digits to use. Defaults to `1`.
|
||||
* - `minFractionDigits` is the minimum number of digits after fraction. Defaults to `0`.
|
||||
* - `maxFractionDigits` is the maximum number of digits after fraction. Defaults to `3`.
|
||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||
* default)
|
||||
*
|
||||
* For more information on the acceptable range for each of these numbers and other
|
||||
* details see your native internationalization library.
|
||||
*
|
||||
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||
* and may require a polyfill. See [Browser Support](guide/browser-support) for details.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example common/pipes/ts/number_pipe.ts region='NumberPipe'}
|
||||
@ -90,24 +41,33 @@ function formatNumber(
|
||||
export class DecimalPipe implements PipeTransform {
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||
|
||||
transform(value: any, digits?: string): string|null {
|
||||
return formatNumber(DecimalPipe, this._locale, value, NumberFormatStyle.Decimal, digits);
|
||||
transform(value: any, digits?: string, locale?: string): string|null {
|
||||
if (isEmpty(value)) return null;
|
||||
|
||||
locale = locale || this._locale;
|
||||
|
||||
const {str, error} = formatNumber(value, locale, NumberFormatStyle.Decimal, digits);
|
||||
|
||||
if (error) {
|
||||
throw invalidPipeArgumentError(CurrencyPipe, error);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a number as a percentage according to locale rules.
|
||||
* @howToUse `number_expression | percent[:digitInfo]`
|
||||
* @howToUse `number_expression | percent[:digitInfo[:locale]]`
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Formats a number as percentage.
|
||||
*
|
||||
* - `digitInfo` See {@link DecimalPipe} for detailed description.
|
||||
*
|
||||
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||
* and may require a polyfill. See [Browser Support](guide/browser-support) for details.
|
||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||
* default)
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
@ -119,28 +79,41 @@ export class DecimalPipe implements PipeTransform {
|
||||
export class PercentPipe implements PipeTransform {
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||
|
||||
transform(value: any, digits?: string): string|null {
|
||||
return formatNumber(PercentPipe, this._locale, value, NumberFormatStyle.Percent, digits);
|
||||
transform(value: any, digits?: string, locale?: string): string|null {
|
||||
if (isEmpty(value)) return null;
|
||||
|
||||
locale = locale || this._locale;
|
||||
|
||||
const {str, error} = formatNumber(value, locale, NumberFormatStyle.Percent, digits);
|
||||
|
||||
if (error) {
|
||||
throw invalidPipeArgumentError(CurrencyPipe, error);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a number as currency using locale rules.
|
||||
* @howToUse `number_expression | currency[:currencyCode[:symbolDisplay[:digitInfo]]]`
|
||||
* @howToUse `number_expression | currency[:currencyCode[:display[:digitInfo[:locale]]]]`
|
||||
* @description
|
||||
*
|
||||
* Use `currency` to format a number as currency.
|
||||
*
|
||||
* - `currencyCode` is the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, such
|
||||
* as `USD` for the US dollar and `EUR` for the euro.
|
||||
* - `symbolDisplay` is a boolean indicating whether to use the currency symbol or code.
|
||||
* - `true`: use symbol (e.g. `$`).
|
||||
* - `false`(default): use code (e.g. `USD`).
|
||||
* - `display` indicates whether to show the currency symbol or the code.
|
||||
* - `code`(default): use code (e.g. `USD`).
|
||||
* - `symbol`: use symbol (e.g. `$`).
|
||||
* - `symbol-narrow`: some countries have two symbols for their currency, one regular and one
|
||||
* - boolean (deprecated from v5): `true` for symbol and false for `code`
|
||||
* narrow (e.g. the canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`).
|
||||
* If there is no narrow symbol for the chosen currency, the regular symbol will be used.
|
||||
* - `digitInfo` See {@link DecimalPipe} for detailed description.
|
||||
*
|
||||
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||
* and may require a polyfill. See [Browser Support](guide/browser-support) for details.
|
||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||
* default)
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
@ -153,22 +126,36 @@ export class CurrencyPipe implements PipeTransform {
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||
|
||||
transform(
|
||||
value: any, currencyCode: string = 'USD', symbolDisplay: boolean = false,
|
||||
digits?: string): string|null {
|
||||
return formatNumber(
|
||||
CurrencyPipe, this._locale, value, NumberFormatStyle.Currency, digits, currencyCode,
|
||||
symbolDisplay);
|
||||
value: any, currencyCode?: string,
|
||||
display: 'code'|'symbol'|'symbol-narrow'|boolean = 'symbol', digits?: string,
|
||||
locale?: string): string|null {
|
||||
if (isEmpty(value)) return null;
|
||||
|
||||
locale = locale || this._locale;
|
||||
|
||||
if (typeof display === 'boolean') {
|
||||
if (<any>console && <any>console.warn) {
|
||||
console.warn(
|
||||
`Warning: the currency pipe has been changed in Angular v5. The symbolDisplay option (third parameter) is now a string instead of a boolean. The accepted values are "code", "symbol" or "symbol-narrow".`);
|
||||
}
|
||||
display = display ? 'symbol' : 'code';
|
||||
}
|
||||
|
||||
let currency = currencyCode || 'USD';
|
||||
if (display !== 'code') {
|
||||
currency = findCurrencySymbol(currency, display === 'symbol' ? 'wide' : 'narrow');
|
||||
}
|
||||
|
||||
const {str, error} = formatNumber(value, locale, NumberFormatStyle.Currency, digits, currency);
|
||||
|
||||
if (error) {
|
||||
throw invalidPipeArgumentError(CurrencyPipe, error);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
function parseIntAutoRadix(text: string): number {
|
||||
const result: number = parseInt(text);
|
||||
if (isNaN(result)) {
|
||||
throw new Error('Invalid integer literal when parsing ' + text);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isNumeric(value: any): boolean {
|
||||
return !isNaN(value - parseFloat(value));
|
||||
function isEmpty(value: any): boolean {
|
||||
return value == null || value === '' || value !== value;
|
||||
}
|
||||
|
Reference in New Issue
Block a user