From 5b27178083a249eaff9114d515f4219b65dcdd6b Mon Sep 17 00:00:00 2001
From: joeeames
Date: Wed, 13 Jan 2016 15:00:43 -0700
Subject: [PATCH] docs(pipes) clarify impure pipes; explain missing
filter/orderBy pipes adds flying-heroes example and its test adds random-pipe
example
---
public/docs/_examples/pipes/e2e-spec.js | 61 +++-
.../_examples/pipes/ts/app/app.component.html | 73 +++-
.../_examples/pipes/ts/app/app.component.ts | 16 +-
.../pipes/ts/app/exponential-strength.pipe.ts | 6 +-
.../_examples/pipes/ts/app/fetch-json.pipe.ts | 24 +-
.../pipes/ts/app/flying-heroes.component.html | 39 +++
.../pipes/ts/app/flying-heroes.component.ts | 64 ++++
.../pipes/ts/app/flying-heroes.pipe.ts | 25 ++
.../ts/app/hero-async-message.component.ts | 32 +-
.../pipes/ts/app/hero-birthday2.component.ts | 8 +-
.../pipes/ts/app/hero-list.component.ts | 6 +-
public/docs/_examples/pipes/ts/app/heroes.ts | 7 +
public/docs/_examples/pipes/ts/app/main.ts | 4 +-
.../pipes/ts/app/random-pipe.component.ts | 24 ++
.../pipes/ts/app/window.extension.d.ts | 3 -
public/docs/_examples/pipes/ts/heroes.json | 8 +-
public/docs/_examples/pipes/ts/index.html | 8 +-
public/docs/_examples/pipes/ts/plnkr.json | 3 +-
.../docs/ts/latest/guide/lifecycle-hooks.jade | 14 +-
public/docs/ts/latest/guide/pipes.jade | 318 +++++++++++++++---
.../devguide/pipes/flying-heroes-anim.gif | Bin 0 -> 54064 bytes
21 files changed, 615 insertions(+), 128 deletions(-)
create mode 100644 public/docs/_examples/pipes/ts/app/flying-heroes.component.html
create mode 100644 public/docs/_examples/pipes/ts/app/flying-heroes.component.ts
create mode 100644 public/docs/_examples/pipes/ts/app/flying-heroes.pipe.ts
create mode 100644 public/docs/_examples/pipes/ts/app/heroes.ts
create mode 100644 public/docs/_examples/pipes/ts/app/random-pipe.component.ts
delete mode 100644 public/docs/_examples/pipes/ts/app/window.extension.d.ts
create mode 100644 public/resources/images/devguide/pipes/flying-heroes-anim.gif
diff --git a/public/docs/_examples/pipes/e2e-spec.js b/public/docs/_examples/pipes/e2e-spec.js
index a8637947d0..4f07ce7137 100644
--- a/public/docs/_examples/pipes/e2e-spec.js
+++ b/public/docs/_examples/pipes/e2e-spec.js
@@ -5,19 +5,19 @@ describe('Pipes', function () {
});
it('should open correctly', function () {
- expect(element.all(by.css('h4')).get(0).getText()).toEqual('Hero Birthday v.1');
- expect(element(by.css('body > hero-birthday p')).getText()).toEqual("The hero's birthday is Apr 15, 1988");
+ expect(element.all(by.tagName('h1')).get(0).getText()).toEqual('Pipes');
+ expect(element(by.css('hero-birthday p')).getText()).toEqual("The hero's birthday is Apr 15, 1988");
});
- it('should show delayed message', function () {
- expect(element.all(by.css('hero-message')).get(0).getText()).toEqual('Message: You are my Hero!');
+ 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 () {
expect(element.all(by.css('hero-list div')).count()).toEqual(4);
});
- it('should show 4 heroes in json', function () {
+ it('should show a familiar hero in json', function () {
expect(element(by.cssContainingText('hero-list p', 'Heroes as JSON')).getText()).toContain('Bombasto');
});
@@ -27,9 +27,9 @@ describe('Pipes', function () {
});
it('should be able to toggle birthday formats', function () {
- var birthDayEle = element(by.css('my-app > hero-birthday > p'));
+ var birthDayEle = element(by.css('hero-birthday2 > p'));
expect(birthDayEle.getText()).toEqual("The hero's birthday is 4/15/1988");
- var buttonEle = element(by.cssContainingText('my-app > hero-birthday > button', "Toggle Format"));
+ var buttonEle = element(by.cssContainingText('hero-birthday2 > button', "Toggle Format"));
expect(buttonEle.isDisplayed()).toBe(true);
buttonEle.click().then(function() {
expect(birthDayEle.getText()).toEqual("The hero's birthday is Friday, April 15, 1988");
@@ -66,5 +66,52 @@ describe('Pipes', function () {
});
+ it('should support flying heroes (pure) ', function () {
+ var nameEle = element(by.css('flying-heroes input[type="text"]'));
+ var canFlyCheckEle = element(by.css('flying-heroes #can-fly'));
+ var mutateCheckEle = element(by.css('flying-heroes #mutate'));
+ var resetEle = element(by.css('flying-heroes button'));
+ var flyingHeroesEle = element.all(by.css('flying-heroes #flyers div'));
+
+ expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"');
+ expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array');
+ expect(flyingHeroesEle.count()).toEqual(2, 'only two of the original heroes can fly');
+
+ return sendKeys(nameEle, "test1\n")
+ .then(function(){
+ expect(flyingHeroesEle.count()).toEqual(2, 'no change while mutating array');
+ return mutateCheckEle.click();
+ })
+ .then(function() {
+ return sendKeys(nameEle, "test2\n");
+ })
+ .then(function() {
+ expect(flyingHeroesEle.count()).toEqual(4, 'not mutating; should see both adds');
+ expect(flyingHeroesEle.get(2).getText()).toContain('test1');
+ expect(flyingHeroesEle.get(3).getText()).toContain('test2');
+ return resetEle.click();
+ })
+ .then(function() {
+ expect(flyingHeroesEle.count()).toEqual(2, 'reset should restore orginal flying heroes');
+ })
+ });
+
+
+ it('should support flying heroes (impure) ', function () {
+ var nameEle = element(by.css('flying-heroes-impure input[type="text"]'));
+ var canFlyCheckEle = element(by.css('flying-heroes-impure #can-fly'));
+ var mutateCheckEle = element(by.css('flying-heroes-impure #mutate'));
+ var resetEle = element(by.css('flying-heroes-impure button'));
+ var flyingHeroesEle = element.all(by.css('flying-heroes-impure #flyers div'));
+
+ expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"');
+ expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array');
+ expect(flyingHeroesEle.count()).toEqual(2, 'only two of the original heroes can fly');
+
+ return sendKeys(nameEle, "test1\n")
+ .then(function(){
+ expect(flyingHeroesEle.count()).toEqual(3, 'new flying hero should show in mutating array');
+ })
+ });
});
diff --git a/public/docs/_examples/pipes/ts/app/app.component.html b/public/docs/_examples/pipes/ts/app/app.component.html
index 7cbc969c84..f86ef24338 100644
--- a/public/docs/_examples/pipes/ts/app/app.component.html
+++ b/public/docs/_examples/pipes/ts/app/app.component.html
@@ -1,11 +1,26 @@
-
-
-
+
+Pipes
+Happy Birthday v.1
+Birthday DatePipe
+Happy Birthday v.2
+Birthday Pipe Chaining
+Power Booster custom pipe
+Power Boost Calculator custom pipe with params
+Flying Heroes filter pipe (pure)
+Flying Heroes filter pipe (impure)
+Async Hero Message and AsyncPipe
+Hero List with caching FetchJsonPipe
+Random Pipe (pure pipe / impure function)
+
-
+
+Hero Birthday v.1
+
+
+Birthday DatePipe
The hero's birthday is {{ birthday | date }}
@@ -15,33 +30,59 @@
-Hero Birthday v.2
-loading...
+
+Hero Birthday v.2
+
+
-
-
-
+
+Birthday Pipe Chaining
+
The chained hero's birthday is
{{ birthday | date | uppercase}}
-
+
-
+
The chained hero's birthday is
{{ birthday | date:'fullDate' | uppercase}}
-
-
+
+
The chained hero's birthday is
{{ ( birthday | date:'fullDate' ) | uppercase}}
-
+
-loading...
+
+
-loading ..
+
+loading
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/pipes/ts/app/app.component.ts b/public/docs/_examples/pipes/ts/app/app.component.ts
index db6a0c7a81..015715cc7e 100644
--- a/public/docs/_examples/pipes/ts/app/app.component.ts
+++ b/public/docs/_examples/pipes/ts/app/app.component.ts
@@ -1,20 +1,30 @@
// #docregion
import {Component} from 'angular2/core';
+import {HTTP_PROVIDERS} from 'angular2/http';
+
+import {FlyingHeroesComponent,
+ FlyingHeroesImpureComponent} from './flying-heroes.component';
import {HeroAsyncMessageComponent} from './hero-async-message.component';
-import {HeroBirthday} from './hero-birthday2.component';
+import {HeroBirthday} from './hero-birthday1.component';
+import {HeroBirthday2} from './hero-birthday2.component';
import {HeroListComponent} from './hero-list.component';
import {PowerBooster} from './power-booster.component';
import {PowerBoostCalculator} from './power-boost-calculator.component';
+import {RandomPipeComponent} from './random-pipe.component';
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives:[
+ FlyingHeroesComponent, FlyingHeroesImpureComponent,
HeroAsyncMessageComponent,
HeroBirthday,
+ HeroBirthday2,
HeroListComponent,
- PowerBooster, PowerBoostCalculator
- ]
+ PowerBooster, PowerBoostCalculator,
+ RandomPipeComponent
+ ],
+ providers:[HTTP_PROVIDERS]
})
export class AppComponent {
birthday = new Date(1988,3,15); // April 15, 1988
diff --git a/public/docs/_examples/pipes/ts/app/exponential-strength.pipe.ts b/public/docs/_examples/pipes/ts/app/exponential-strength.pipe.ts
index 2907cd00dd..7164086f21 100644
--- a/public/docs/_examples/pipes/ts/app/exponential-strength.pipe.ts
+++ b/public/docs/_examples/pipes/ts/app/exponential-strength.pipe.ts
@@ -11,8 +11,8 @@ import {Pipe, PipeTransform} from 'angular2/core';
*/
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
-
- transform(value:number, args:string[]) : any {
- return Math.pow(value, parseInt(args[0] || '1', 10));
+ transform(value:number, [exponent]) : number {
+ var exp = parseFloat(exponent);
+ return Math.pow(value, isNaN(exp) ? 1 : exp);
}
}
diff --git a/public/docs/_examples/pipes/ts/app/fetch-json.pipe.ts b/public/docs/_examples/pipes/ts/app/fetch-json.pipe.ts
index 9383ce0990..99cae3fc09 100644
--- a/public/docs/_examples/pipes/ts/app/fetch-json.pipe.ts
+++ b/public/docs/_examples/pipes/ts/app/fetch-json.pipe.ts
@@ -1,6 +1,6 @@
-///
// #docregion
import {Pipe, PipeTransform} from 'angular2/core';
+import {Http} from 'angular2/http';
// #docregion pipe-metadata
@Pipe({
@@ -9,16 +9,20 @@ import {Pipe, PipeTransform} from 'angular2/core';
})
// #enddocregion pipe-metadata
export class FetchJsonPipe implements PipeTransform{
- private fetchedValue:any;
- private fetchPromise:Promise;
+ private fetched:any = null;
+ private prevUrl = '';
- transform(value:string, args:string[]):any {
- if (!this.fetchPromise) {
- this.fetchPromise = window.fetch(value)
- .then((result:any) => result.json())
- .then((json:any) => this.fetchedValue = json);
+ constructor(private _http: Http) { }
+
+ transform(url:string):any {
+ if (url !== this.prevUrl) {
+ this.prevUrl = url;
+ this.fetched = null;
+ this._http.get(url)
+ .map( result => result.json() )
+ .subscribe( result => this.fetched = result )
}
- return this.fetchedValue;
+ return this.fetched;
}
-}
\ No newline at end of file
+}
diff --git a/public/docs/_examples/pipes/ts/app/flying-heroes.component.html b/public/docs/_examples/pipes/ts/app/flying-heroes.component.html
new file mode 100644
index 0000000000..b6beb80fee
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/app/flying-heroes.component.html
@@ -0,0 +1,39 @@
+
+
+{{title}}
+
+
+New hero:
+
+
+ can fly
+
+
+ Mutate array
+
+
+
+
+
+Heroes who fly (piped)
+
+
+
+ {{hero.name}}
+
+
+
+
+All Heroes (no pipe)
+
+
+
+
+ {{hero.name}}
+
+
+
+
+
diff --git a/public/docs/_examples/pipes/ts/app/flying-heroes.component.ts b/public/docs/_examples/pipes/ts/app/flying-heroes.component.ts
new file mode 100644
index 0000000000..dcb3d812f8
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/app/flying-heroes.component.ts
@@ -0,0 +1,64 @@
+// #docplaster
+// #docregion
+import {Component} from 'angular2/core';
+import {FlyingHeroesPipe,
+ FlyingHeroesImpurePipe} from './flying-heroes.pipe';
+import {HEROES} from './heroes';
+
+@Component({
+ selector: 'flying-heroes',
+ templateUrl: 'app/flying-heroes.component.html',
+ styles: ['#flyers, #all {font-style: italic}'],
+ pipes: [FlyingHeroesPipe]
+})
+// #docregion v1
+export class FlyingHeroesComponent {
+ heroes:any[] = [];
+ canFly = true;
+// #enddocregion v1
+ mutate = true;
+ title = 'Flying Heroes (pure pipe)';
+
+// #docregion v1
+ constructor() { this.reset(); }
+
+ addHero(name:string) {
+ name = name.trim();
+ if (!name) { return; }
+ let hero = {name, canFly: this.canFly};
+// #enddocregion v1
+ if (this.mutate) {
+ // Pure pipe won't update display because heroes array reference is unchanged
+ // Impure pipe will display
+// #docregion v1
+// #docregion push
+ this.heroes.push(hero)
+// #enddocregion push
+// #enddocregion v1
+ } else {
+ // Pipe updates display because heroes array is a new object
+// #docregion concat
+ this.heroes = this.heroes.concat(hero);
+// #enddocregion concat
+ }
+// #docregion v1
+ }
+
+ reset() { this.heroes = HEROES.slice(); }
+}
+// #enddocregion v1
+
+////// Identical except for impure pipe //////
+// #docregion impure-component
+@Component({
+ selector: 'flying-heroes-impure',
+ templateUrl: 'app/flying-heroes.component.html',
+// #enddocregion impure-component
+ styles: ['.flyers, .all {font-style: italic}'],
+// #docregion impure-component
+ pipes: [FlyingHeroesImpurePipe]
+})
+export class FlyingHeroesImpureComponent extends FlyingHeroesComponent {
+ title = 'Flying Heroes (impure pipe)';
+}
+// #docregion impure-component
diff --git a/public/docs/_examples/pipes/ts/app/flying-heroes.pipe.ts b/public/docs/_examples/pipes/ts/app/flying-heroes.pipe.ts
new file mode 100644
index 0000000000..c03fe02a3a
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/app/flying-heroes.pipe.ts
@@ -0,0 +1,25 @@
+// #docregion
+// #docregion pure
+import {Flyer} from './heroes';
+import {Pipe, PipeTransform} from 'angular2/core';
+
+@Pipe({ name: 'flyingHeroes' })
+export class FlyingHeroesPipe implements PipeTransform {
+ transform(allHeroes:Flyer[]) {
+ // #docregion filter
+ return allHeroes.filter(hero => hero.canFly);
+ // #enddocregion filter
+ }
+}
+// #enddocregion pure
+
+/////// Identical except for the pure flag
+// #docregion impure
+// #docregion pipe-decorator
+@Pipe({
+ name: 'flyingHeroes',
+ pure: false
+})
+// #enddocregion pipe-decorator
+export class FlyingHeroesImpurePipe extends FlyingHeroesPipe {}
+// #enddocregion impure
diff --git a/public/docs/_examples/pipes/ts/app/hero-async-message.component.ts b/public/docs/_examples/pipes/ts/app/hero-async-message.component.ts
index 9303932d41..a5a00bfb2f 100644
--- a/public/docs/_examples/pipes/ts/app/hero-async-message.component.ts
+++ b/public/docs/_examples/pipes/ts/app/hero-async-message.component.ts
@@ -1,15 +1,39 @@
// #docregion
import {Component} from 'angular2/core';
+import {Observable} from 'rxjs/Rx';
// Initial view: "Message: "
// After 500ms: Message: You are my Hero!"
@Component({
selector: 'hero-message',
- template: 'Message: {{delayedMessage | async}}',
+ template: `
+ Async Hero Message and AsyncPipe
+
+ Message: {{ message$ | async }}
+
+ `,
})
export class HeroAsyncMessageComponent {
- delayedMessage:Promise = new Promise((resolve, reject) => {
- setTimeout(() => resolve('You are my Hero!'), 500);
- });
+ message$:Observable;
+
+ constructor() { this.resend(); }
+
+ resend() {
+ this.message$ = Observable.interval(500)
+ .map(i => this.messages[i])
+ .take(this.messages.length);
+ }
+
+ private messages = [
+ 'You are my hero!',
+ 'You are the best hero!',
+ 'Will you be my hero?'
+ ];
}
+// #enddocregion
+
+// Alternative message$ formula:
+// this.message$ = Observable.fromArray(this.messages)
+// .map(message => Observable.timer(500).map(() => message))
+// .concatAll();
diff --git a/public/docs/_examples/pipes/ts/app/hero-birthday2.component.ts b/public/docs/_examples/pipes/ts/app/hero-birthday2.component.ts
index e0b357c7f4..6aef612778 100644
--- a/public/docs/_examples/pipes/ts/app/hero-birthday2.component.ts
+++ b/public/docs/_examples/pipes/ts/app/hero-birthday2.component.ts
@@ -3,7 +3,7 @@
import {Component} from 'angular2/core'
@Component({
- selector: 'hero-birthday',
+ selector: 'hero-birthday2',
// #docregion template
template: `
The hero's birthday is {{ birthday | date:format }}
@@ -12,13 +12,11 @@ import {Component} from 'angular2/core'
// #enddocregion template
})
// #docregion class
-export class HeroBirthday {
+export class HeroBirthday2 {
birthday = new Date(1988,3,15); // April 15, 1988
-
toggle = true; // start with true == shortDate
- get format() { return this.toggle ? 'shortDate' : 'fullDate'}
-
+ get format() { return this.toggle ? 'shortDate' : 'fullDate'}
toggleFormat() { this.toggle = !this.toggle; }
}
// #enddocregion class
diff --git a/public/docs/_examples/pipes/ts/app/hero-list.component.ts b/public/docs/_examples/pipes/ts/app/hero-list.component.ts
index 169767eac1..414281e6e6 100644
--- a/public/docs/_examples/pipes/ts/app/hero-list.component.ts
+++ b/public/docs/_examples/pipes/ts/app/hero-list.component.ts
@@ -6,7 +6,7 @@ import {FetchJsonPipe} from './fetch-json.pipe';
selector: 'hero-list',
// #docregion template
template: `
- Heroes from JSON File
+ Heroes from JSON File
{{hero.name}}
@@ -19,6 +19,4 @@ import {FetchJsonPipe} from './fetch-json.pipe';
// #enddocregion template
pipes: [FetchJsonPipe]
})
-export class HeroListComponent {
- /* I've got nothing to do ;-) */
-}
+export class HeroListComponent { }
diff --git a/public/docs/_examples/pipes/ts/app/heroes.ts b/public/docs/_examples/pipes/ts/app/heroes.ts
new file mode 100644
index 0000000000..fe30ad84f4
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/app/heroes.ts
@@ -0,0 +1,7 @@
+export interface Flyer { canFly: boolean }
+export const HEROES = [
+ {"name": "Windstorm", "canFly": true},
+ {"name": "Bombasto", "canFly": false},
+ {"name": "Magneto", "canFly": false},
+ {"name": "Tornado", "canFly": true}
+];
\ No newline at end of file
diff --git a/public/docs/_examples/pipes/ts/app/main.ts b/public/docs/_examples/pipes/ts/app/main.ts
index 597ab3aa7c..fd7c63a8a2 100644
--- a/public/docs/_examples/pipes/ts/app/main.ts
+++ b/public/docs/_examples/pipes/ts/app/main.ts
@@ -1,6 +1,6 @@
import {bootstrap} from 'angular2/platform/browser';
+import 'rxjs/Rx';
+
import {AppComponent} from './app.component';
-import {HeroBirthday} from './hero-birthday1.component';
bootstrap(AppComponent);
-bootstrap(HeroBirthday); // v.1
\ No newline at end of file
diff --git a/public/docs/_examples/pipes/ts/app/random-pipe.component.ts b/public/docs/_examples/pipes/ts/app/random-pipe.component.ts
new file mode 100644
index 0000000000..fe86108432
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/app/random-pipe.component.ts
@@ -0,0 +1,24 @@
+import {Component} from 'angular2/core';
+import {Pipe, PipeTransform} from 'angular2/core';
+
+// #docregion pipe
+// Pure pipe
+@Pipe({ name: 'randomizer' })
+export class RandomizerPipe implements PipeTransform {
+ // Impure function
+ transform() { return Math.random() * 10 ;}
+}
+// #enddocregion pipe
+
+@Component({
+ selector: 'random-pipe',
+ template: `
+
Random Pipe (pure pipe/impure function)
+
+
Input value: {{box.value}}
+
Random pipe output: {{box.value | randomizer}}
+ `,
+ pipes: [RandomizerPipe]
+})
+export class RandomPipeComponent {
+}
diff --git a/public/docs/_examples/pipes/ts/app/window.extension.d.ts b/public/docs/_examples/pipes/ts/app/window.extension.d.ts
deleted file mode 100644
index 0a5a71bbf6..0000000000
--- a/public/docs/_examples/pipes/ts/app/window.extension.d.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-interface Window {
- fetch(url: string, options? : {}) : Promise
-}
diff --git a/public/docs/_examples/pipes/ts/heroes.json b/public/docs/_examples/pipes/ts/heroes.json
index 436b220d53..e5f0b77262 100644
--- a/public/docs/_examples/pipes/ts/heroes.json
+++ b/public/docs/_examples/pipes/ts/heroes.json
@@ -1,6 +1,6 @@
[
- {"name": "Windstorm"},
- {"name": "Bombasto"},
- {"name": "Magneto"},
- {"name": "Tornado"}
+ {"name": "Windstorm", "canFly": true},
+ {"name": "Bombasto", "canFly": false},
+ {"name": "Magneto", "canFly": false},
+ {"name": "Tornado", "canFly": true}
]
diff --git a/public/docs/_examples/pipes/ts/index.html b/public/docs/_examples/pipes/ts/index.html
index f11eba1c67..498e376a5e 100644
--- a/public/docs/_examples/pipes/ts/index.html
+++ b/public/docs/_examples/pipes/ts/index.html
@@ -9,14 +9,15 @@
-
+
+