2016-02-06 02:27:06 -05:00
include ../_util-fns
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
Every application starts out with what seems like a simple task: get data, transform them, and show them to users.
2015-11-10 13:31:46 -05:00
Getting data could be as simple as creating a local variable or as complex as streaming data over a Websocket.
Once data arrive, we could push their raw `toString` values directly to screen.
That rarely makes for a good user experience.
Almost everyone prefers a simple birthday date
(<span style="font-family:courier">April 15, 1988</span>) to the original raw string format
( <span style="font-family:courier">Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)</span> ).
2015-10-19 12:56:24 -04:00
Clearly some values benefit from a bit of massage. We soon discover that we
desire many of the same transformations repeatedly, both within and across many applications.
2015-11-10 13:31:46 -05:00
We almost think of them as styles.
2015-10-19 12:56:24 -04:00
In fact, we'd like to apply them in our HTML templates as we do styles.
2015-11-10 13:31:46 -05:00
Welcome, Angular pipes, the simple display-value transformations that we can declare in our HTML!
2016-04-06 14:15:07 -04:00
2015-12-13 01:01:46 -05:00
[Live Example](/resources/live-examples/pipes/ts/plnkr.html).
2015-10-19 12:56:24 -04:00
.l-main-section
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
## Using Pipes
2015-11-10 13:31:46 -05:00
A pipe takes in data as input and transforms it to a desired output.
We'll illustrate by transforming a component's birthday property into
2015-12-13 01:01:46 -05:00
a human-friendly date:
2016-01-13 17:00:43 -05:00
+makeExample('pipes/ts/app/hero-birthday1.component.ts', null, 'app/hero-birthday1.component.ts')(format='.')
2015-11-02 03:28:38 -05:00
2015-11-10 13:31:46 -05:00
:marked
2016-04-06 14:15:07 -04:00
Focus on the component's template.
2015-12-13 01:01:46 -05:00
+makeExample('pipes/ts/app/app.component.html', 'hero-birthday-template')(format=".")
2015-11-10 13:31:46 -05:00
:marked
2015-12-13 01:01:46 -05:00
Inside the interpolation expression we flow the component's `birthday` value through the
2016-04-06 14:15:07 -04:00
[pipe operator](./template-syntax.html#pipe) ( | ) to the [Date pipe](../api/common/DatePipe-class.html)
2015-12-13 01:01:46 -05:00
function on the right. All pipes work this way.
2015-10-19 12:56:24 -04:00
.l-main-section
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
## Built-in pipes
2015-11-10 13:31:46 -05:00
Angular comes with a stock set of pipes such as
2015-10-19 12:56:24 -04:00
`DatePipe`, `UpperCasePipe`, `LowerCasePipe`, `CurrencyPipe`, and `PercentPipe`.
They are all immediately available for use in any template.
.l-sub-section
2015-11-10 13:31:46 -05:00
:marked
2016-01-13 17:00:43 -05:00
Learn more about these and many other built-in pipes in the the [API Reference](../api/#!?apiFilter=pipe);
2015-11-10 13:31:46 -05:00
filter for entries that include the word "pipe".
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
Angular 2 doesn't have a `FilterPipe` or an `OrderByPipe` for reasons explained in an [appendix below](#no-filter-pipe)
2015-10-19 12:56:24 -04:00
.l-main-section
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
## Parameterizing a Pipe
A pipe may accept any number of optional parameters to fine-tune its output.
2015-11-10 13:31:46 -05:00
2015-10-19 12:56:24 -04:00
We add parameters to a pipe by following the pipe name with a colon ( : ) and then the parameter value
2015-10-23 12:31:51 -04:00
(e.g., `currency:'EUR'`). If our pipe accepts multiple parameters, we separate the values with colons (e.g. `slice:1:5`)
2015-11-10 13:31:46 -05:00
2015-12-13 01:01:46 -05:00
We'll modify our birthday template to give the date pipe a format parameter.
After formatting the hero's April 15th birthday should display as **<span style="font-family:courier">04/15/88</span>**.
2015-10-19 12:56:24 -04:00
2015-12-13 01:01:46 -05:00
+makeExample('pipes/ts/app/app.component.html', 'format-birthday')(format=".")
2015-10-19 12:56:24 -04:00
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
The parameter value can be any valid
2016-02-12 09:54:42 -05:00
[template expression](./template-syntax.html#template-expressions)
2016-04-06 14:15:07 -04:00
such as a string literal or a component property.
2015-12-13 01:01:46 -05:00
In other words, we can control the format through a binding the same way we control the birthday value through a binding.
2016-04-06 14:15:07 -04:00
2015-12-13 01:01:46 -05:00
Let's write a second component that *binds* the pipe's format parameter
to the component's `format` property. Here's the template for that component:
+makeExample('pipes/ts/app/hero-birthday2.component.ts', 'template', 'app/hero-birthday2.component.ts (template)')(format=".")
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
We also added a button to the template and bound its click event to the component's `toggleFormat` method.
That method toggles the component's `format` property between a short form
('shortDate') and a longer form ('fullDate').
2016-01-13 17:00:43 -05:00
+makeExample('pipes/ts/app/hero-birthday2.component.ts', 'class', 'app/hero-birthday2.component.ts (class)')(format='.')
2015-12-13 01:01:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
As we click the button, the displayed date alternates between
2015-11-02 03:28:38 -05:00
"**<span style="font-family:courier">04/15/1988</span>**" and
2015-10-19 12:56:24 -04:00
"**<span style="font-family:courier">Friday, April 15, 1988</span>**".
2015-11-02 03:28:38 -05:00
figure.image-display
img(src='/resources/images/devguide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle")
2015-11-10 13:31:46 -05:00
:marked
2015-12-13 01:01:46 -05:00
2015-10-19 12:56:24 -04:00
.l-sub-section
2015-11-10 13:31:46 -05:00
:marked
2016-03-11 02:24:28 -05:00
Learn more about the `DatePipes` format options in the [API Docs](../api/common/DatePipe-class.html).
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
## Chaining pipes
2015-11-10 13:31:46 -05:00
We can chain pipes together in potentially useful combinations.
2015-10-19 12:56:24 -04:00
In the following example, we chain the birthday to the `DatePipe` and on to the `UpperCasePipe`
2015-11-10 13:31:46 -05:00
so we can display the birthday in uppercase. The following birthday displays as
2015-10-19 12:56:24 -04:00
**<span style="font-family:courier">APR 15, 1988</span>**
2015-11-02 03:28:38 -05:00
2015-12-13 01:01:46 -05:00
+makeExample('pipes/ts/app/app.component.html', 'chained-birthday')(format=".")
2015-11-02 03:28:38 -05:00
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
If we pass a parameter to a filter, we have to add parentheses
to help the template compiler with the evaluation order.
The following example displays
2015-11-10 13:31:46 -05:00
**<span style="font-family:courier">FRIDAY, APRIL 15, 1988</span>**
2015-11-02 03:28:38 -05:00
2015-12-13 01:01:46 -05:00
+makeExample('pipes/ts/app/app.component.html', 'chained-parameter-birthday')(format=".")
2015-10-19 12:56:24 -04:00
2015-12-13 01:01:46 -05:00
:marked
2016-04-06 14:15:07 -04:00
We can add parentheses to alter the evaluation order or
2016-01-13 17:00:43 -05:00
to provide extra clarity:
+makeExample('pipes/ts/app/app.component.html', 'chained-parameter-birthday-parens')(format=".")
2015-10-19 12:56:24 -04:00
.l-main-section
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
## Custom Pipes
2015-11-02 03:28:38 -05:00
We can write our own custom pipes.
2015-11-10 13:31:46 -05:00
2015-12-13 01:01:46 -05:00
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
2015-10-19 12:56:24 -04:00
2016-01-13 17:00:43 -05:00
+makeExample('pipes/ts/app/exponential-strength.pipe.ts', null, 'app/exponential-strength.pipe.ts')(format=".")
2015-11-10 13:31:46 -05:00
:marked
2015-11-21 21:05:50 -05:00
This pipe definition reveals several key points
2016-04-06 14:15:07 -04:00
2015-12-13 01:01:46 -05:00
* A pipe is a class decorated with pipe metadata.
2016-04-06 14:15:07 -04:00
2015-12-28 14:52:41 -05:00
* The pipe class implements the `PipeTransform` interface's `transform` method that
2016-01-13 17:00:43 -05:00
accepts an input value and an optional array of parameters and returns the transformed value.
2015-12-13 01:01:46 -05:00
* There will be one item in the parameter array for each parameter passed to the pipe
2016-04-06 14:15:07 -04:00
2015-12-13 01:01:46 -05:00
* We tell Angular that this is a pipe by applying the
`@Pipe` decorator which we import from the core Angular library.
2016-04-06 14:15:07 -04:00
2015-10-19 12:56:24 -04:00
* The `@Pipe` decorator takes an object with a name property whose value is the
2015-11-17 13:17:14 -05:00
pipe name that we'll use within a template expression. It must be a valid JavaScript identifier.
2016-01-15 15:30:00 -05:00
Our pipe's name is `exponentialStrength`.
2015-12-13 01:01:46 -05:00
2015-12-28 14:52:41 -05:00
.l-sub-section
:marked
### The *PipeTransform* Interface
2015-12-13 01:01:46 -05:00
2016-04-06 14:15:07 -04:00
The `transform` method is essential to a pipe.
2016-01-13 17:00:43 -05:00
The `PipeTransform` *interface* defines that method and guides both tooling and the compiler.
It is technically optional; Angular looks for and executes the `transform` method regardless.
2016-04-06 14:15:07 -04:00
2015-12-28 14:52:41 -05:00
:marked
2015-12-13 01:01:46 -05:00
Now we need a component to demonstrate our pipe.
2016-01-13 17:00:43 -05:00
+makeExample('pipes/ts/app/power-booster.component.ts',null,'app/power-booster.component.ts')(format='.')
2015-11-02 03:28:38 -05:00
figure.image-display
img(src='/resources/images/devguide/pipes/power-booster.png' alt="Power Booster")
2015-10-17 13:01:41 -04:00
2015-11-10 13:31:46 -05:00
:marked
2015-10-23 12:31:51 -04:00
Two things to note:
2015-12-13 01:01:46 -05:00
1. We use our custom pipe the same way we use the built-in pipes.
2015-11-10 13:31:46 -05:00
2016-01-13 17:00:43 -05:00
1. We must list our pipe in the `pipes` array of the `@Component` decorator.
2015-11-10 13:31:46 -05:00
2015-12-13 01:01:46 -05:00
.callout.is-helpful
2015-10-19 12:56:24 -04:00
header Remember the pipes array!
2015-11-10 13:31:46 -05:00
:marked
Angular reports an error if we neglect to list our custom pipe.
2015-10-19 12:56:24 -04:00
We didn't list the `DatePipe` in our previous example because all
2015-11-10 13:31:46 -05:00
Angular built-in pipes are pre-registered.
2015-10-19 12:56:24 -04:00
Custom pipes must be registered manually.
2015-11-10 13:31:46 -05:00
:marked
2015-12-13 01:01:46 -05:00
If we try the [live code](/resources/live-examples/pipes/ts/plnkr.html) example,
2015-10-19 12:56:24 -04:00
we can probe its behavior by changing the value and the optional exponent in the template.
2015-11-10 13:31:46 -05:00
2015-10-19 12:56:24 -04:00
## Power Boost Calculator (extra-credit)
It's not much fun updating the template to test our custom pipe.
We could upgrade the example to a "Power Boost Calculator" that combines
2015-12-13 01:01:46 -05:00
our pipe and two-way data binding with `ngModel`.
2015-10-19 12:56:24 -04:00
2016-01-13 17:00:43 -05:00
+makeExample('pipes/ts/app/power-boost-calculator.component.ts', null, '/app/power-boost-calculator.component.ts')(format='.')
2015-11-02 03:28:38 -05:00
figure.image-display
2015-12-13 01:01:46 -05:00
img(src='/resources/images/devguide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator")
2015-10-19 12:56:24 -04:00
2015-10-17 13:01:41 -04:00
.l-main-section
2016-01-13 17:00:43 -05:00
a(id="change-detection")
:marked
## Pipes and Change Detection
Angular looks for changes to data-bound values through a *change detection* process that runs after every JavaScript event:
2016-04-06 14:15:07 -04:00
every keystroke, mouse move, timer tick, and server response. It could be expensive.
Angular strives to lower the cost whenever possible and appropriate.
2016-01-13 17:00:43 -05:00
Angular picks a simpler, faster change detection algorithm when we use a pipe. Let's see how.
2016-04-06 14:15:07 -04:00
### No pipe
2016-01-13 17:00:43 -05:00
The component in our next example uses the default, aggressive change detection strategy to monitor and update
its display of every hero in the `heroes` array. Here's the template:
+makeExample('pipes/ts/app/flying-heroes.component.html', 'template-1', 'app/flying-heroes.component.html (v1)')(format='.')
:marked
The companion component class provides heroes, pushes new heroes into the array, and can reset the array.
+makeExample('pipes/ts/app/flying-heroes.component.ts', 'v1', 'app/flying-heroes.component.ts (v1)')(format='.')
:marked
2016-04-06 14:15:07 -04:00
We can add a new hero and Angular updates the display when we do.
The `reset` button replaces `heroes` with a new array of the original heroes and Angular updates the display when we do.
2016-01-13 17:00:43 -05:00
If we added the ability to remove or change a hero, Angular would detect those changes too and update the display again.
add or remove heroes. It updates the display when we modify a hero.
2016-04-06 14:15:07 -04:00
### Flying Heroes pipe
2016-01-13 17:00:43 -05:00
Let's add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly.
+makeExample('pipes/ts/app/flying-heroes.component.html', 'template-flying-heroes', 'app/flying-heroes.component.html (flyers)')(format='.')
2015-11-10 13:31:46 -05:00
:marked
2016-01-13 17:00:43 -05:00
Here's the `FlyingHeroesPipe` implementation which follows the pattern for custom pipes we saw earlier.
+makeExample('pipes/ts/app/flying-heroes.pipe.ts', 'pure', 'app/flying-heroes.pipe.ts')(format='.')
2015-10-19 12:56:24 -04:00
2016-01-13 17:00:43 -05:00
:marked
When we run the sample now we see odd behavior (try it in the [live example](/resources/live-examples/pipes/ts/plnkr.html)).
2016-04-06 14:15:07 -04:00
Every hero we add is a flying hero but none of them are displayed.
Although we're not getting the behavior we want, Angular isn't broken.
2016-01-13 17:00:43 -05:00
It's just using a different change detection algorithm — one that ignores changes to the list or any of its items.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
Look at how we're adding a new hero:
+makeExample('pipes/ts/app/flying-heroes.component.ts', 'push')(format='.')
:marked
2016-04-06 14:15:07 -04:00
We're pushing the new hero into the `heroes` array. The object reference to the array hasn't changed.
2016-01-13 17:00:43 -05:00
It's the same array. That's all Angular cares about. From its perspective, *same array, no change, no display update*.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
We can fix that. Let's use `concat` to create a new array with the new hero appended and assign that to `heroes`.
2016-04-06 14:15:07 -04:00
This time Angular detects that the array object reference has changed.
2016-01-13 17:00:43 -05:00
It executes the pipe and updates the display with the new array which includes the new flying hero.
2016-04-06 14:15:07 -04:00
*If we **mutate** the array, no pipe and no display update;
if we **replace** the array, the pipe executes and the display updates*.
2016-01-13 17:00:43 -05:00
The *Flying Heroes* in the [live example](/resources/live-examples/pipes/ts/plnkr.html) extends the
code with checkbox switches and additional displays to help us experience these effects.
figure.image-display
img(src='/resources/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes")
2015-11-10 13:31:46 -05:00
2016-01-13 17:00:43 -05:00
:marked
Replacing the array is an efficient way to signal to Angular that it should update the display.
When do we replace the array? When the data change.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
That's an easy rule to follow in *this toy* example
where the only way to change the data is by adding a new hero.
2016-04-06 14:15:07 -04:00
More often we don't know when the data have changed,
especially in applications that mutate data in many ways,
2016-01-13 17:00:43 -05:00
perhaps in application locations far away.
A component is such an application usually can't know about those changes.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
Moreover, it's unwise to distort our component design to accommodate a pipe.
We strive as much as possible to keep the component class independent of the HTML.
The component should be unaware of pipes.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
Perhaps we should consider a different kind of pipe for filtering flying heroes, an *impure pipe*.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
.l-main-section
:marked
## Pure and Impure Pipes
2015-11-10 13:31:46 -05:00
2016-01-13 17:00:43 -05:00
There are two categories of pipes: **pure** and **impure**.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
Pipes are pure by default. Every pipe we've seen so far has been pure.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
We make a pipe impure by setting its pure flag to false. We could make the `FlyingHeroesPipe`
impure with a flip of the switch:
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
+makeExample('pipes/ts/app/flying-heroes.pipe.ts', 'pipe-decorator')(format='.')
:marked
Before we do that, let's understand the difference between *pure* and *impure*, starting with a *pure* pipe.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
### Pure pipes
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
Angular executes a *pure pipe* only when it detects a *pure change* to the input value.
2016-04-06 14:15:07 -04:00
A *pure change* is *either* a change to a primitive input value (`String`, `Number`, `Boolean`, `Symbol`)
*or* a changed object reference (`Date`, `Array`, `Function`, `Object`).
Angular ignores changes *within* the object itself.
2016-01-13 17:00:43 -05:00
It won't call a pure pipe if we change the input month, add to the input array, or update an input object property.
2015-11-10 13:31:46 -05:00
2016-04-06 14:15:07 -04:00
This may seem restrictive but is is also fast.
2016-01-13 17:00:43 -05:00
An object reference check is fast ... much faster than a deep check for differences.
... so Angular can quickly determine if it can skip both the pipe execution and a screen update.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
For this reason, we prefer a pure pipe if we can live with the change detection strategy.
When we can't, we *may* turn to the impure pipe.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
.l-sub-section
:marked
2016-04-06 14:15:07 -04:00
Or we might not use a pipe at all.
2016-01-13 17:00:43 -05:00
It may be better to pursue the pipe's purpose with a property of the component,
a point we take up later.
:marked
### Impure pipes
2016-04-06 14:15:07 -04:00
Angular executes an *impure pipe* during *every* component change detection cycle.
2016-01-13 17:00:43 -05:00
An impure pipe will be called a lot, as often as every keystroke or mouse-move.
2016-04-06 14:15:07 -04:00
With that concern in mind, we must implement an impure pipe with great care.
2016-01-13 17:00:43 -05:00
An expensive, long-running pipe could destroy the user experience.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
<a id="impure-flying-heroes"></a>
### An impure *FlyingHeroesPipe*
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
A flip of the switch turns our `FlyingHeroesPipe` into a `FlyingHeroesImpurePipe`.
Here's the complete implementation:
+makeTabs(
2016-04-06 14:15:07 -04:00
'pipes/ts/app/flying-heroes.pipe.ts, pipes/ts/app/flying-heroes.pipe.ts',
2016-01-13 17:00:43 -05:00
'impure, pure',
'FlyingHeroesImpurePipe, FlyingHeroesPipe')(format='.')
:marked
We inherit from `FlyingHeroesPipe` to prove the point that nothing changed internally.
The only difference is the `pure` flag in the pipe metadata.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
This is a good candidate for an impure pipe because the `transform` function is trivial and fast.
+makeExample('pipes/ts/app/flying-heroes.pipe.ts','filter')(format='.')
2015-10-19 12:56:24 -04:00
2016-01-13 17:00:43 -05:00
We can derive a `FlyingHeroesImpureComponent` that we derive from the `FlyingHeroesComponent`.
2016-03-26 12:18:13 -04:00
+makeExample('pipes/ts/app/flying-heroes.component.ts','impure-component','app/flying-heroes.component.ts (FlyingHeroesImpureComponent)')(format='.')
2016-01-13 17:00:43 -05:00
:marked
2016-04-06 14:15:07 -04:00
The only substantive change is the pipe.
2016-01-13 17:00:43 -05:00
We can confirm in the [live example](/resources/live-examples/pipes/ts/plnkr.html)
that the *flying heroes* display updates as we enter new heroes even when we mutate the `heroes` array.
2016-04-06 14:15:07 -04:00
2015-12-01 06:15:14 -05:00
<a id="async-pipe"></a>
2016-01-13 17:00:43 -05:00
### The impure *AsyncPipe*
The Angular `AsyncPipe` is an interesting example of an impure pipe.
The `AsyncPipe` accepts a `Promise` or `Observable` as input
and subscribes to the input automatically, eventually returning the emitted value(s).
2015-11-10 13:31:46 -05:00
2016-04-06 14:15:07 -04:00
It is also stateful.
The pipe maintains a subscription to the input `Observable` and
2016-01-13 17:00:43 -05:00
keeps delivering values from that `Observable` as they arrive.
2015-10-19 12:56:24 -04:00
2016-01-13 17:00:43 -05:00
In this next example, we bind an `Observable` of message strings (`messages$`) to a view with the `async` pipe.
2015-12-13 01:01:46 -05:00
+makeExample('pipes/ts/app/hero-async-message.component.ts', null, 'app/hero-async-message.component.ts')
2015-10-19 12:56:24 -04:00
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
The Async pipe saves boilerplate in the component code.
The component doesn't have to subscribe to the async data source,
2015-10-23 12:31:51 -04:00
it doesn't extract the resolved values and expose them for binding,
2016-01-13 17:00:43 -05:00
and the component doesn't have to unsubscribe when it is destroyed
2015-10-19 12:56:24 -04:00
(a potent source of memory leaks).
2015-11-10 13:31:46 -05:00
2016-01-13 17:00:43 -05:00
### An impure caching pipe
Let's write one more impure pipe, a pipe that makes an http request to the server.
2016-04-06 14:15:07 -04:00
Normally, that's a horrible idea.
2016-01-13 17:00:43 -05:00
It's probably a horrible idea no matter what we do.
We're forging ahead anyway to make a point.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
Remember that impure pipes are called every few microseconds.
If we're not careful, this pipe will punish the server with requests.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
We are careful. Our pipe only makes a server call if the request URL has changed.
It caches the request URL and waits for a result which it also caches when it arrives.
The pipe returns the cached result (which is null while a request is in flight)
after every Angular call and only contacts the server as necessary.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
Here's the code, which uses the [Angular http](server-communication.html) facility
to retrieve a `heroes.json` file:
2015-10-19 12:56:24 -04:00
2015-12-13 01:01:46 -05:00
+makeExample('pipes/ts/app/fetch-json.pipe.ts', null, 'app/fetch-json.pipe.ts')
2015-11-10 13:31:46 -05:00
:marked
2016-01-13 17:00:43 -05:00
Then we demonstrate it in a harness component whose template defines two bindings to this pipe.
2015-12-13 01:01:46 -05:00
+makeExample('pipes/ts/app/hero-list.component.ts', 'template', 'app/hero-list.component.ts (template)')
:marked
2016-04-06 14:15:07 -04:00
Despite the two bindings and what we know to be frequent pipe calls,
2016-01-13 17:00:43 -05:00
the nework tab in the browser developer tools confirms that there is only one request for the file.
2016-04-06 14:15:07 -04:00
2015-12-13 01:01:46 -05:00
The component renders like this:
2015-11-02 03:28:38 -05:00
figure.image-display
img(src='/resources/images/devguide/pipes/hero-list.png' alt="Hero List")
2015-12-13 01:01:46 -05:00
:marked
2016-01-13 17:00:43 -05:00
### *JsonPipe*
The second binding involving the `FetchPipe` uses more pipe chaining.
2016-04-06 14:15:07 -04:00
We take the same fetched results displayed in the first binding
2016-01-13 17:00:43 -05:00
and display them again, this time in JSON format by chaining through to the built-in `JsonPipe`.
2016-04-06 14:15:07 -04:00
2015-12-13 01:01:46 -05:00
.callout.is-helpful
header Debugging with the json pipe
:marked
2016-03-17 00:42:55 -04:00
The [JsonPipe](../api/common/JsonPipe-class.html)
2016-01-13 17:00:43 -05:00
provides an easy way to diagnosis a mysteriously failing data binding or
2016-04-06 14:15:07 -04:00
inspect an object for future binding.
2015-12-13 01:01:46 -05:00
:marked
Here's the complete component implementation:
+makeExample('pipes/ts/app/hero-list.component.ts', null, 'app/hero-list.component.ts')
2016-01-13 17:00:43 -05:00
a(id="pure-pipe-pure-fn")
:marked
2016-04-20 20:12:21 -04:00
### Pure pipes and pure functions
2016-04-06 14:15:07 -04:00
2016-04-20 20:12:21 -04:00
A pure pipe uses pure functions.
2016-04-06 14:15:07 -04:00
Pure functions process inputs and return values without detectable side-effects.
2016-01-13 17:00:43 -05:00
Given the same input they should always return the same output.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
The pipes we saw earlier in this chapter were implemented with pure functions.
2016-04-06 14:15:07 -04:00
The built-in `DatePipe` is a pure pipe with a pure function implementation.
2016-01-13 17:00:43 -05:00
So is our `ExponentialStrengthPipe`.
So is our `FlyingHeroesPipe`.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
A few steps back we reviewed the `FlyingHeroesImpurePipe` — *an impure pipe with a pure function*.
2016-04-20 20:12:21 -04:00
But a *pure pipe* must always be implemented with a *pure function*. Failure to heed this warning will bring about many a console errors regarding expressions that have changed after they were checked.
2015-10-23 12:31:51 -04:00
.l-main-section
2015-11-10 13:31:46 -05:00
:marked
2015-10-23 12:31:51 -04:00
## Next Steps
2015-11-10 13:31:46 -05:00
2015-10-23 12:31:51 -04:00
Pipes are a great way to encapsulate and share common display-value
transformations. We use them like styles, dropping them
into our templates expressions to enrich the appeal and usability
of our views.
2015-11-10 13:31:46 -05:00
2016-01-13 17:00:43 -05:00
Explore Angular's inventory of built-in pipes in the [API Reference](../api/#!?apiFilter=pipe).
2015-11-17 13:17:14 -05:00
Try writing a custom pipe and perhaps contributing it to the community.
2016-01-13 17:00:43 -05:00
a(id="no-filter-pipe")
.l-main-section
:marked
## No *FilterPipe* or *OrderByPipe*
Angular does not ship with pipes for filtering or sorting lists.
Developers familiar with Angular 1 know these as `filter` and `orderBy`.
2016-04-06 14:15:07 -04:00
There are no equivalents in Angular 2.
2016-01-13 17:00:43 -05:00
This is not an oversight. Angular 2 is unlikely to offer such pipes because
(a) they perform poorly and (b) they prevent aggressive minification.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
Both *filter* and *orderBy* require parameters that reference object properties.
We learned earlier that such pipes must be [*impure*](#pure-and-impure-pipes) and that
Angular calls impure pipes in almost every change detection cycle.
2016-04-06 14:15:07 -04:00
Filtering and especially sorting are expensive operations.
2016-01-13 17:00:43 -05:00
The user experience can degrade severely for even moderate sized lists when Angular calls these pipe methods many times per second.
The `filter` and `orderBy` have often been abused in Angular 1 apps, leading to complaints that Angular itself is slow.
2016-04-06 14:15:07 -04:00
That charge is fair in the indirect sense that Angular 1 prepared this performance trap
2016-01-13 17:00:43 -05:00
by offering `filter` and `orderBy` in the first place.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
The minification hazard is also compelling if less obvious. Imagine a sorting pipe applied to a list of heroes.
We might sort the list by hero `name` and `planet` origin properties something like this:
code-example(format="." language="html")
<!-- NOT REAL CODE! -->
2016-05-01 18:29:25 -04:00
<div *ngFor="let hero of heroes | orderBy:'name,planet'"></div>
2016-01-13 17:00:43 -05:00
:marked
We identify the sort fields by text strings, expecting the pipe to reference a property value by indexing
(e.g., `hero['name']`).
Unfortunately, aggressive minification *munges* the `Hero` property names so that `Hero.name` and `Hero.planet`
becomes something like `Hero.a` and `Hero.b`. Clearly `hero['name']` is not going to work.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
Some of us may not care to minify this aggressively. That's *our* choice.
But the Angular product should not prevent someone else from minifying aggressively.
Therefore, the Angular team decided that everything shipped in Angular will minify safely.
2016-04-06 14:15:07 -04:00
2016-01-13 17:00:43 -05:00
The Angular team and many experienced Angular developers strongly recommend that you move
2016-04-06 14:15:07 -04:00
filtering and sorting logic into the component itself.
2016-01-13 17:00:43 -05:00
The component can expose a `filteredHeroes` or `sortedHeroes` property and take control
2016-04-06 14:15:07 -04:00
over when and how often to execute the supporting logic.
Any capabilities that you would have put in a pipe and shared across the app can be
2016-01-13 17:00:43 -05:00
written in a filtering/sorting service and injected into the component.
2016-04-06 14:15:07 -04:00
If these performance and minification considerations do not apply to you, you can always create your own such pipes
2016-01-13 17:00:43 -05:00
(along the lines of the [FlyingHeroesPipe](#impure-flying-heroes)) or find them in the community.