In an Angular template, how do you format an invoice's amount, issue date and quantity with built-in pipes, including arguments and chaining?
answer
- the vertical bar in bindings
- colon-separated positional arguments
- left-to-right chaining
- digitsInfo mini-format
basics
~10 sApply the currency, date and number pipes with |, e.g. amount | currency:'EUR', issuedOn | date:'longDate', qty | number:'1.0-0'. Arguments follow colons in order, and chained pipes run left to right.
solid answer
~40 sI import `CurrencyPipe`, `DatePipe` and `DecimalPipe` from `@angular/common` into the standalone component, then write `{{ line.unitPrice | currency: invoice.currency }}`, `{{ invoice.issuedOn | date: 'longDate' }}` and `{{ line.quantity | number: '1.0-0' }}`. Arguments come after colons in the order of the `transform` parameters, so currency takes code, then display (`'symbol'` by default), then digitsInfo, then locale. digitsInfo is `min integer . min fraction - max fraction`; `number` defaults to `1.0-3`, `percent` to `1.0-0`, and `currency` uses the currency's own digits. Pipes chain left to right, as in `date: 'MMM d' | uppercase`. Without a code, currency uses `DEFAULT_CURRENCY_CODE`, `USD` by default.
code
ts · 20 linesimport { Component, input } from '@angular/core';
import { CurrencyPipe, DatePipe, DecimalPipe, PercentPipe } from '@angular/common';
@Component({
selector: 'app-invoice-line',
imports: [CurrencyPipe, DatePipe, DecimalPipe, PercentPipe],
template: `
<span>{{ quantity() | number: '1.0-0' }}</span>
<span>{{ unitPrice() | currency: currency() : 'symbol' : '1.2-2' }}</span>
<span>{{ taxRate() | percent: '1.1-1' }}</span>
<span>{{ issuedOn() | date: 'longDate' }}</span>
`,
})
export class InvoiceLine {
quantity = input.required<number>();
unitPrice = input.required<number>();
currency = input('EUR');
taxRate = input(0.2);
issuedOn = input.required<string>();
}go deeper
Recall the | syntax, colon arguments and the main pipe names: currency, date, number, percent, and that pipes chain left to right.
Explain digitsInfo and each pipe's default, the currency pipe's argument order, and why the locale, not the currency code, decides separators.
Point out where built-in formatting stops being enough, such as mixed currencies per line or server-rendered totals, and keep formatting consistent via shared tokens.
Decide which formatting lives in templates versus the API contract, so invoices, emails and exports agree on the same rules.
## What a pipe is in an Angular template A **pipe** is a named transformation you apply inside a template expression with the `|` operator. The value on the left is passed to the pipe's `transform` method, and the result is what the template renders. Angular ships its formatting pipes in `@angular/common`, and because they are standalone, a standalone component lists the ones it uses in `imports` (for example `CurrencyPipe`, `DatePipe`, `DecimalPipe`, `PercentPipe`). Pipes suit an invoice line well, because the raw data is numbers and dates while the screen needs locale-aware text. ## The invoice line, pipe by pipe ```html <tr> <td>{{ line.description | titlecase }}</td> <td>{{ line.quantity | number: '1.0-0' }}</td> <td>{{ line.unitPrice | currency: invoice.currency }}</td> <td>{{ line.taxRate | percent: '1.1-1' }}</td> <td>{{ invoice.issuedOn | date: 'longDate' }}</td> </tr> ``` - **`CurrencyPipe`** (`currency`) formats a number as money. Arguments, in order: the ISO 4217 **currency code**, the **display** (`'symbol'` by default, or `'code'`, `'symbol-narrow'`, or any literal string), the **digitsInfo**, and a **locale**. Without a code it uses the `DEFAULT_CURRENCY_CODE` token, which defaults to `USD`. - **`DecimalPipe`** (`number`) formats a plain number with grouping separators and a controlled number of decimals. - **`PercentPipe`** (`percent`) multiplies by 100 and adds the locale's percent sign, so `0.075 | percent: '1.1-1'` renders `7.5%`. - **`DatePipe`** (`date`) formats a `Date`, a number of milliseconds or an ISO string. With no format it uses `'mediumDate'`; named formats such as `'shortDate'` and `'longDate'`, or custom patterns such as `'dd MMM y'`, change the output. - **`TitleCasePipe`** (`titlecase`) upper-cases the first letter of each word and lower-cases the rest. ## Arguments: colons, in order Arguments follow the pipe name, each introduced by a **colon**, in the order of the `transform` parameters. To reach a later argument you must supply the earlier ones: ```html {{ total | currency: 'EUR' : 'code' : '1.2-2' }} ``` Arguments are ordinary template expressions, so `currency: invoice.currency` reads a field, and a signal is read by calling it: `{{ total() | currency }}`. ## digitsInfo: one mini-format for three pipes `DecimalPipe`, `PercentPipe` and `CurrencyPipe` share the **digitsInfo** string `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`: | Pipe | Default digitsInfo | Example | |---|---|---| | `number` | `1.0-3` | `3.14159 \| number` shows `3.142` | | `percent` | `1.0-0` | `0.256 \| percent` shows `26%` | | `currency` | the currency's own ISO 4217 digits | USD uses 2, some currencies use 0 | Values that need truncating are rounded to nearest, so `'1.0-0'` turns `3.6` into `4`. ## Chaining: left to right Several pipes can follow one another; Angular applies them **left to right**, each receiving the previous result: ```html {{ invoice.issuedOn | date: 'MMM d' | uppercase }} ``` Here `date` produces `Sep 25` and `uppercase` turns it into `SEP 25`. Order matters: `uppercase` before `date` would receive a `Date` instead of text, and `UpperCasePipe` rejects non-string input with an invalid pipe argument error. ## Other built-ins worth naming - **`SlicePipe`** (`slice`) returns a subset of an array or string, like `Array.prototype.slice`. - **`JsonPipe`** (`json`) runs `JSON.stringify`; it is meant for debugging output, not user-facing text. - **`KeyValuePipe`** (`keyvalue`) turns an object or `Map` into an array of `{key, value}` pairs. - **`I18nPluralPipe`** (`i18nPlural`) picks a message for a number, such as an item count. ## Things that trip people up 1. The formatting pipes (`number`, `currency`, `percent`, `date`) return `null` for `null` or `undefined` input, so the cell renders empty instead of throwing. 2. The formatting locale comes from `LOCALE_ID` (`en-US` unless configured), not from the currency code: `EUR` in an `en-US` app still uses a period as the decimal separator. 3. Pipes are for bindings that produce values; you cannot use one inside an event binding such as `(click)`.
- Why does {{ total | currency:'EUR' }} still show a period as the decimal separator in a default Angular app?The currency code only picks the currency and its symbol; separators and symbol placement come from the locale, which is `LOCALE_ID` and defaults to `en-US`. Changing the display to euros does not change the locale.
- How do you pass only the digitsInfo argument to CurrencyPipe?Arguments are positional, so you must supply the earlier ones: `{{ total | currency: 'EUR' : 'symbol' : '1.0-0' }}`. You can pass `undefined` for the code to fall back to `DEFAULT_CURRENCY_CODE`.
- What does a built-in formatting pipe render for a null amount?The number, currency, percent and date pipes return `null` for `null` or `undefined` input, so the interpolation renders nothing instead of throwing. Handle the empty case explicitly if the UI needs a placeholder.
saying these in an interview costs you the question
- Passes pipe arguments as a comma-separated list in parentheses
- Thinks chained pipes run right to left
- Believes the currency code also switches decimal separators
- Expects number to show two decimals by default
- Forgets to import the pipe into a standalone component