Pipe Tricks with Angular 2 for Dart
Pipes in Angular 2 (known as filters in Angular 1.x) are a nice, convenient way to keep your application data clean, unsullied by visual artifacts, but still present that data to your users in an attractive way. Currency would be a good example of this situation: Users like to see currency symbols, such as dollar signs, with their numbers, but internally, your code has no use for those. You're trying to do math on these numbers. Users also don't care to see currency values with more than two decimal places. With pipes, you can make everyone happy.
Dart
Dart is an open-source, scalable, object-oriented programming language, with robust libraries and runtimes, for building web, server, and mobile apps. It was originally developed by Google, but has since become an ECMA standard.
Dart Primers
Check out the Dart Language Tour for a crash course in the Dart language. If you already know JavaScript, Java, PHP, ActionScript, C/C++/C#, or another "curly brace" language, you'll find Dart to be familiar, and you can be productive with Dart within an hour or so.
If you like learning from videos, take a look at Learn Dart in One Video.
Get Dart
Before you can get into the exciting world of writing Dart code, you need to download the Dart SDK and, for web apps, a special browser called Dartium:
- Dart SDK and Dartium
- On Windows? Try the Dart for Windows installer, or use Chocolatey.
- On Mac? Use Homebrew.
- On Linux? Use apt-get or the Dart Debian package.
- Dart-Up is a great option for Windows, Mac, or Linux.
Angular 2
Angular 2 is the much anticipated successor to the popular Angular web application framework, featuring many of the same concepts, but with a greatly simplified syntax. The framework is available for Dart, JavaScript, and TypeScript.
Quickstart
To get an Angular 2 Dart app up and running quickly, try the 5-Minute Quickstart tutorial.
Pipes
A pipe is basically an intermediary transformer that takes in one value, manipulates it in some way, and spits out the new value. Angular provides a number of pipes out of the box, including pipes for formatting dates, making strings uppercase or lowercase, and one for formatting percentage values. You can also create custom pipes.
Making Pipes Available
The typical way to make a pipe available to your Angular component is to import the pipe's class, and then include it in the @Component decorator's pipes list:
lib/app_component.dart
import 'package:angular2/angular2.dart';
@Component(selector: 'my-app',
templateUrl: 'app_component.html',
pipes: const [CurrencyPipe]
)
class AppComponent {
AppComponent();
}
Note: Here, I've used the very broad import,
package:angular2/angular2.dart, which happens to include all of the built-in pipes.
If it seems laborious to declare commonly used pipes in this way, there is a trick you can use to make Angular's pipes available to all. You can coerce the Angular transformer to do it for you, as demonstrated in this snippet from a project's pubspec.yaml file:
pubspec.yaml
transformers:
- angular2:
platform_pipes:
- 'package:angular2/common.dart#COMMON_PIPES'
entry_points: web/main.dart
With that in place, you'll be able to access all of Angular's handy pipes without declaring them explicitly to every component that needs them:
lib/app_component.dart
import 'package:angular2/angular2.dart';
@Component(selector: 'my-app',
templateUrl: 'app_component.html'
)
class AppComponent {
AppComponent();
}
Percent Pipe
Using one of Angular's built-in pipes, let's take a look at an example. Suppose you have a floating point value representing a percent called progress, and you want to display it in your view:
lib/app_component.dart
class AppComponent {
num progress = 0.259;
AppComponent();
}
lib/app_component.html
Total progress is: {{progress}}
The output of Angular's interpolation will be:
Total progress is: 0.259
This output could be confusing to your users, as it supplies no indication that it's a percentage. Let's improve the display with PercentPipe:
lib/app_component.html
Total progress is: {{progress | percent}}
Now the display looks like:
Total progress is: 25.9%
Better! Now it's clear we're talking about a portion of a whole. What if we want more control over the way the number is displayed, though? Maybe it's in a table, and we want all the numbers to line up nicely.
Pipes Can Take Parameters
Luckily, the Angular team hasn't let us down here. Like many pipes, PercentPipe is configurable. It can take one value, digitInfo, which allows you to format the placement of the digits, including using padding when needed:
lib/app_component.html
Total progress is: {{progress | percent:'4.3-5'}}
The argument passed here follows this pattern: {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}
And now you have:
Total progress is: 0025.900%
Async Pipe
Maybe you don't yet have your percentage data. It might be coming from a remote source, delivered to your client asynchronously. This is where the AsyncPipe comes in. Take a look at this example, where the data binding chains the AsyncPipe and PercentPipe to show progress:
lib/app_component.html
Total progress is: {{progressStream | async | percent:'1.2-2'}}
lib/app_component.dart
import 'dart:async';
import 'package:angular2/angular2.dart';
@Component(selector: 'my-app',
templateUrl: 'app_component.html'
)
class AppComponent {
Stream progressStream;
AppComponent() {
Duration delay = new Duration(milliseconds: 500);
progressStream =
new Stream.periodic(delay, (int index) => index * 0.005);
}
}
If you run that code, you'll see the value increasing by 0.5% every half-second as data is fed into progressStream.
Total progress is: 8.50%
Bug! The AsyncPipe stopped working after Angular 2 Dart beta-15, and an issue has been filed. For now, you will need to restrict your Angular dependency in pubspec.yaml to that version if you want to test this code:
angular2: 2.0.0-beta.15
Creating Custom Pipes
Of course, you can create your own pipes. A pipe is free to transform a value in any complicated way you want, but in this example, it'll be very simple. It will add the symbol for "degrees" at the end of whatever value is passed in.
First, create the pipe class:
lib/degrees_pipe.dart
import 'package:angular2/core.dart';
@Pipe(name: 'degrees')
class DegreesPipe extends PipeTransform {
String transform(val, [List args]) => "$val°";
}
Then tell your component about it. Remember, for custom pipes, you'll have to go back to using the pipes list in your @Component decorator:
lib/app_component.dart
import 'package:angular2/angular2.dart';
import 'degrees_pipe.dart';
@Component(selector: 'my-app',
templateUrl: 'app_component.html',
pipes: const [DegreesPipe]
)
class AppComponent {
num temperature = 65;
AppComponent();
}
And finally, use it in your view template:
lib/app_component.html
Temperature is: {{temperature | degrees}}
And the output:
Temperature is: 65°
Making a Custom Pipe Globally Accessible
In the last section, you created a custom pipe and then found, to your everlasting consternation, that you had to go back to using the pipes list to tell your component about it. It turns out that you can add your own list of common pipes for Dart's Angular transformer to auto-magically inject everywhere for you.
First, create a new file to house your list:
lib/common_pipes.dart
import 'degrees_pipe.dart' show DegreesPipe;
export 'degrees_pipe.dart' show DegreesPipe;
const COMMON_PIPES = const [DegreesPipe];
In that file, you need to import your pipe so that you can reference it in the COMMON_PIPES list, and you need to export it to make it available to the transformer.
Now that you've created your list of one common pipe, you need to tell the transformer about it:
pubspec.yaml
transformers:
- angular2:
platform_pipes:
- 'package:angular2/common.dart#COMMON_PIPES'
- 'package:my_project/common_pipes.dart#COMMON_PIPES'
entry_points: web/main.dart
In addition to Angular's common pipes, you include your own list. Once you have, the transformer will make sure your pipes are available in every component in your project without them having to be explicitly injected, or even imported.
Important! Don't forget to change
my_projectto the name of your own project. It should be the name defined at the top of your pubspec.yaml file.
Conclusion
Thank you for joining us for this first (and possibly only) edition of Pipe Tricks. Until next time, use Angular, use Dart, and use good judgement.