skip to content

Built-In Value Formatters

The | operator runs values through DatePipe, CurrencyPipe, DecimalPipe, KeyValuePipe and others, with arguments and chaining. Interviewers check precedence, locale output and key order.

part ofAngularoverview, primer and where to startread it →
on this pageshow

explore

questions

5

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
open as a page

In Angular, in what order does KeyValuePipe return an object's entries, and how do you keep the original order instead?

level: middleimportance: should knowfreq 36%

basics

~10 s

KeyValuePipe sorts entries by key by default, strings by code unit and numbers numerically. Pass a compare function as its argument to change that, or null (since v19) to keep the input's natural order.

open as a page

In an Angular template, how is {{ discounted ? net : gross | currency }} parsed, and where does the pipe rank against ?? and ternaries?

level: middleimportance: should knowfreq 42%

basics

~20 s

It parses as discounted ? net : (gross | currency), so only gross is formatted. The pipe binds more loosely than +, &&, || and ?? but more tightly than the ternary, so wrap the conditional in parentheses.

open as a page

An Angular invoice's due date shows one day early for some users; how do DatePipe's input parsing and time-zone argument explain it, and how do you fix it?

level: seniorimportance: should knowfreq 32%

basics

~20 s

An instant such as UTC midnight is formatted in each user's local zone by default, so western users see the previous day. Send a date-only string, or pass a zone argument or DATE_PIPE_DEFAULT_OPTIONS timezone such as 'UTC'.

open as a page

In an Angular template, how does I18nPluralPipe choose the message for an invoice's item count, and what does # do in the mapping?

level: middleimportance: nice to knowfreq 22%

basics

~10 s

I18nPluralPipe looks for an exact '=n' key first, then the locale's plural category such as 'one', then 'other', and throws if none match. In the chosen message each # is replaced by the number.

open as a page