docs(dev guide): pipes - new Dart prose, update Dart and Ts code (#1353)
+ guide/pipes/ts: update docs and example code + guide/pipes/dart: new prose, updated example code + fix platform_directives reference; html cleanup + enable pipes e2e testing For `e2e-spec.js`: If the async test is executed too early it will fail (simply because the async message hasn’t been received yet). + follow new constants naming convention
This commit is contained in:
parent
c1440e7eff
commit
ad95b04e69
|
@ -15,6 +15,10 @@
|
||||||
- var _array = 'array';
|
- var _array = 'array';
|
||||||
- var _an_array = 'an array';
|
- var _an_array = 'an array';
|
||||||
|
|
||||||
|
//- Promise vs. Future, etc
|
||||||
|
- var _Promise = 'Promise';
|
||||||
|
- var _Observable = 'Observable';
|
||||||
|
|
||||||
//- Used to prefix identifiers that are private. In Dart this will be '_'.
|
//- Used to prefix identifiers that are private. In Dart this will be '_'.
|
||||||
- var _priv = '';
|
- var _priv = '';
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,26 @@
|
||||||
|
// #docregion
|
||||||
import 'package:angular2/angular2.dart';
|
import 'package:angular2/angular2.dart';
|
||||||
|
|
||||||
|
import 'flying_heroes_component.dart';
|
||||||
import 'hero_async_message_component.dart';
|
import 'hero_async_message_component.dart';
|
||||||
|
import 'hero_birthday1_component.dart';
|
||||||
import 'hero_birthday2_component.dart';
|
import 'hero_birthday2_component.dart';
|
||||||
import 'hero_list_component.dart';
|
import 'hero_list_component.dart';
|
||||||
import 'power_booster.dart';
|
import 'power_boost_calculator_component.dart';
|
||||||
import 'power_boost_calculator.dart';
|
import 'power_booster_component.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
templateUrl: 'app_component.html',
|
templateUrl: 'app_component.html',
|
||||||
directives: const [
|
directives: const [
|
||||||
|
FlyingHeroesComponent,
|
||||||
|
FlyingHeroesImpureComponent,
|
||||||
HeroAsyncMessageComponent,
|
HeroAsyncMessageComponent,
|
||||||
HeroBirthday,
|
HeroBirthday,
|
||||||
|
HeroBirthday2,
|
||||||
HeroListComponent,
|
HeroListComponent,
|
||||||
|
PowerBoostCalculator,
|
||||||
PowerBooster,
|
PowerBooster,
|
||||||
PowerBoostCalculator
|
|
||||||
])
|
])
|
||||||
class AppComponent {
|
class AppComponent {
|
||||||
DateTime birthday = new DateTime(1988, 4, 15); // April 15, 1988
|
DateTime birthday = new DateTime(1988, 4, 15); // April 15, 1988
|
||||||
|
|
|
@ -1,36 +1,83 @@
|
||||||
|
<a id="toc"></a>
|
||||||
|
<h1>Pipes</h1>
|
||||||
|
<a href="#happy-birthday1">Happy Birthday v1</a><br>
|
||||||
|
<a href="#birthday-date-pipe">Birthday DatePipe</a><br>
|
||||||
|
<a href="#happy-birthday2">Happy Birthday v2</a><br>
|
||||||
|
<a href="#birthday-pipe-chaining">Birthday Pipe Chaining</a><br>
|
||||||
|
<a href="#power-booster">Power Booster custom pipe</a><br>
|
||||||
|
<a href="#power-boost-calc">Power Boost Calculator custom pipe with params</a><br>
|
||||||
|
<a href="#flying-heroes">Flying Heroes filter pipe (pure)</a><br>
|
||||||
|
<a href="#flying-heroes-impure">Flying Heroes filter pipe (impure)</a><br>
|
||||||
|
<a href="#hero-message">Async Hero Message and AsyncPipe</a><br>
|
||||||
|
<a href="#hero-list">Hero List with caching FetchJsonPipe</a><br>
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
<a id="happy-birthday1"></a>
|
||||||
|
<h2>Hero Birthday v1</h2>
|
||||||
|
<hero-birthday></hero-birthday>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<a id="birthday-date-pipe"></a>
|
||||||
|
<h2>Birthday DatePipe</h2>
|
||||||
|
<!-- #docregion hero-birthday-template -->
|
||||||
|
<p>The hero's birthday is {{ birthday | date }}</p>
|
||||||
|
<!-- #enddocregion hero-birthday-template-->
|
||||||
|
|
||||||
|
<!-- #docregion format-birthday -->
|
||||||
|
<p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </p>
|
||||||
|
<!-- #enddocregion format-birthday-->
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<a id="happy-birthday2"></a>
|
||||||
|
<h2>Hero Birthday v2</h2>
|
||||||
|
<hero-birthday2></hero-birthday2>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<a id="birthday-pipe-chaining"></a>
|
||||||
|
<h2>Birthday Pipe Chaining</h2>
|
||||||
|
<p>
|
||||||
|
<!-- #docregion chained-birthday -->
|
||||||
|
The chained hero's birthday is
|
||||||
|
{{ birthday | date | uppercase}}
|
||||||
|
<!-- #enddocregion chained-birthday -->
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<!-- #docregion chained-parameter-birthday -->
|
||||||
|
The chained hero's birthday is
|
||||||
|
{{ birthday | date:'fullDate' | uppercase}}
|
||||||
|
<!-- #enddocregion chained-parameter-birthday -->
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<!-- #docregion chained-parameter-birthday-parens -->
|
||||||
|
The chained hero's birthday is
|
||||||
|
{{ ( birthday | date:'fullDate' ) | uppercase}}
|
||||||
|
<!-- #enddocregion chained-parameter-birthday-parens -->
|
||||||
|
</p>
|
||||||
|
<hr>
|
||||||
|
<a id="power-booster"></a>
|
||||||
|
<power-booster></power-booster>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<a id="power-boost-calc"></a>
|
||||||
|
<power-boost-calculator>loading</power-boost-calculator>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<a id="flying-heroes"></a>
|
||||||
|
<flying-heroes></flying-heroes>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<a id="flying-heroes-impure"></a>
|
||||||
|
<flying-heroes-impure></flying-heroes-impure>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<a id="hero-message"></a>
|
||||||
<!-- async examples at the top so can see them in action -->
|
<!-- async examples at the top so can see them in action -->
|
||||||
<hero-message></hero-message>
|
<hero-message></hero-message>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
<a id="hero-list"></a>
|
||||||
<hero-list></hero-list>
|
<hero-list></hero-list>
|
||||||
|
|
||||||
<hr>
|
<div style="margin-top:12em;"></div>
|
||||||
<p>The hero's birthday is {{ birthday | date }}</p>
|
|
||||||
|
|
||||||
<p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </p>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<h4>Hero Birthday v.2</h4>
|
|
||||||
<hero-birthday>loading...</hero-birthday>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The chained hero's birthday is
|
|
||||||
{{ birthday | date | uppercase}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The chained hero's birthday is
|
|
||||||
{{ birthday | date:'fullDate' | uppercase}}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The chained hero's birthday is
|
|
||||||
{{ ( birthday | date:'fullDate' ) | uppercase}}
|
|
||||||
</p>
|
|
||||||
<hr>
|
|
||||||
<power-booster>loading...</power-booster>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<power-boost-calculator>loading ..</power-boost-calculator>
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
// #docregion
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:angular2/angular2.dart';
|
import 'package:angular2/angular2.dart';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -13,11 +13,13 @@ import 'package:angular2/angular2.dart';
|
||||||
*/
|
*/
|
||||||
@Pipe(name: 'exponentialStrength')
|
@Pipe(name: 'exponentialStrength')
|
||||||
class ExponentialStrengthPipe extends PipeTransform {
|
class ExponentialStrengthPipe extends PipeTransform {
|
||||||
transform(dynamic value, [List<dynamic> args]) {
|
num transform(dynamic _value, [List<dynamic> args]) {
|
||||||
var v = int.parse(value.toString(), onError: (source) => 0);
|
var exponent = args.isEmpty
|
||||||
var p = args.isEmpty
|
|
||||||
? 1
|
? 1
|
||||||
: int.parse(args.first.toString(), onError: (source) => 1);
|
: args.first is num
|
||||||
return math.pow(v, p);
|
? args.first
|
||||||
|
: num.parse(args.first.toString(), (_) => 1);
|
||||||
|
var value = _value is num ? _value : num.parse(_value.toString(), (_) => 0);
|
||||||
|
return math.pow(value, exponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:html';
|
||||||
|
|
||||||
import 'package:angular2/angular2.dart';
|
import 'package:angular2/angular2.dart';
|
||||||
|
|
||||||
|
@ -9,15 +8,17 @@ import 'package:angular2/angular2.dart';
|
||||||
@Pipe(name: 'fetch', pure: false)
|
@Pipe(name: 'fetch', pure: false)
|
||||||
// #enddocregion pipe-metadata
|
// #enddocregion pipe-metadata
|
||||||
class FetchJsonPipe extends PipeTransform {
|
class FetchJsonPipe extends PipeTransform {
|
||||||
dynamic _fetchedValue;
|
dynamic _fetchedJson;
|
||||||
Future<dynamic> _fetchPromise;
|
String _prevUrl;
|
||||||
|
|
||||||
transform(dynamic url, [List<dynamic> args]) {
|
dynamic transform(dynamic url, [List<dynamic> args]) {
|
||||||
if (_fetchPromise == null) {
|
if (url != _prevUrl) {
|
||||||
_fetchPromise = new Future(() async {
|
_prevUrl = url;
|
||||||
_fetchedValue = JSON.decode(await HttpRequest.getString(url));
|
_fetchedJson = null;
|
||||||
|
HttpRequest.getString(url).then((s) {
|
||||||
|
_fetchedJson = JSON.decode(s);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return _fetchedValue;
|
return _fetchedJson;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
// #docplaster
|
||||||
|
// #docregion
|
||||||
|
import 'package:angular2/angular2.dart';
|
||||||
|
import 'flying_heroes_pipe.dart';
|
||||||
|
import 'heroes.dart';
|
||||||
|
|
||||||
|
@Component(
|
||||||
|
selector: 'flying-heroes',
|
||||||
|
templateUrl: 'flying_heroes_component.html',
|
||||||
|
styles: const ['#flyers, #all {font-style: italic}'],
|
||||||
|
pipes: const [FlyingHeroesPipe])
|
||||||
|
// #docregion v1
|
||||||
|
class FlyingHeroesComponent {
|
||||||
|
List<Hero> heroes;
|
||||||
|
bool canFly = true;
|
||||||
|
// #enddocregion v1
|
||||||
|
bool mutate = true;
|
||||||
|
String title = 'Flying Heroes (pure pipe)';
|
||||||
|
|
||||||
|
// #docregion v1
|
||||||
|
FlyingHeroesComponent() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addHero(String name) {
|
||||||
|
name = name.trim();
|
||||||
|
if (name.isEmpty) return;
|
||||||
|
|
||||||
|
var hero = new Hero(name, canFly);
|
||||||
|
// #enddocregion v1
|
||||||
|
if (mutate) {
|
||||||
|
// Pure pipe won't update display because heroes list
|
||||||
|
// reference is unchanged; Impure pipe will display.
|
||||||
|
// #docregion v1, push
|
||||||
|
heroes.add(hero);
|
||||||
|
// #enddocregion v1, push
|
||||||
|
} else {
|
||||||
|
// Pipe updates display because heroes list is a new object
|
||||||
|
// #docregion concat
|
||||||
|
heroes = new List<Hero>.from(heroes)..add(hero);
|
||||||
|
// #enddocregion concat
|
||||||
|
}
|
||||||
|
// #docregion v1
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
heroes = new List<Hero>.from(mockHeroes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #enddocregion v1
|
||||||
|
|
||||||
|
//\\\\ Identical except for impure pipe \\\\\\
|
||||||
|
// #docregion impure-component
|
||||||
|
@Component(
|
||||||
|
selector: 'flying-heroes-impure',
|
||||||
|
templateUrl: 'flying_heroes_component.html',
|
||||||
|
// #enddocregion impure-component
|
||||||
|
styles: const ['.flyers, .all {font-style: italic}'],
|
||||||
|
// #docregion impure-component
|
||||||
|
pipes: const [FlyingHeroesImpurePipe])
|
||||||
|
class FlyingHeroesImpureComponent extends FlyingHeroesComponent {
|
||||||
|
FlyingHeroesImpureComponent() {
|
||||||
|
title = 'Flying Heroes (impure pipe)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #docregion impure-component
|
|
@ -0,0 +1,38 @@
|
||||||
|
<!-- #docplaster-->
|
||||||
|
<!-- #docregion -->
|
||||||
|
<h2>{{title}}</h2>
|
||||||
|
<p>
|
||||||
|
<!-- #docregion template-1 -->
|
||||||
|
New hero:
|
||||||
|
<input type="text" #box
|
||||||
|
(keyup.enter)="addHero(box.value); box.value=''"
|
||||||
|
placeholder="hero name">
|
||||||
|
<!-- #enddocregion template-1 -->
|
||||||
|
<input id="can-fly" type="checkbox" [(ngModel)]="canFly"> can fly
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input id="mutate" type="checkbox" [(ngModel)]="mutate">Mutate array
|
||||||
|
<!-- #docregion template-1 -->
|
||||||
|
<button (click)="reset()">Reset</button>
|
||||||
|
<!-- #enddocregion template-1 -->
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>Heroes who fly (piped)</h4>
|
||||||
|
<div id="flyers">
|
||||||
|
<!-- #docregion template-flying-heroes -->
|
||||||
|
<div *ngFor="#hero of (heroes | flyingHeroes)">
|
||||||
|
{{hero.name}}
|
||||||
|
</div>
|
||||||
|
<!-- #enddocregion template-flying-heroes -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4>All Heroes (no pipe)</h4>
|
||||||
|
<div id="all">
|
||||||
|
<!-- #docregion template-1 -->
|
||||||
|
<!-- #docregion template-all-heroes -->
|
||||||
|
<div *ngFor="#hero of heroes">
|
||||||
|
{{hero.name}}
|
||||||
|
</div>
|
||||||
|
<!-- #enddocregion template-all-heroes -->
|
||||||
|
<!-- #enddocregion template-1 -->
|
||||||
|
</div>
|
|
@ -0,0 +1,19 @@
|
||||||
|
// #docregion
|
||||||
|
// #docregion pure
|
||||||
|
import 'package:angular2/angular2.dart';
|
||||||
|
import 'heroes.dart';
|
||||||
|
|
||||||
|
@Pipe(name: 'flyingHeroes')
|
||||||
|
class FlyingHeroesPipe extends PipeTransform {
|
||||||
|
// #docregion filter
|
||||||
|
List<Hero> transform(dynamic value, [List<dynamic> args]) =>
|
||||||
|
value.where((hero) => hero.canFly).toList();
|
||||||
|
// #enddocregion filter
|
||||||
|
}
|
||||||
|
// #enddocregion pure
|
||||||
|
|
||||||
|
// Identical except for the pure flag
|
||||||
|
// #docregion impure, pipe-decorator
|
||||||
|
@Pipe(name: 'flyingHeroes', pure: false)
|
||||||
|
// #enddocregion pipe-decorator
|
||||||
|
class FlyingHeroesImpurePipe extends FlyingHeroesPipe {}
|
|
@ -1,12 +1,32 @@
|
||||||
|
// #docregion
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:angular2/angular2.dart';
|
import 'package:angular2/angular2.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'hero-message', template: 'Message: {{delayedMessage | async}}')
|
selector: 'hero-message',
|
||||||
|
template: '''
|
||||||
|
<h2>Async Hero Message and AsyncPipe</h2>
|
||||||
|
<p>Message: {{ message | async }}</p>
|
||||||
|
<button (click)="resend()">Resend</button>
|
||||||
|
''')
|
||||||
class HeroAsyncMessageComponent {
|
class HeroAsyncMessageComponent {
|
||||||
Future<String> delayedMessage =
|
static const _msgEventDelay = const Duration(milliseconds: 500);
|
||||||
new Future.delayed(new Duration(milliseconds: 500), () {
|
|
||||||
return 'You are my Hero!';
|
Stream<String> message;
|
||||||
});
|
|
||||||
|
HeroAsyncMessageComponent() {
|
||||||
|
resend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resend() {
|
||||||
|
message =
|
||||||
|
new Stream.periodic(_msgEventDelay, (i) => _msgs[i]).take(_msgs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> _msgs = <String>[
|
||||||
|
'You are my hero!',
|
||||||
|
'You are the best hero!',
|
||||||
|
'Will you be my hero?'
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
// #docregion
|
||||||
import 'package:angular2/angular2.dart';
|
import 'package:angular2/angular2.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'hero-birthday',
|
selector: 'hero-birthday',
|
||||||
template: '''
|
// #docregion hero-birthday-template
|
||||||
<p>The hero's birthday is {{ birthday | date }}</p>
|
template: "<p>The hero's birthday is {{ birthday | date }}</p>"
|
||||||
''')
|
// #enddocregion hero-birthday-template
|
||||||
|
)
|
||||||
class HeroBirthday {
|
class HeroBirthday {
|
||||||
DateTime birthday = new DateTime(1988, 4, 15); // April 15, 1988
|
DateTime birthday = new DateTime(1988, 4, 15); // April 15, 1988
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
|
// #docregion
|
||||||
import 'package:angular2/angular2.dart';
|
import 'package:angular2/angular2.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'hero-birthday',
|
selector: 'hero-birthday2',
|
||||||
|
// #docregion template
|
||||||
template: '''
|
template: '''
|
||||||
<p>The hero's birthday is {{ birthday | date:format }}</p>
|
<p>The hero's birthday is {{ birthday | date:format }}</p>
|
||||||
<button (click)="toggleFormat()">Toggle Format</button>
|
<button (click)="toggleFormat()">Toggle Format</button>
|
||||||
''')
|
'''
|
||||||
class HeroBirthday {
|
// #enddocregion template
|
||||||
|
)
|
||||||
|
// #docregion class
|
||||||
|
class HeroBirthday2 {
|
||||||
DateTime birthday = new DateTime(1988, 4, 15); // April 15, 1988
|
DateTime birthday = new DateTime(1988, 4, 15); // April 15, 1988
|
||||||
|
|
||||||
bool toggle = true;
|
bool toggle = true;
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
// #docregion
|
||||||
import 'package:angular2/angular2.dart';
|
import 'package:angular2/angular2.dart';
|
||||||
|
|
||||||
import 'fetch_json_pipe.dart';
|
import 'fetch_json_pipe.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'hero-list',
|
selector: 'hero-list',
|
||||||
|
// #docregion template
|
||||||
template: '''
|
template: '''
|
||||||
<h4>Heroes from JSON File</h4>
|
<h4>Heroes from JSON File</h4>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
class Hero {
|
||||||
|
final String name;
|
||||||
|
final bool canFly;
|
||||||
|
|
||||||
|
const Hero(this.name, this.canFly);
|
||||||
|
|
||||||
|
String toString() => "$name (${canFly ? 'can fly' : 'doesn\'t fly'})";
|
||||||
|
}
|
||||||
|
|
||||||
|
const List<Hero> mockHeroes = const <Hero>[
|
||||||
|
const Hero("Windstorm", true),
|
||||||
|
const Hero("Bombasto", false),
|
||||||
|
const Hero("Magneto", false),
|
||||||
|
const Hero("Tornado", true),
|
||||||
|
];
|
|
@ -1,21 +0,0 @@
|
||||||
import 'package:angular2/angular2.dart';
|
|
||||||
|
|
||||||
import 'exponential_strength_pipe.dart';
|
|
||||||
|
|
||||||
@Component(
|
|
||||||
selector: 'power-boost-calculator',
|
|
||||||
template: '''
|
|
||||||
<h2>Power Boost Calculator</h2>
|
|
||||||
<div>Normal power: <input [(ngModel)]="power" /></div>
|
|
||||||
<div>Boost factor: <input [(ngModel)]="factor" /></div>
|
|
||||||
<p>
|
|
||||||
Super Hero Power: {{power | exponentialStrength: factor}}
|
|
||||||
</p>
|
|
||||||
''',
|
|
||||||
pipes: const [ExponentialStrengthPipe],
|
|
||||||
directives: const [COMMON_DIRECTIVES])
|
|
||||||
class PowerBoostCalculator {
|
|
||||||
// XXX: These should be ints, but that causes exceptions in checked mode.
|
|
||||||
String power = '5';
|
|
||||||
String factor = '1';
|
|
||||||
}
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
// #docregion
|
||||||
|
import 'package:angular2/angular2.dart';
|
||||||
|
import 'exponential_strength_pipe.dart';
|
||||||
|
|
||||||
|
@Component(
|
||||||
|
selector: 'power-boost-calculator',
|
||||||
|
template: '''
|
||||||
|
<h2>Power Boost Calculator</h2>
|
||||||
|
<div>Normal power: <input type="number" [(ngModel)]="power"/></div>
|
||||||
|
<div>Boost factor: <input type="number" [(ngModel)]="factor"/></div>
|
||||||
|
<p>
|
||||||
|
Super Hero Power: {{power | exponentialStrength: factor}}
|
||||||
|
</p>
|
||||||
|
''',
|
||||||
|
pipes: const [ExponentialStrengthPipe])
|
||||||
|
class PowerBoostCalculator {
|
||||||
|
num power = 5;
|
||||||
|
num factor = 1;
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
// #docregion
|
||||||
import 'package:angular2/angular2.dart';
|
import 'package:angular2/angular2.dart';
|
||||||
import 'exponential_strength_pipe.dart';
|
import 'exponential_strength_pipe.dart';
|
||||||
|
|
||||||
|
@ -5,9 +6,7 @@ import 'exponential_strength_pipe.dart';
|
||||||
selector: 'power-booster',
|
selector: 'power-booster',
|
||||||
template: '''
|
template: '''
|
||||||
<h2>Power Booster</h2>
|
<h2>Power Booster</h2>
|
||||||
<p>
|
<p>Super power boost: {{2 | exponentialStrength: 10}}</p>
|
||||||
Super power boost: {{2 | exponentialStrength: 10}}
|
|
||||||
</p>
|
|
||||||
''',
|
''',
|
||||||
pipes: const [ExponentialStrengthPipe])
|
pipes: const [ExponentialStrengthPipe])
|
||||||
class PowerBooster {}
|
class PowerBooster {}
|
|
@ -10,7 +10,7 @@ dependencies:
|
||||||
dart_to_js_script_rewriter: ^1.0.1
|
dart_to_js_script_rewriter: ^1.0.1
|
||||||
transformers:
|
transformers:
|
||||||
- angular2:
|
- angular2:
|
||||||
platform_directives: 'package:angular2/src/common/directives.dart#CORE_DIRECTIVES'
|
platform_directives: 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||||
platform_pipes: 'package:angular2/common.dart#COMMON_PIPES'
|
platform_pipes: 'package:angular2/common.dart#COMMON_PIPES'
|
||||||
entry_points: web/main.dart
|
entry_points: web/main.dart
|
||||||
- dart_to_js_script_rewriter
|
- dart_to_js_script_rewriter
|
||||||
|
|
|
@ -8,9 +8,6 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h4>Hero Birthday v.1</h4>
|
|
||||||
<hero-birthday>hero-birthday loading...</hero-birthday>
|
|
||||||
|
|
||||||
<my-app>my-app loading ...</my-app>
|
<my-app>my-app loading ...</my-app>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,6 @@ describe('Pipes', function () {
|
||||||
expect(element(by.css('hero-birthday p')).getText()).toEqual("The hero's birthday is Apr 15, 1988");
|
expect(element(by.css('hero-birthday p')).getText()).toEqual("The hero's birthday is Apr 15, 1988");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show an async hero message', function () {
|
|
||||||
expect(element.all(by.tagName('hero-message')).get(0).getText()).toContain('hero');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show 4 heroes', function () {
|
it('should show 4 heroes', function () {
|
||||||
expect(element.all(by.css('hero-list div')).count()).toEqual(4);
|
expect(element.all(by.css('hero-list div')).count()).toEqual(4);
|
||||||
});
|
});
|
||||||
|
@ -114,4 +110,8 @@ describe('Pipes', function () {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show an async hero message', function () {
|
||||||
|
expect(element.all(by.tagName('hero-message')).get(0).getText()).toContain('hero');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<a id="toc"></a>
|
<a id="toc"></a>
|
||||||
<h1>Pipes</h1>
|
<h1>Pipes</h1>
|
||||||
<a href="#happy-birthday1">Happy Birthday v.1</a><br>
|
<a href="#happy-birthday1">Happy Birthday v1</a><br>
|
||||||
<a href="#birthday-date-pipe">Birthday DatePipe</a><br>
|
<a href="#birthday-date-pipe">Birthday DatePipe</a><br>
|
||||||
<a href="#happy-birthday2">Happy Birthday v.2</a><br>
|
<a href="#happy-birthday2">Happy Birthday v2</a><br>
|
||||||
<a href="#birthday-pipe-chaining">Birthday Pipe Chaining</a><br>
|
<a href="#birthday-pipe-chaining">Birthday Pipe Chaining</a><br>
|
||||||
<a href="#power-booster">Power Booster custom pipe</a><br>
|
<a href="#power-booster">Power Booster custom pipe</a><br>
|
||||||
<a href="#power-boost-calc">Power Boost Calculator custom pipe with params</a><br>
|
<a href="#power-boost-calc">Power Boost Calculator custom pipe with params</a><br>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<a id="happy-birthday1"></a>
|
<a id="happy-birthday1"></a>
|
||||||
<h2>Hero Birthday v.1</h2>
|
<h2>Hero Birthday v1</h2>
|
||||||
<hero-birthday></hero-birthday>
|
<hero-birthday></hero-birthday>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<a id="happy-birthday2"></a>
|
<a id="happy-birthday2"></a>
|
||||||
<h2>Hero Birthday v.2</h2>
|
<h2>Hero Birthday v2</h2>
|
||||||
<hero-birthday2></hero-birthday2>
|
<hero-birthday2></hero-birthday2>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Http } from '@angular/http';
|
||||||
})
|
})
|
||||||
// #enddocregion pipe-metadata
|
// #enddocregion pipe-metadata
|
||||||
export class FetchJsonPipe implements PipeTransform{
|
export class FetchJsonPipe implements PipeTransform{
|
||||||
private fetched:any = null;
|
private fetchedJson: any = null;
|
||||||
private prevUrl = '';
|
private prevUrl = '';
|
||||||
|
|
||||||
constructor(private _http: Http) { }
|
constructor(private _http: Http) { }
|
||||||
|
@ -17,12 +17,12 @@ export class FetchJsonPipe implements PipeTransform{
|
||||||
transform(url: string): any {
|
transform(url: string): any {
|
||||||
if (url !== this.prevUrl) {
|
if (url !== this.prevUrl) {
|
||||||
this.prevUrl = url;
|
this.prevUrl = url;
|
||||||
this.fetched = null;
|
this.fetchedJson = null;
|
||||||
this._http.get(url)
|
this._http.get(url)
|
||||||
.map( result => result.json() )
|
.map( result => result.json() )
|
||||||
.subscribe( result => this.fetched = result );
|
.subscribe( result => this.fetchedJson = result );
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.fetched;
|
return this.fetchedJson;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,5 +35,4 @@ New hero:
|
||||||
</div>
|
</div>
|
||||||
<!-- #enddocregion template-all-heroes -->
|
<!-- #enddocregion template-all-heroes -->
|
||||||
<!-- #enddocregion template-1 -->
|
<!-- #enddocregion template-1 -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,16 +2,11 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Rx';
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
|
||||||
// Initial view: "Message: "
|
|
||||||
// After 500ms: Message: You are my Hero!"
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hero-message',
|
selector: 'hero-message',
|
||||||
template: `
|
template: `
|
||||||
<h2>Async Hero Message and AsyncPipe</h2>
|
<h2>Async Hero Message and AsyncPipe</h2>
|
||||||
|
|
||||||
<p>Message: {{ message$ | async }}</p>
|
<p>Message: {{ message$ | async }}</p>
|
||||||
|
|
||||||
<button (click)="resend()">Resend</button>`,
|
<button (click)="resend()">Resend</button>`,
|
||||||
})
|
})
|
||||||
export class HeroAsyncMessageComponent {
|
export class HeroAsyncMessageComponent {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// Version #1
|
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
|
|
||||||
|
@ -11,4 +10,3 @@ import { Component } from '@angular/core'
|
||||||
export class HeroBirthday {
|
export class HeroBirthday {
|
||||||
birthday = new Date(1988,3,15); // April 15, 1988
|
birthday = new Date(1988,3,15); // April 15, 1988
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// Version #2
|
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
|
|
||||||
|
@ -19,4 +18,3 @@ export class HeroBirthday2 {
|
||||||
get format() { return this.toggle ? 'shortDate' : 'fullDate'}
|
get format() { return this.toggle ? 'shortDate' : 'fullDate'}
|
||||||
toggleFormat() { this.toggle = !this.toggle; }
|
toggleFormat() { this.toggle = !this.toggle; }
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
|
||||||
|
|
|
@ -7,9 +7,7 @@ import { ExponentialStrengthPipe } from './exponential-strength.pipe';
|
||||||
selector: 'power-booster',
|
selector: 'power-booster',
|
||||||
template: `
|
template: `
|
||||||
<h2>Power Booster</h2>
|
<h2>Power Booster</h2>
|
||||||
<p>
|
<p>Super power boost: {{2 | exponentialStrength: 10}}</p>
|
||||||
Super power boost: {{2 | exponentialStrength: 10}}
|
|
||||||
</p>
|
|
||||||
`,
|
`,
|
||||||
pipes: [ExponentialStrengthPipe]
|
pipes: [ExponentialStrengthPipe]
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,6 +6,8 @@ include ../../../_includes/_util-fns
|
||||||
- var _array = 'list';
|
- var _array = 'list';
|
||||||
- var _an_array = 'a list';
|
- var _an_array = 'a list';
|
||||||
- var _priv = '_';
|
- var _priv = '_';
|
||||||
|
- var _Promise = 'Future';
|
||||||
|
- var _Observable = 'Stream';
|
||||||
|
|
||||||
mixin liveExampleLink(linkText, exampleUrlPartName)
|
mixin liveExampleLink(linkText, exampleUrlPartName)
|
||||||
a(href='https://angular-examples.github.io/#{exampleUrlPartName}')= linkText
|
a(href='https://angular-examples.github.io/#{exampleUrlPartName}')= linkText
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
|
extends ../../../ts/latest/guide/pipes.jade
|
||||||
|
|
||||||
|
block includes
|
||||||
include ../_util-fns
|
include ../_util-fns
|
||||||
|
|
||||||
|
block pure-change
|
||||||
:marked
|
:marked
|
||||||
We're working on the Dart version of this chapter.
|
Angular executes a *pure pipe* only when it detects a *pure change* to the input value.
|
||||||
In the meantime, please see these resources:
|
In Angular Dart, a *pure change* results only from a change in object reference
|
||||||
|
(given that [everything is an object in Dart](https://www.dartlang.org/docs/dart-up-and-running/ch02.html#important-concepts)).
|
||||||
* [Pipes](/docs/ts/latest/guide/pipes.html):
|
|
||||||
The TypeScript version of this chapter
|
|
||||||
|
|
||||||
* [Dart source code](https://github.com/angular/angular.io/tree/master/public/docs/_examples/pipes/dart):
|
|
||||||
A preliminary version of the example code that will appear in this chapter
|
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
|
block includes
|
||||||
include ../_util-fns
|
include ../_util-fns
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Every application starts out with what seems like a simple task: get data, transform them, and show them to users.
|
Every application starts out with what seems like a simple task: get data, transform them, and show them to users.
|
||||||
|
|
||||||
Getting data could be as simple as creating a local variable or as complex as streaming data over a Websocket.
|
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.
|
Once data arrive, we could push their raw `toString` values directly to the view.
|
||||||
That rarely makes for a good user experience.
|
That rarely makes for a good user experience.
|
||||||
Almost everyone prefers a simple birthday date
|
E.g., almost everyone prefers a simple birthday date like
|
||||||
(<span style="font-family:courier">April 15, 1988</span>) to the original raw string format
|
<samp>April 15, 1988</samp> to the original raw string format
|
||||||
( <span style="font-family:courier">Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)</span> ).
|
— <samp>Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)</samp>.
|
||||||
|
|
||||||
Clearly some values benefit from a bit of massage. We soon discover that we
|
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.
|
desire many of the same transformations repeatedly, both within and across many applications.
|
||||||
We almost think of them as styles.
|
We almost think of them as styles.
|
||||||
In fact, we'd like to apply them in our HTML templates as we do styles.
|
In fact, we'd like to apply them in our HTML templates as we do styles.
|
||||||
|
p.
|
||||||
Welcome, Angular pipes, the simple display-value transformations that we can declare in our HTML!
|
Welcome, Angular pipes, the simple display-value transformations that we can declare in our HTML!
|
||||||
|
Try the #[+liveExampleLink2('live example', 'pipes')].
|
||||||
[Live Example](/resources/live-examples/pipes/ts/plnkr.html).
|
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
|
@ -26,11 +26,14 @@ include ../_util-fns
|
||||||
A pipe takes in data as input and transforms it to a desired output.
|
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
|
We'll illustrate by transforming a component's birthday property into
|
||||||
a human-friendly date:
|
a human-friendly date:
|
||||||
|
|
||||||
+makeExample('pipes/ts/app/hero-birthday1.component.ts', null, 'app/hero-birthday1.component.ts')(format='.')
|
+makeExample('pipes/ts/app/hero-birthday1.component.ts', null, 'app/hero-birthday1.component.ts')(format='.')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Focus on the component's template.
|
Focus on the component's template.
|
||||||
|
|
||||||
+makeExample('pipes/ts/app/app.component.html', 'hero-birthday-template')(format=".")
|
+makeExample('pipes/ts/app/app.component.html', 'hero-birthday-template')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Inside the interpolation expression we flow the component's `birthday` value through the
|
Inside the interpolation expression we flow the component's `birthday` value through the
|
||||||
[pipe operator](./template-syntax.html#pipe) ( | ) to the [Date pipe](../api/common/DatePipe-class.html)
|
[pipe operator](./template-syntax.html#pipe) ( | ) to the [Date pipe](../api/common/DatePipe-class.html)
|
||||||
|
@ -39,26 +42,27 @@ include ../_util-fns
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Built-in pipes
|
## Built-in pipes
|
||||||
Angular comes with a stock set of pipes such as
|
Angular comes with a stock of pipes such as
|
||||||
`DatePipe`, `UpperCasePipe`, `LowerCasePipe`, `CurrencyPipe`, and `PercentPipe`.
|
`DatePipe`, `UpperCasePipe`, `LowerCasePipe`, `CurrencyPipe`, and `PercentPipe`.
|
||||||
They are all immediately available for use in any template.
|
They are all immediately available for use in any template.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Learn more about these and many other built-in pipes in the the [API Reference](../api/#!?apiFilter=pipe);
|
Learn more about these and many other built-in pipes in the the [API Reference](../api/#!?apiFilter=pipe);
|
||||||
filter for entries that include the word "pipe".
|
filter for entries that include the word "pipe".
|
||||||
|
|
||||||
Angular 2 doesn't have a `FilterPipe` or an `OrderByPipe` for reasons explained in an [appendix below](#no-filter-pipe)
|
Angular 2 doesn't have a `FilterPipe` or an `OrderByPipe` for reasons explained in an [appendix below](#no-filter-pipe).
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Parameterizing a Pipe
|
## Parameterizing a Pipe
|
||||||
A pipe may accept any number of optional parameters to fine-tune its output.
|
|
||||||
|
|
||||||
|
A pipe may accept any number of optional parameters to fine-tune its output.
|
||||||
We add parameters to a pipe by following the pipe name with a colon ( : ) and then the parameter value
|
We add parameters to a pipe by following the pipe name with a colon ( : ) and then the parameter value
|
||||||
(e.g., `currency:'EUR'`). If our pipe accepts multiple parameters, we separate the values with colons (e.g. `slice:1:5`)
|
(e.g., `currency:'EUR'`). If our pipe accepts multiple parameters, we separate the values with colons (e.g. `slice:1:5`)
|
||||||
|
|
||||||
We'll modify our birthday template to give the date pipe a format parameter.
|
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>**.
|
After formatting the hero's April 15th birthday, it should render as **<samp>04/15/88</samp>**.
|
||||||
|
|
||||||
+makeExample('pipes/ts/app/app.component.html', 'format-birthday')(format=".")
|
+makeExample('pipes/ts/app/app.component.html', 'format-birthday')(format=".")
|
||||||
|
|
||||||
|
@ -70,16 +74,20 @@ include ../_util-fns
|
||||||
|
|
||||||
Let's write a second component that *binds* the pipe's format parameter
|
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:
|
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=".")
|
+makeExample('pipes/ts/app/hero-birthday2.component.ts', 'template', 'app/hero-birthday2.component.ts (template)')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We also added a button to the template and bound its click event to the component's `toggleFormat` method.
|
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
|
That method toggles the component's `format` property between a short form
|
||||||
('shortDate') and a longer form ('fullDate').
|
(`'shortDate'`) and a longer form (`'fullDate'`).
|
||||||
|
|
||||||
+makeExample('pipes/ts/app/hero-birthday2.component.ts', 'class', 'app/hero-birthday2.component.ts (class)')(format='.')
|
+makeExample('pipes/ts/app/hero-birthday2.component.ts', 'class', 'app/hero-birthday2.component.ts (class)')(format='.')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
As we click the button, the displayed date alternates between
|
As we click the button, the displayed date alternates between
|
||||||
"**<span style="font-family:courier">04/15/1988</span>**" and
|
"**<samp>04/15/1988</samp>**" and
|
||||||
"**<span style="font-family:courier">Friday, April 15, 1988</span>**".
|
"**<samp>Friday, April 15, 1988</samp>**".
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle")
|
img(src='/resources/images/devguide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle")
|
||||||
|
@ -88,37 +96,32 @@ figure.image-display
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Learn more about the `DatePipes` format options in the [API Docs](../api/common/DatePipe-class.html).
|
Learn more about the `DatePipes` format options in the [API Docs](../api/common/DatePipe-class.html).
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
## Chaining pipes
|
## Chaining pipes
|
||||||
|
|
||||||
We can chain pipes together in potentially useful combinations.
|
We can chain pipes together in potentially useful combinations.
|
||||||
In the following example, we chain the birthday to the `DatePipe` and on to the `UpperCasePipe`
|
In the following example, we chain the birthday to the `DatePipe` and on to the `UpperCasePipe`
|
||||||
so we can display the birthday in uppercase. The following birthday displays as
|
so we can display the birthday in uppercase. The following birthday displays as
|
||||||
**<span style="font-family:courier">APR 15, 1988</span>**
|
**<samp>APR 15, 1988</samp>**
|
||||||
|
|
||||||
+makeExample('pipes/ts/app/app.component.html', 'chained-birthday')(format=".")
|
+makeExample('pipes/ts/app/app.component.html', 'chained-birthday')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
If we pass a parameter to a filter, we have to add parentheses
|
This example — which displays **<samp>FRIDAY, APRIL 15, 1988</samp>** —
|
||||||
to help the template compiler with the evaluation order.
|
chains the same pipes as above, but passes in a parameter to `date` as well.
|
||||||
The following example displays
|
|
||||||
**<span style="font-family:courier">FRIDAY, APRIL 15, 1988</span>**
|
|
||||||
|
|
||||||
+makeExample('pipes/ts/app/app.component.html', 'chained-parameter-birthday')(format=".")
|
+makeExample('pipes/ts/app/app.component.html', 'chained-parameter-birthday')(format=".")
|
||||||
|
|
||||||
:marked
|
|
||||||
We can add parentheses to alter the evaluation order or
|
|
||||||
to provide extra clarity:
|
|
||||||
+makeExample('pipes/ts/app/app.component.html', 'chained-parameter-birthday-parens')(format=".")
|
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Custom Pipes
|
## Custom Pipes
|
||||||
|
|
||||||
We can write our own custom pipes.
|
We can write our own custom pipes.
|
||||||
|
|
||||||
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
|
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
|
||||||
|
|
||||||
+makeExample('pipes/ts/app/exponential-strength.pipe.ts', null, 'app/exponential-strength.pipe.ts')(format=".")
|
+makeExample('pipes/ts/app/exponential-strength.pipe.ts', null, 'app/exponential-strength.pipe.ts')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
This pipe definition reveals several key points
|
This pipe definition reveals several key points
|
||||||
|
|
||||||
|
@ -128,13 +131,13 @@ figure.image-display
|
||||||
accepts an input value followed by optional parameters and returns the transformed value.
|
accepts an input value followed by optional parameters and returns the transformed value.
|
||||||
|
|
||||||
* There will be one additional argument to the `transform` method for each parameter passed to the pipe.
|
* There will be one additional argument to the `transform` method for each parameter passed to the pipe.
|
||||||
Our pipe has one such parameter: The `exponent`.
|
Our pipe has one such parameter: the `exponent`.
|
||||||
|
|
||||||
* We tell Angular that this is a pipe by applying the
|
* We tell Angular that this is a pipe by applying the
|
||||||
`@Pipe` decorator which we import from the core Angular library.
|
`@Pipe` #{_decorator} which we import from the core Angular library.
|
||||||
|
|
||||||
* The `@Pipe` decorator takes an object with a name property whose value is the
|
* The `@Pipe` #{_decorator} allows us to define the
|
||||||
pipe name that we'll use within a template expression. It must be a valid JavaScript identifier.
|
pipe name that we'll use within template expressions. It must be a valid JavaScript identifier.
|
||||||
Our pipe's name is `exponentialStrength`.
|
Our pipe's name is `exponentialStrength`.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
|
@ -155,20 +158,23 @@ figure.image-display
|
||||||
Two things to note:
|
Two things to note:
|
||||||
1. We use our custom pipe the same way we use the built-in pipes.
|
1. We use our custom pipe the same way we use the built-in pipes.
|
||||||
|
|
||||||
1. We must list our pipe in the `pipes` array of the `@Component` decorator.
|
1. We must include our pipe in the `pipes` #{_array} of the `@Component` #{_decorator}.
|
||||||
|
|
||||||
.callout.is-helpful
|
.callout.is-helpful
|
||||||
header Remember the pipes array!
|
header Remember the pipes #{_array}!
|
||||||
:marked
|
:marked
|
||||||
Angular reports an error if we neglect to list our custom pipe.
|
Angular reports an error if we neglect to list our custom pipe.
|
||||||
We didn't list the `DatePipe` in our previous example because all
|
We didn't list the `DatePipe` in our previous example because all
|
||||||
Angular built-in pipes are pre-registered.
|
Angular built-in pipes are pre-registered.
|
||||||
Custom pipes must be registered manually.
|
Custom pipes must be registered manually.
|
||||||
:marked
|
|
||||||
If we try the [live code](/resources/live-examples/pipes/ts/plnkr.html) example,
|
p.
|
||||||
|
If we try the #[+liveExampleLink('live code', 'pipes')] example,
|
||||||
we can probe its behavior by changing the value and the optional exponent in the template.
|
we can probe its behavior by changing the value and the optional exponent in the template.
|
||||||
|
|
||||||
|
:marked
|
||||||
## Power Boost Calculator (extra-credit)
|
## Power Boost Calculator (extra-credit)
|
||||||
|
|
||||||
It's not much fun updating the template to test our custom pipe.
|
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
|
We could upgrade the example to a "Power Boost Calculator" that combines
|
||||||
our pipe and two-way data binding with `ngModel`.
|
our pipe and two-way data binding with `ngModel`.
|
||||||
|
@ -179,29 +185,34 @@ figure.image-display
|
||||||
img(src='/resources/images/devguide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator")
|
img(src='/resources/images/devguide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator")
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
a(id="change-detection")
|
a#change-detection
|
||||||
:marked
|
:marked
|
||||||
## Pipes and Change Detection
|
## Pipes and Change Detection
|
||||||
|
|
||||||
Angular looks for changes to data-bound values through a *change detection* process that runs after every JavaScript event:
|
Angular looks for changes to data-bound values through a *change detection* process that runs after every JavaScript event:
|
||||||
every keystroke, mouse move, timer tick, and server response. It could be expensive.
|
every keystroke, mouse move, timer tick, and server response. This could be expensive.
|
||||||
Angular strives to lower the cost whenever possible and appropriate.
|
Angular strives to lower the cost whenever possible and appropriate.
|
||||||
|
|
||||||
Angular picks a simpler, faster change detection algorithm when we use a pipe. Let's see how.
|
Angular picks a simpler, faster change detection algorithm when we use a pipe. Let's see how.
|
||||||
|
|
||||||
### No pipe
|
### No pipe
|
||||||
|
|
||||||
The component in our next example uses the default, aggressive change detection strategy to monitor and update
|
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:
|
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='.')
|
+makeExample('pipes/ts/app/flying-heroes.component.html', 'template-1', 'app/flying-heroes.component.html (v1)')(format='.')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
The companion component class provides heroes, pushes new heroes into the array, and can reset the array.
|
The companion component class provides heroes, adds 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='.')
|
+makeExample('pipes/ts/app/flying-heroes.component.ts', 'v1', 'app/flying-heroes.component.ts (v1)')(format='.')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We can add a new hero and Angular updates the display when we do.
|
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.
|
The `reset` button replaces `heroes` with a new #{_array} of the original heroes and Angular updates the display when we do.
|
||||||
If we added the ability to remove or change a hero, Angular would detect those changes too and update the display again.
|
If we added the ability to remove or change a hero, Angular would detect those changes too and update the display as well.
|
||||||
add or remove heroes. It updates the display when we modify a hero.
|
|
||||||
|
|
||||||
### Flying Heroes pipe
|
### Flying Heroes pipe
|
||||||
|
|
||||||
Let's add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly.
|
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='.')
|
+makeExample('pipes/ts/app/flying-heroes.component.html', 'template-flying-heroes', 'app/flying-heroes.component.html (flyers)')(format='.')
|
||||||
:marked
|
:marked
|
||||||
|
@ -218,25 +229,24 @@ a(id="change-detection")
|
||||||
Look at how we're adding a new hero:
|
Look at how we're adding a new hero:
|
||||||
+makeExample('pipes/ts/app/flying-heroes.component.ts', 'push')(format='.')
|
+makeExample('pipes/ts/app/flying-heroes.component.ts', 'push')(format='.')
|
||||||
:marked
|
:marked
|
||||||
We're pushing the new hero into the `heroes` array. The object reference to the array hasn't changed.
|
We're adding the new hero into the `heroes` #{_array}. The reference to the #{_array} hasn't changed.
|
||||||
It's the same array. That's all Angular cares about. From its perspective, *same array, no change, no display update*.
|
It's the same #{_array}. That's all Angular cares about. From its perspective, *same #{_array}, no change, no display update*.
|
||||||
|
|
||||||
We can fix that. Let's use `concat` to create a new array with the new hero appended and assign that to `heroes`.
|
We can fix that. Let's create a new #{_array} with the new hero appended and assign that to `heroes`.
|
||||||
This time Angular detects that the array object reference has changed.
|
This time Angular detects that the #{_array} reference has changed.
|
||||||
It executes the pipe and updates the display with the new array which includes the new flying hero.
|
It executes the pipe and updates the display with the new #{_array} which includes the new flying hero.
|
||||||
|
|
||||||
*If we **mutate** the array, no pipe and no display update;
|
|
||||||
if we **replace** the array, the pipe executes and the display updates*.
|
|
||||||
|
|
||||||
|
*If we **mutate** the #{_array}, no pipe is invoked and no display updated;
|
||||||
|
if we **replace** the #{_array}, then the pipe executes and the display is updated*.
|
||||||
The *Flying Heroes* in the [live example](/resources/live-examples/pipes/ts/plnkr.html) extends the
|
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.
|
code with checkbox switches and additional displays to help us experience these effects.
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes")
|
img(src='/resources/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Replacing the array is an efficient way to signal to Angular that it should update the display.
|
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.
|
When do we replace the #{_array}? When the data change.
|
||||||
|
|
||||||
That's an easy rule to follow in *this toy* example
|
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.
|
where the only way to change the data is by adding a new hero.
|
||||||
|
|
||||||
|
@ -244,7 +254,6 @@ figure.image-display
|
||||||
especially in applications that mutate data in many ways,
|
especially in applications that mutate data in many ways,
|
||||||
perhaps in application locations far away.
|
perhaps in application locations far away.
|
||||||
A component is such an application usually can't know about those changes.
|
A component is such an application usually can't know about those changes.
|
||||||
|
|
||||||
Moreover, it's unwise to distort our component design to accommodate a pipe.
|
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.
|
We strive as much as possible to keep the component class independent of the HTML.
|
||||||
The component should be unaware of pipes.
|
The component should be unaware of pipes.
|
||||||
|
@ -256,29 +265,31 @@ figure.image-display
|
||||||
## Pure and Impure Pipes
|
## Pure and Impure Pipes
|
||||||
|
|
||||||
There are two categories of pipes: **pure** and **impure**.
|
There are two categories of pipes: **pure** and **impure**.
|
||||||
|
|
||||||
Pipes are pure by default. Every pipe we've seen so far has been pure.
|
Pipes are pure by default. Every pipe we've seen so far has been pure.
|
||||||
|
|
||||||
We make a pipe impure by setting its pure flag to false. We could make the `FlyingHeroesPipe`
|
We make a pipe impure by setting its pure flag to false. We could make the `FlyingHeroesPipe`
|
||||||
impure with a flip of the switch:
|
impure like this:
|
||||||
|
|
||||||
+makeExample('pipes/ts/app/flying-heroes.pipe.ts', 'pipe-decorator')(format='.')
|
+makeExample('pipes/ts/app/flying-heroes.pipe.ts', 'pipe-decorator')(format='.')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Before we do that, let's understand the difference between *pure* and *impure*, starting with a *pure* pipe.
|
Before we do that, let's understand the difference between *pure* and *impure*, starting with a *pure* pipe.
|
||||||
|
|
||||||
### Pure pipes
|
### Pure pipes
|
||||||
|
|
||||||
|
block pure-change
|
||||||
|
:marked
|
||||||
Angular executes a *pure pipe* only when it detects a *pure change* to the input value.
|
Angular executes a *pure pipe* only when it detects a *pure change* to the input value.
|
||||||
|
A ***pure change*** is *either* a change to a primitive input value (`String`, `Number`, `Boolean`, `Symbol`)
|
||||||
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`).
|
*or* a changed object reference (`Date`, `Array`, `Function`, `Object`).
|
||||||
|
|
||||||
Angular ignores changes *within* the object itself.
|
:marked
|
||||||
It won't call a pure pipe if we change the input month, add to the input array, or update an input object property.
|
Angular ignores changes *within* (composite) objects.
|
||||||
|
It won't call a pure pipe if we change an input month, add to an input #{_array}, or update an input object property.
|
||||||
|
|
||||||
This may seem restrictive but is is also fast.
|
This may seem restrictive but is is also fast.
|
||||||
An object reference check is fast ... much faster than a deep check for differences.
|
An object reference check is fast — much faster than a deep check for
|
||||||
... so Angular can quickly determine if it can skip both the pipe execution and a screen update.
|
differences — so Angular can quickly determine if it can skip both the
|
||||||
|
pipe execution and a view update.
|
||||||
|
|
||||||
For this reason, we prefer a pure pipe if we can live with the change detection strategy.
|
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.
|
When we can't, we *may* turn to the impure pipe.
|
||||||
|
@ -288,8 +299,10 @@ figure.image-display
|
||||||
Or we might not use a pipe at all.
|
Or we might not use a pipe at all.
|
||||||
It may be better to pursue the pipe's purpose with a property of the component,
|
It may be better to pursue the pipe's purpose with a property of the component,
|
||||||
a point we take up later.
|
a point we take up later.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
### Impure pipes
|
### Impure pipes
|
||||||
|
|
||||||
Angular executes an *impure pipe* during *every* component change detection cycle.
|
Angular executes an *impure pipe* during *every* component change detection cycle.
|
||||||
An impure pipe will be called a lot, as often as every keystroke or mouse-move.
|
An impure pipe will be called a lot, as often as every keystroke or mouse-move.
|
||||||
|
|
||||||
|
@ -305,6 +318,7 @@ figure.image-display
|
||||||
'pipes/ts/app/flying-heroes.pipe.ts, pipes/ts/app/flying-heroes.pipe.ts',
|
'pipes/ts/app/flying-heroes.pipe.ts, pipes/ts/app/flying-heroes.pipe.ts',
|
||||||
'impure, pure',
|
'impure, pure',
|
||||||
'FlyingHeroesImpurePipe, FlyingHeroesPipe')(format='.')
|
'FlyingHeroesImpurePipe, FlyingHeroesPipe')(format='.')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We inherit from `FlyingHeroesPipe` to prove the point that nothing changed internally.
|
We inherit from `FlyingHeroesPipe` to prove the point that nothing changed internally.
|
||||||
The only difference is the `pure` flag in the pipe metadata.
|
The only difference is the `pure` flag in the pipe metadata.
|
||||||
|
@ -317,19 +331,23 @@ figure.image-display
|
||||||
:marked
|
:marked
|
||||||
The only substantive change is the pipe.
|
The only substantive change is the pipe.
|
||||||
We can confirm in the [live example](/resources/live-examples/pipes/ts/plnkr.html)
|
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.
|
that the *flying heroes* display updates as we enter new heroes even when we mutate the `heroes` #{_array}.
|
||||||
|
- var _dollar = _docsFor === 'ts' ? '$' : '';
|
||||||
|
:marked
|
||||||
<a id="async-pipe"></a>
|
<a id="async-pipe"></a>
|
||||||
### The impure *AsyncPipe*
|
### The impure *AsyncPipe*
|
||||||
|
|
||||||
The Angular `AsyncPipe` is an interesting example of an impure pipe.
|
The Angular `AsyncPipe` is an interesting example of an impure pipe.
|
||||||
The `AsyncPipe` accepts a `Promise` or `Observable` as input
|
The `AsyncPipe` accepts a `#{_Promise}` or `#{_Observable}` as input
|
||||||
and subscribes to the input automatically, eventually returning the emitted value(s).
|
and subscribes to the input automatically, eventually returning the emitted value(s).
|
||||||
|
|
||||||
It is also stateful.
|
It is also stateful.
|
||||||
The pipe maintains a subscription to the input `Observable` and
|
The pipe maintains a subscription to the input `#{_Observable}` and
|
||||||
keeps delivering values from that `Observable` as they arrive.
|
keeps delivering values from that `#{_Observable}` as they arrive.
|
||||||
|
|
||||||
|
In this next example, we bind an `#{_Observable}` of message strings
|
||||||
|
(`message#{_dollar}`) to a view with the `async` pipe.
|
||||||
|
|
||||||
In this next example, we bind an `Observable` of message strings (`messages$`) to a view with the `async` pipe.
|
|
||||||
+makeExample('pipes/ts/app/hero-async-message.component.ts', null, 'app/hero-async-message.component.ts')
|
+makeExample('pipes/ts/app/hero-async-message.component.ts', null, 'app/hero-async-message.component.ts')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
|
@ -341,12 +359,10 @@ figure.image-display
|
||||||
|
|
||||||
### An impure caching pipe
|
### An impure caching pipe
|
||||||
|
|
||||||
Let's write one more impure pipe, a pipe that makes an http request to the server.
|
Let's write one more impure pipe, a pipe that makes an HTTP request to the server.
|
||||||
|
|
||||||
Normally, that's a horrible idea.
|
Normally, that's a horrible idea.
|
||||||
It's probably a horrible idea no matter what we do.
|
It's probably a horrible idea no matter what we do.
|
||||||
We're forging ahead anyway to make a point.
|
We're forging ahead anyway to make a point.
|
||||||
|
|
||||||
Remember that impure pipes are called every few microseconds.
|
Remember that impure pipes are called every few microseconds.
|
||||||
If we're not careful, this pipe will punish the server with requests.
|
If we're not careful, this pipe will punish the server with requests.
|
||||||
|
|
||||||
|
@ -367,10 +383,13 @@ figure.image-display
|
||||||
the nework tab in the browser developer tools confirms that there is only one request for the file.
|
the nework tab in the browser developer tools confirms that there is only one request for the file.
|
||||||
|
|
||||||
The component renders like this:
|
The component renders like this:
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/pipes/hero-list.png' alt="Hero List")
|
img(src='/resources/images/devguide/pipes/hero-list.png' alt="Hero List")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
### *JsonPipe*
|
### *JsonPipe*
|
||||||
|
|
||||||
The second binding involving the `FetchPipe` uses more pipe chaining.
|
The second binding involving the `FetchPipe` uses more pipe chaining.
|
||||||
We take the same fetched results displayed in the first binding
|
We take the same fetched results displayed in the first binding
|
||||||
and display them again, this time in JSON format by chaining through to the built-in `JsonPipe`.
|
and display them again, this time in JSON format by chaining through to the built-in `JsonPipe`.
|
||||||
|
@ -381,8 +400,10 @@ figure.image-display
|
||||||
The [JsonPipe](../api/common/JsonPipe-class.html)
|
The [JsonPipe](../api/common/JsonPipe-class.html)
|
||||||
provides an easy way to diagnosis a mysteriously failing data binding or
|
provides an easy way to diagnosis a mysteriously failing data binding or
|
||||||
inspect an object for future binding.
|
inspect an object for future binding.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Here's the complete component implementation:
|
Here's the complete component implementation:
|
||||||
|
|
||||||
+makeExample('pipes/ts/app/hero-list.component.ts', null, 'app/hero-list.component.ts')
|
+makeExample('pipes/ts/app/hero-list.component.ts', null, 'app/hero-list.component.ts')
|
||||||
|
|
||||||
a(id="pure-pipe-pure-fn")
|
a(id="pure-pipe-pure-fn")
|
||||||
|
@ -390,7 +411,6 @@ a(id="pure-pipe-pure-fn")
|
||||||
### Pure pipes and pure functions
|
### Pure pipes and pure functions
|
||||||
|
|
||||||
A pure pipe uses pure functions.
|
A pure pipe uses pure functions.
|
||||||
|
|
||||||
Pure functions process inputs and return values without detectable side-effects.
|
Pure functions process inputs and return values without detectable side-effects.
|
||||||
Given the same input they should always return the same output.
|
Given the same input they should always return the same output.
|
||||||
|
|
||||||
|
@ -398,7 +418,6 @@ a(id="pure-pipe-pure-fn")
|
||||||
The built-in `DatePipe` is a pure pipe with a pure function implementation.
|
The built-in `DatePipe` is a pure pipe with a pure function implementation.
|
||||||
So is our `ExponentialStrengthPipe`.
|
So is our `ExponentialStrengthPipe`.
|
||||||
So is our `FlyingHeroesPipe`.
|
So is our `FlyingHeroesPipe`.
|
||||||
|
|
||||||
A few steps back we reviewed the `FlyingHeroesImpurePipe` — *an impure pipe with a pure function*.
|
A few steps back we reviewed the `FlyingHeroesImpurePipe` — *an impure pipe with a pure function*.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
@ -419,14 +438,14 @@ a(id="no-filter-pipe")
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## No *FilterPipe* or *OrderByPipe*
|
## No *FilterPipe* or *OrderByPipe*
|
||||||
|
|
||||||
Angular does not ship with pipes for filtering or sorting lists.
|
Angular does not ship with pipes for filtering or sorting lists.
|
||||||
Developers familiar with Angular 1 know these as `filter` and `orderBy`.
|
Developers familiar with Angular 1 know these as `filter` and `orderBy`.
|
||||||
There are no equivalents in Angular 2.
|
There are no equivalents in Angular 2.
|
||||||
|
|
||||||
This is not an oversight. Angular 2 is unlikely to offer such pipes because
|
This is not an oversight. Angular 2 is unlikely to offer such pipes because
|
||||||
(a) they perform poorly and (b) they prevent aggressive minification.
|
(a) they perform poorly and (b) they prevent aggressive minification.
|
||||||
|
Both `filter` and `orderBy` require parameters that reference object properties.
|
||||||
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
|
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.
|
Angular calls impure pipes in almost every change detection cycle.
|
||||||
|
|
||||||
|
@ -437,8 +456,8 @@ a(id="no-filter-pipe")
|
||||||
by offering `filter` and `orderBy` in the first place.
|
by offering `filter` and `orderBy` in the first place.
|
||||||
|
|
||||||
The minification hazard is also compelling if less obvious. Imagine a sorting pipe applied to a list of heroes.
|
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:
|
We might sort the list by hero `name` and `planet` of origin properties something like this:
|
||||||
code-example(format="." language="html")
|
code-example(language="html")
|
||||||
<!-- NOT REAL CODE! -->
|
<!-- NOT REAL CODE! -->
|
||||||
<div *ngFor="let hero of heroes | orderBy:'name,planet'"></div>
|
<div *ngFor="let hero of heroes | orderBy:'name,planet'"></div>
|
||||||
:marked
|
:marked
|
||||||
|
|
Loading…
Reference in New Issue