Angular Pipes
Read one Angular interview question at a time, then flip for the complete explanation, example, and code.
Read one Angular interview question at a time, then flip for the complete explanation, example, and code.
Angular Interview Questions · Angular Pipes Companion Article
Angular applications often receive data in a form that is correct for the application but not ideal for displaying directly to users. A date may arrive as a JavaScript Date object, a price may be stored as a number, or text may need a consistent case before it appears on the screen. Angular pipes solve this presentation problem. They transform values directly inside templates without forcing developers to write repetitive formatting logic inside component classes. For example, suppose a component contains this value:
price = 1250.5;
Instead of manually formatting it in TypeScript, Angular can format it directly in the template:
<p>{{ price | currency:'USD' }}</p>
The original price value remains unchanged. The pipe only changes how the value is displayed.
A pipe is a reusable transformation that takes a value, processes it, and returns another value for display.
The basic syntax is:
{{ value | pipeName }}
For example:
<p>{{ username | uppercase }}</p>
If username contains:
dattatray
the displayed result becomes:
DATTATRAY
The component property itself is still dattatray.
Pipes are commonly used for:
They help keep presentation-related logic out of component classes.
Without a pipe, a component may contain many methods only for formatting values. With pipes, the template remains expressive and the formatting logic can be reused.
A pipe can be understood as a simple transformation:
Original Value
|
v
Pipe
|
v
Formatted Value
For example:
2500.5
|
CurrencyPipe
|
v
$2,500.50
The important point is that pipes normally transform the displayed representation, not the original value stored by the application.
Angular uses the pipe character | between a value and a pipe.
{{ value | pipe }}
Example:
{{ name | uppercase }}
Multiple pipes can also be chained:
{{ name | lowercase | titlecase }}
Each pipe receives the output produced by the previous pipe.
Angular provides several built-in pipes for common display requirements.
Frequently used pipes include:
| Pipe | Purpose |
|---|---|
DatePipe | Formats dates and times |
CurrencyPipe | Formats currency values |
DecimalPipe | Formats decimal numbers |
PercentPipe | Formats percentage values |
UpperCasePipe | Converts text to uppercase |
LowerCasePipe | Converts text to lowercase |
TitleCasePipe | Converts text to title case |
JsonPipe | Converts values to JSON-style text |
AsyncPipe | Reads values from Promises and Observables |
These pipes remove the need to repeatedly implement common formatting operations.
DatePipe formats date and time values.
Consider:
today = new Date();
A basic template can use:
<p>{{ today | date }}</p>
Angular converts the date into a readable format.
Angular provides predefined format names.
{{ today | date:'short' }}
{{ today | date:'medium' }}
{{ today | date:'long' }}
{{ today | date:'full' }}
For date-only formatting:
{{ today | date:'shortDate' }}
{{ today | date:'mediumDate' }}
{{ today | date:'longDate' }}
{{ today | date:'fullDate' }}
For time:
{{ today | date:'shortTime' }}
{{ today | date:'mediumTime' }}
Custom patterns can also be used.
{{ today | date:'dd/MM/yyyy' }}
Possible output:
17/08/2026
Another example:
{{ today | date:'dd MMM yyyy' }}
Possible output:
17 Aug 2026
Including time:
{{ today | date:'dd MMM yyyy, hh:mm a' }}
Possible output:
17 Aug 2026, 10:30 AM
A timezone can be supplied as another pipe argument.
{{ today | date:'medium':'UTC' }}
This is useful when an application stores timestamps in one timezone but needs to present them using another timezone.
Typical examples include:
Keeping the raw timestamp in the data layer and formatting it only when displaying it is generally cleaner than storing already formatted date strings.
CurrencyPipe converts a number into a localized currency representation.
Consider:
price = 1250.5;
Template:
{{ price | currency }}
A currency code can be supplied explicitly:
{{ price | currency:'USD' }}
Possible output:
$1,250.50
Indian rupees:
{{ price | currency:'INR' }}
Possible output:
₹1,250.50
Euro:
{{ price | currency:'EUR' }}
The display option can be changed.
{{ price | currency:'USD':'code' }}
This can display a value using the currency code rather than only its symbol.
A symbol can be requested with:
{{ price | currency:'USD':'symbol' }}
The digit formatting argument can control minimum and maximum decimal places.
{{ price | currency:'USD':'symbol':'1.2-2' }}
The general pattern is:
minimumIntegerDigits.minimumFractionDigits-maximumFractionDigits
For example:
1.2-2
means:
product = {
name: 'Laptop',
price: 65999
};
<h3>{{ product.name }}</h3>
<p>Price: {{ product.price | currency:'INR' }}</p>
This is much cleaner than manually adding currency symbols and decimal formatting throughout the application.
DecimalPipe formats ordinary numeric values.
Suppose:
value = 12345.6789;
Template:
{{ value | number }}
Angular can apply locale-aware thousands separators and decimal formatting.
{{ value | number:'1.2-2' }}
The format follows:
minimumIntegerDigits.minimumFractionDigits-maximumFractionDigits
Example:
{{ 12.34567 | number:'1.2-3' }}
The result keeps at least two and at most three fraction digits.
DecimalPipe is useful for displaying:
PercentPipe converts a numeric value into percentage format.
For example:
completion = 0.75;
Template:
{{ completion | percent }}
Possible result:
75%
This is important because the value 0.75 represents 75 percent.
{{ completion | percent:'1.0-2' }}
This controls the number of decimal digits displayed.
For example:
{{ 0.7565 | percent:'1.2-2' }}
may be displayed as:
75.65%
Consider:
percentage = 75;
Then:
{{ percentage | percent }}
does not mean "display 75%". PercentPipe treats 1 as 100%, so a value of 75 represents 7500%.
When data already stores percentage points such as 75, convert it appropriately before applying PercentPipe, or display it according to the application's data model.
UpperCasePipe converts text to uppercase.
technology = 'Angular';
{{ technology | uppercase }}
Output:
ANGULAR
It is useful when a design requires uppercase presentation but the original data should remain unchanged.
LowerCasePipe converts text to lowercase.
{{ 'ANGULAR DEVELOPMENT' | lowercase }}
Output:
angular development
It can be useful when presenting normalized text such as tags, usernames, or labels.
However, formatting data using CSS may sometimes be more appropriate if the requirement is purely visual. Pipes should be used when Angular's transformed value is part of the desired template output.
TitleCasePipe converts words into title-style capitalization.
{{ 'angular web development' | titlecase }}
Output:
Angular Web Development
This can be useful for:
Example:
courseName = 'complete angular interview preparation';
<h2>{{ courseName | titlecase }}</h2>
JsonPipe converts an object into a JSON-style string.
Suppose the component contains:
user = {
id: 101,
name: 'Rahul',
role: 'Developer'
};
Template:
<pre>{{ user | json }}</pre>
This makes the object's structure easy to inspect.
Without JsonPipe, writing:
{{ user }}
does not provide a useful representation of a complex object.
Using:
{{ user | json }}
helps developers inspect:
JsonPipe is useful for inspection and debugging, but it is usually not the best choice for presenting application data to end users.
For example, instead of displaying:
<pre>{{ user | json }}</pre>
a production UI would normally display specific properties:
<p>Name: {{ user.name }}</p>
<p>Role: {{ user.role }}</p>
This gives users a clearer interface.
AsyncPipe is one of the most useful Angular pipes when working with asynchronous data.
It can work with values such as:
ObservablePromiseSuppose a component has:
userName$ = of('Amit');
The template can use:
{{ userName$ | async }}
The AsyncPipe subscribes to the Observable and displays the emitted value.
Developers can manually subscribe:
this.userName$.subscribe(value => {
this.userName = value;
});
This may also require subscription cleanup depending on how the subscription is managed.
The template can consume the Observable directly:
<p>{{ userName$ | async }}</p>
AsyncPipe handles the subscription lifecycle for the template.
When the component is destroyed, Angular cleans up the subscription managed by the pipe.
Suppose:
user$ = this.userService.getUser();
A template may use modern Angular control flow:
@if (user$ | async; as user) {
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
}
This is often preferable to applying async repeatedly to the same Observable.
Instead of:
<p>{{ (user$ | async)?.name }}</p>
<p>{{ (user$ | async)?.email }}</p>
<p>{{ (user$ | async)?.role }}</p>
capture the resolved value once:
@if (user$ | async; as user) {
<p>{{ user.name }}</p>
<p>{{ user.email }}</p>
<p>{{ user.role }}</p>
}
This also makes the template easier to read.
AsyncPipe is designed for asynchronous primitives such as Observables and Promises.
Angular signals are read directly:
{{ username() }}
They do not normally require:
{{ username | async }}
Understanding this distinction is useful when working with modern Angular applications that combine RxJS and signals.
Many pipes accept additional values called arguments or parameters.
Arguments are separated using colons.
Basic syntax:
{{ value | pipeName:argument1:argument2 }}
Example:
{{ amount | currency:'INR':'symbol' }}
Here:
amount is the input valuecurrency is the pipe'INR' is the first argument'symbol' is the second argumentAnother example:
{{ today | date:'dd/MM/yyyy':'UTC' }}
Arguments allow one pipe to support many formatting requirements.
Angular allows multiple pipes to be applied sequentially.
{{ value | pipe1 | pipe2 }}
Example:
{{ 'angular PIPE example' | lowercase | titlecase }}
Processing occurs from left to right.
First:
angular PIPE example
becomes:
angular pipe example
Then TitleCasePipe produces:
Angular Pipe Example
Pipe chaining should be used when each transformation has a clear purpose. Excessive chaining can make a template difficult to understand.
Built-in pipes solve common formatting requirements, but applications frequently need business-specific transformations.
Angular allows developers to create custom pipes.
Examples include:
A custom pipe typically uses:
@PipePipeTransformtransform() methodExample:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'reverseText'
})
export class ReverseTextPipe implements PipeTransform {
transform(value: string): string {
return value.split('').reverse().join('');
}
}
Template:
{{ 'Angular' | reverseText }}
Output:
ralugnA
The transform() method contains the transformation logic.
A custom pipe can accept arguments after the input value.
Example:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'shortText'
})
export class ShortTextPipe implements PipeTransform {
transform(value: string, limit: number = 20): string {
if (!value) {
return '';
}
return value.length > limit
? value.substring(0, limit) + '...'
: value;
}
}
Template:
{{ description | shortText:50 }}
Here:
description is the inputshortText is the pipe50 is the pipe argumentThis approach makes the pipe reusable with different limits.
In modern Angular applications, a pipe can be standalone.
Example:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'fileSize',
standalone: true
})
export class FileSizePipe implements PipeTransform {
transform(bytes: number): string {
if (bytes < 1024) {
return `${bytes} B`;
}
return `${(bytes / 1024).toFixed(2)} KB`;
}
}
A standalone component can import the pipe:
@Component({
selector: 'app-file',
standalone: true,
imports: [FileSizePipe],
template: `
<p>{{ fileSize | fileSize }}</p>
`
})
export class FileComponent {
fileSize = 4096;
}
This makes the pipe directly available to that component's template.
Angular pipes are pure by default.
A pure pipe executes when Angular detects a meaningful change to its input.
For primitive values, this includes changes such as:
10 → 20
or:
Angular → React
For objects and arrays, reference changes are especially important.
@Pipe({
name: 'customFormat',
pure: true
})
export class CustomFormatPipe implements PipeTransform {
transform(value: string): string {
return value.toUpperCase();
}
}
Because pure: true is the default, it normally does not need to be specified explicitly.
This is enough:
@Pipe({
name: 'customFormat'
})
Consider:
users = [
{ name: 'Amit' },
{ name: 'Rahul' }
];
If the same array is mutated:
this.users.push({ name: 'Neha' });
a pure pipe that receives users may not execute just because the internal contents changed, since the array reference can remain the same.
Creating a new array reference makes the change explicit:
this.users = [
...this.users,
{ name: 'Neha' }
];
This distinction is important when designing custom pipes for arrays and objects.
An impure pipe is created using:
@Pipe({
name: 'filterUsers',
pure: false
})
Angular can execute an impure pipe much more frequently during change detection.
This means an impure pipe can notice changes inside mutable objects or arrays, but that flexibility has a performance cost.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'activeUsers',
pure: false
})
export class ActiveUsersPipe implements PipeTransform {
transform(users: any[]): any[] {
return users.filter(user => user.active);
}
}
Template:
@for (user of users | activeUsers; track user.id) {
<p>{{ user.name }}</p>
}
This may work, but Angular may invoke the transformation frequently.
For a large list or expensive calculation, this can create unnecessary processing.
| Feature | Pure Pipe | Impure Pipe |
|---|---|---|
| Default Angular behavior | Yes | No |
| Configuration | pure: true | pure: false |
| Performance | Usually better | Can be expensive |
| Detects primitive input changes | Yes | Yes |
| Designed for frequent mutable-content checks | No | Can react to them |
| Recommended for normal transformations | Yes | Only when justified |
| Can execute frequently during change detection | Less often | Yes |
Pure pipes should normally be preferred.
Impure pipes should be introduced only when the behavior is genuinely required and its performance impact is understood.
A pipe may be evaluated as part of Angular's template update process.
Therefore, expensive work such as:
should not be casually placed inside a pipe.
For example, this would be a poor design for a frequently evaluated pipe:
transform(items: Product[]): Product[] {
return items
.filter(...)
.sort(...)
.map(...)
.filter(...);
}
if thousands of items are processed repeatedly.
For complex business transformations, consider handling the operation before the data reaches the template.
A pipe should ideally behave like a transformation:
Input → Transformation → Output
It should not normally:
Predictable pipes are easier to test, reuse, and maintain.
Avoid code such as:
transform(user: User): User {
user.name = user.name.toUpperCase();
return user;
}
The pipe is modifying the original object.
A safer transformation returns the required output without mutating the input:
transform(user: User): string {
return user.name.toUpperCase();
}
For object transformations, create a new object when appropriate rather than unexpectedly modifying the existing one.
Real applications often receive incomplete data.
A custom pipe should consider values such as:
null
undefined
''
Example:
transform(value: string | null | undefined): string {
if (!value) {
return 'Not Available';
}
return value.toUpperCase();
}
Defensive handling makes custom pipes safer when consuming API data.
A backend may return:
1536000
Displaying that raw byte count is not very user friendly.
A custom pipe can convert it.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'fileSize',
standalone: true
})
export class FileSizePipe implements PipeTransform {
transform(bytes: number): string {
if (!Number.isFinite(bytes) || bytes < 0) {
return 'Invalid size';
}
if (bytes < 1024) {
return `${bytes} B`;
}
if (bytes < 1024 * 1024) {
return `${(bytes / 1024).toFixed(2)} KB`;
}
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
}
}
Template:
<p>File Size: {{ attachment.size | fileSize }}</p>
This is a good use of a custom pipe because the transformation is:
Suppose a UI should display only the final four digits of an account number.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'maskAccount',
standalone: true
})
export class MaskAccountPipe implements PipeTransform {
transform(value: string): string {
if (!value || value.length <= 4) {
return value;
}
return '*'.repeat(value.length - 4) + value.slice(-4);
}
}
Template:
{{ accountNumber | maskAccount }}
Possible result:
********5678
Remember that UI masking is a presentation technique. It is not a substitute for proper security, authorization, and protection of sensitive data.
In standalone Angular components, built-in pipes can be imported when needed.
Example:
import { Component } from '@angular/core';
import { CurrencyPipe, DatePipe } from '@angular/common';
@Component({
selector: 'app-order',
standalone: true,
imports: [CurrencyPipe, DatePipe],
template: `
<p>{{ orderDate | date:'mediumDate' }}</p>
<p>{{ total | currency:'INR' }}</p>
`
})
export class OrderComponent {
orderDate = new Date();
total = 4999;
}
Import only the template features that the component actually needs.
Several Angular formatting pipes are locale-aware.
Examples include:
DatePipeCurrencyPipeDecimalPipePercentPipeThis is important for international applications because countries may use different:
For example, blindly creating strings such as:
'$' + price.toFixed(2)
does not provide the same localization support as Angular's formatting infrastructure.
Suppose a template repeatedly calls:
{{ formatCurrency(product.price) }}
A component method may execute frequently as Angular updates the view.
For reusable presentation transformations, a pure pipe can often communicate the intention more clearly:
{{ product.price | currency:'INR' }}
However, not every function should automatically become a pipe. Choose according to responsibility:
Suppose text needs to visually appear in uppercase.
Angular can do:
{{ name | uppercase }}
CSS can also do:
.name {
text-transform: uppercase;
}
These are not identical design decisions.
Use CSS when uppercase is purely visual styling.
Use a pipe when the transformed textual value is intentionally part of Angular's template output.
Choosing the correct layer keeps application responsibilities clear.
Pipes and services solve different problems.
Best suited to reusable display transformations.
1250 → ₹1,250.00
Better suited to application or business operations such as:
A pipe should not become a replacement for a service.
Impure pipes may run frequently, so expensive transformations can affect UI performance.
Changing an array internally without changing its reference can prevent a pure pipe from producing an updated result when expected.
Pipes are best for presentation transformations, not major application workflows.
Network calls should normally be handled through services rather than triggered by template transformations.
Capture an asynchronous result once when multiple properties are required.
Remember that:
0.75 → 75%
JsonPipe is excellent for debugging but normally not appropriate as a user-friendly production display.
Custom pipes should safely handle missing values when application data can be incomplete.
Not every formatting problem requires a custom pipe. Built-in pipes, CSS, component state, computed values, or plain template expressions may already solve the problem.
Follow these guidelines when designing pipes:
null and undefined where appropriate.AsyncPipe when template code directly consumes suitable Observables or Promises.A custom pipe is a good choice when all or most of the following are true:
For example, an application that repeatedly displays file sizes would benefit from:
{{ file.size | fileSize }}
instead of repeating formatting logic in every component.
Consider an order dashboard containing:
order = {
customerName: 'rahul patil',
orderDate: new Date(),
total: 15499.5,
discountRate: 0.1,
status: 'confirmed'
};
The template can use several pipes:
<h2>{{ order.customerName | titlecase }}</h2>
<p>
Order Date:
{{ order.orderDate | date:'dd MMM yyyy' }}
</p>
<p>
Total:
{{ order.total | currency:'INR' }}
</p>
<p>
Discount:
{{ order.discountRate | percent }}
</p>
<p>
Status:
{{ order.status | uppercase }}
</p>
The underlying data remains suitable for calculations and application logic, while the UI receives readable formatted output.
This separation is one of the main reasons pipes are valuable in Angular applications.
| Requirement | Example | ||
|---|---|---|---|
| Uppercase text | `{{ name \ | uppercase }}` | |
| Lowercase text | `{{ name \ | lowercase }}` | |
| Title case | `{{ title \ | titlecase }}` | |
| Format date | `{{ date \ | date:'dd/MM/yyyy' }}` | |
| Indian currency | `{{ price \ | currency:'INR' }}` | |
| US currency | `{{ price \ | currency:'USD' }}` | |
| Format decimal | `{{ value \ | number:'1.2-2' }}` | |
| Format percentage | `{{ rate \ | percent }}` | |
| Inspect an object | `{{ object \ | json }}` | |
| Read Observable/Promise | `{{ data$ \ | async }}` | |
| Pass pipe argument | `{{ value \ | pipeName:argument }}` | |
| Chain pipes | `{{ text \ | lowercase \ | titlecase }}` |
A pipe transforms a value for presentation inside an Angular template. It is commonly used for dates, currency, numbers, percentages, text formatting, and asynchronous values.
Normally, no. A pipe receives the value and returns a transformed representation. Well-designed pipes should avoid unexpectedly mutating the original data.
Angular uses the | pipe operator.
{{ value | pipeName }}
Yes.
{{ name | lowercase | titlecase }}
Pipes are processed from left to right.
Arguments are separated using colons.
{{ value | pipeName:arg1:arg2 }}
A pure pipe is Angular's normal/default pipe behavior. It is evaluated when Angular detects relevant input changes rather than being intentionally executed for mutable internal changes on every detection cycle.
An impure pipe uses:
pure: false
It can execute frequently during change detection and therefore should be used carefully.
Yes. Custom Angular pipes are pure by default unless pure: false is configured.
Pure pipes are generally preferred because they offer more predictable and efficient behavior. Impure pipes are appropriate only for specific situations.
AsyncPipe reads values from supported asynchronous sources such as Observables and Promises and updates the template when new values become available.
For subscriptions it manages, Angular cleans them up when the associated view is destroyed or when the asynchronous source changes.
No. Signals are normally read directly in templates by calling them, such as:
{{ username() }}
It is particularly useful during development for inspecting objects, API results, forms, and component state.
Yes. A pipe can participate in template expressions used in bindings where Angular's template syntax permits it.
For example:
<input [value]="username | uppercase">
Yes.
Conceptually:
{{ value | customPipe:arg1:arg2:arg3 }}
The corresponding transform() method receives those arguments after the input value.
Normally, no. HTTP communication belongs in services or another appropriate data layer. Pipes should remain predictable transformations.
Technically code can mutate objects, but doing so inside a pipe is generally poor design. A pipe should preferably return a transformed result without unexpectedly modifying its input.
push() modifies the existing array rather than creating a new array reference. Pure pipe behavior is designed around changes to its input values/references rather than deep inspection of every mutable object.
A new reference can be created with:
this.items = [...this.items, newItem];
Usually not automatically. Repeated filtering of large collections during change detection can become expensive. Consider preparing or deriving the filtered data outside an impure template pipe.
A custom pipe is useful when the transformation is reusable, presentation-focused, deterministic, and used from templates. Component-specific behavior may remain in the component.
No. Pipes can process many types of values including:
The accepted input depends on the individual pipe.
Yes. A custom pipe's transform() method can return any appropriate type. However, object and array transformations should be designed carefully with change detection and performance in mind.
Remember these core points:
| operator for pipes.:.AsyncPipe is commonly used with Observables and Promises.PipeTransform.transform().pure: false.Angular pipes are therefore more than template shortcuts. Used correctly, they provide a clean boundary between raw application data and the human-readable representation shown in the user interface.