skip to content

In an Angular template, how do you format an invoice's amount, issue date and quantity with built-in pipes, including arguments and chaining?

level: juniorimportance: must knowfreq 70%

answer

  1. the vertical bar in bindings
  2. colon-separated positional arguments
  3. left-to-right chaining
  4. digitsInfo mini-format

basics

~10 s

Apply 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 s

I 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 lines
ts
import { 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

for a junior

Recall the | syntax, colon arguments and the main pipe names: currency, date, number, percent, and that pipes chain left to right.

for a middle

Explain digitsInfo and each pipe's default, the currency pipe's argument order, and why the locale, not the currency code, decides separators.

for a senior

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.

for a principal

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