2015-10-17 13:01:41 -04:00
include ../../../../_includes/_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!
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
a human-friendly date.
2015-10-19 12:56:24 -04:00
Here's a complete mini-app with a `DatePipe`:
<!--
All date samples are in my plunker
http://plnkr.co/edit/RDlOma?p=preview
-->
2015-11-10 13:31:46 -05:00
2015-11-02 03:28:38 -05:00
+makeExample('pipes/ts/src/app/app.ts', 'hero-birthday')
2015-11-10 13:31:46 -05:00
:marked
Focus on the component's template to see how we applied the built-in `DatePipe`
while binding the `birthday` property.
2015-11-02 03:28:38 -05:00
+makeExample('pipes/ts/src/app/app.html', 'hero-birthday-template')(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
Angular [template syntax](./template-syntax.html#pipe) includes a pipe operator ( | ) which we're
using to flow the birthday value on the left through to the `Date` pipe function on the right.
All pipes work this way.
.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
Learn more about these and many other built-in pipes in the the [API Reference](../api/);
filter for entries that include the word "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-10-23 12:31:51 -04:00
We'll modify our birthday example to give the date pipe a format parameter.
The formatted date should display as **<span style="font-family:courier">04/15/88</span>**.
2015-10-19 12:56:24 -04:00
2015-11-02 03:28:38 -05:00
+makeExample('pipes/ts/src/app/app.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
2015-11-10 13:31:46 -05:00
[template expression](./template-expression.html#template-expressions)
2015-10-19 12:56:24 -04:00
such as a string literal or a component property.
Let's revise our example to bind the pipe's format parameter
to the component's `format` property.
2015-11-10 13:31:46 -05:00
+makeExample('pipes/ts/src/app/hero-birthday.2.ts', 'hero-birthday2')
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
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').
2015-11-10 13:31:46 -05:00
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-10-19 12:56:24 -04:00
.l-sub-section
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
Learn more about the `DatePipes` format options in the [API Docs](../api/core/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
+makeExample('pipes/ts/src/app/app.html', 'chained-birthday')
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
+makeExample('pipes/ts/src/app/app.html', 'chained-parameter-birthday')
2015-10-19 12:56:24 -04:00
.l-sub-section
p Future improvements in the template compiler may eliminate the need for parentheses.
.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-11-02 03:28:38 -05:00
Let's make a custom pipe named `ExponentialStrengthPipe`
2015-10-19 12:56:24 -04:00
that can boost a hero's powers.
2015-11-10 13:31:46 -05:00
2015-11-02 03:28:38 -05:00
Create a new file, `exponential-strength-pipe.ts`, and enter the following:
2015-10-19 12:56:24 -04:00
<!--
The exponential pipe samples are in my plunker
http://plnkr.co/edit/8Nnnwf?p=preview
2015-11-02 03:28:38 -05:00
-->
+makeExample('pipes/ts/src/app/exponential-strength-pipe.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
This pipe definition reveals several few key points
* We import the `Pipe` decorator from the Angular library (while getting the usual symbols)
* A pipe is a class
2015-11-10 13:31:46 -05:00
* We decorate the class with the `@Pipe` decorator function.
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.
2015-11-10 13:31:46 -05:00
Our pipe's name is `exponenentialStrength`.
* The pipe class implements a `transform` method
* `transform` takes a value and an optional array of strings.
2015-10-19 12:56:24 -04:00
The value can be of any type but the arguments array must be an array of strings.
* There will be one item in the array for each parameter passed to the pipe
* `transform` returns a modified value that Angular converts to a string.
2015-11-02 03:28:38 -05:00
Now let's create a component to demonstrate our pipe.
+makeExample('pipes/ts/src/app/power-booster.ts')
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-11-10 13:31:46 -05:00
1. We use the pipe in the template expression exactly as we described in the pipe's comments.
2015-10-19 12:56:24 -04:00
We pass the value to transform from the left and give our pipe an exponent parameter of `10`.
2015-11-10 13:31:46 -05:00
2015-10-19 12:56:24 -04:00
1. We must list our pipe in the @Component decorator's `pipes` array.
2015-11-10 13:31:46 -05:00
2015-10-19 12:56:24 -04:00
.callout.is-critical
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-10-19 12:56:24 -04:00
If we are inclined to try this in a live-coding tool (such a [plunker](http://plnkr.co/)),
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
our pipe and two-way data binding with `ng-model`.
2015-11-02 03:28:38 -05:00
+makeExample('pipes/ts/src/app/power-boost-calculator.ts')
figure.image-display
img(src='/resources/images/devguide/pipes/power-boost-calculator.png' alt="Power Boost Calculator")
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
2015-10-17 13:01:41 -04:00
.l-main-section
2015-11-10 13:31:46 -05:00
:marked
2015-10-19 12:56:24 -04:00
## Stateful Pipes
2015-11-10 13:31:46 -05:00
There are two categories of pipes, stateless and stateful.
Stateless pipes are pure functions that flow input data
2015-10-19 12:56:24 -04:00
through without remembering anything or causing detectable side-effects.
2015-11-10 13:31:46 -05:00
2015-10-19 12:56:24 -04:00
Most pipes are stateless. The `DatePipe` in our first example is a stateless pipe. So is our custom `ExponentialStrengthPipe`.
2015-11-10 13:31:46 -05:00
Stateful pipes are conceptually similar to classes in object-oriented programming. They can manage the data they transform. A pipe that creates an HTTP request, stores the response and displays the output, is a stateful pipe.
Pipes that retrieve or request data should be used cautiously, since working with network data tends to introduce error conditions that are better handled in JavaScript than in a template.
2015-10-23 12:31:51 -04:00
We can mitigate this risk by creating a custom pipe for a particular backend and bake-in the essential error-handling.
2015-10-19 12:56:24 -04:00
## The stateful `AsyncPipe`
2015-11-10 13:31:46 -05:00
The Angular Async pipe is a remarkable example of a stateful pipe.
The Async pipe can receive a Promise or Observable as input
and subscribe to the input automatically, eventually returning the emitted value(s).
2015-10-19 12:56:24 -04:00
It is stateful because the pipe maintains a subscription to the input and its returned values depend on that subscription.
2015-10-23 12:31:51 -04:00
In the next example, we bind a simple promise to a view with the async pipe.
2015-11-02 03:28:38 -05:00
+makeExample('pipes/ts/src/app/app.ts', 'async-message')
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,
2015-10-19 12:56:24 -04:00
and (in the case of Obsevable stream sources like `EventEmitter`)
the component doesn't have to unsubscribe when it is destroyed
(a potent source of memory leaks).
2015-11-10 13:31:46 -05:00
2015-10-19 12:56:24 -04:00
### Implementing a Stateful Pipe
2015-11-10 13:31:46 -05:00
Pipes are stateless by default.
We must declare a pipe to be stateful
2015-11-02 03:28:38 -05:00
by setting the `pure` property of the `@Pipe` decorator to `false`.
2015-11-10 13:31:46 -05:00
This setting tells Angular’ s change detection system to
2015-10-19 12:56:24 -04:00
check the output of this pipe each cycle, whether its input has changed or not.
2015-11-10 13:31:46 -05:00
Here's how we'll decorate our new stateful `FetchJsonPipe` that
2015-10-19 12:56:24 -04:00
makes an HTTP `fetch` request and (eventually) displays the data in the server's response:
2015-11-02 03:28:38 -05:00
+makeExample('pipes/ts/src/app/fetch-json-pipe.ts', 'pipe-metadata')
2015-11-10 13:31:46 -05:00
:marked
Immediately below we have the finished pipe. Its input value is an url to an endpoint that returns a JSON file.
2015-10-19 12:56:24 -04:00
The pipe makes a one-time async request to the server and eventually receives the JSON response.
2015-11-02 03:28:38 -05:00
+makeExample('pipes/ts/src/app/fetch-json-pipe.ts')
2015-11-10 13:31:46 -05:00
:marked
2015-11-02 03:28:38 -05:00
Next we use this pipe in two template bindings where we
1. display hero names in an `ng-for` repeater
1. chain the fetched results to the built-in `JsonPipe` that renders
the data in JSON format
+makeExample('pipes/ts/src/app/hero-list-component.ts')
2015-10-19 12:56:24 -04:00
2015-11-02 03:28:38 -05:00
figure.image-display
img(src='/resources/images/devguide/pipes/hero-list.png' alt="Hero List")
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
Explore Angular's inventory of built-in pipes in the [API Reference](../api/).
2015-11-17 13:17:14 -05:00
Try writing a custom pipe and perhaps contributing it to the community.