Angular2 - 管道
管道的作用:
管道把数据作为输入,然后转换它,给出期望的输出。可以有好的将“显示--值”转换器 声明在HTML中。
管道表达式: {{ 显示 | 格式 }}
使用管道:
<p>The hero's birthday is {{ birthday | date }}</p>
内置的管道:
比如 DatePipe
、UpperCasePipe
、LowerCasePipe
、CurrencyPipe
和 PercentPipe
。 它们全都可以直接用在任何模板中
对管道进行参数化:
管道可能接受任何数量的可选参数来对它的输出进行微调。 可以在管道名后面添加一个冒号( : )再跟一个参数值,来为管道添加参数(比如 currency:'EUR'
)。 如果这个管道可以接受多个参数,那么就用冒号来分隔这些参数值(比如 slice:1:5
)。
<p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </p>
还可以将管道的格式参数绑定到该组件的属性上:如下所示
template: ` <p>The hero's birthday is {{ birthday | date:format }}</p> <button (click)="toggleFormat()">Toggle Format</button> `
export class HeroBirthday2Component { birthday = new Date(1988, 3, 15); // April 15, 1988 toggle = true; // start with true == shortDate get format() { return this.toggle ? 'shortDate' : 'fullDate'; } toggleFormat() { this.toggle = !this.toggle; } }
链式管道:把管道串联在一起,以组合出一些潜在的有用功能。
The chained hero's birthday is
{{ birthday | date | uppercase}}
//同样的方式链接了这两个管道,而且同时还给 date 管道传进去一个参数
The chained hero's birthday is
{{ birthday | date:'fullDate' | uppercase}}
自定义管道:
import { Pipe, PipeTransform } from '@angular/core'; /* * Raise the value exponentially * Takes an exponent argument that defaults to 1. * Usage: * value | exponentialStrength:exponent * Example: * {{ 2 | exponentialStrength:10 }} * formats to: 1024 */ @Pipe({name: 'exponentialStrength'}) export class ExponentialStrengthPipe implements PipeTransform { transform(value: number, exponent: string): number { let exp = parseFloat(exponent); return Math.pow(value, isNaN(exp) ? 1 : exp); } }
在这个管道的定义中体现了几个关键点:
-
管道是一个带有“管道元数据(pipe metadata)”装饰器的类。
-
这个管道类实现了
PipeTransform
接口的transform
方法,该方法接受一个输入值和一些可选参数,并返回转换后的值。 -
当每个输入值被传给
transform
方法时,还会带上另一个参数,比如你这个管道就有一个exponent
(放大指数) 参数。 -
可以通过
@Pipe
装饰器来告诉 Angular:这是一个管道。该装饰器是从 Angular 的core
库中引入的。 -
这个
@Pipe
装饰器允许你定义管道的名字,这个名字会被用在模板表达式中。它必须是一个有效的 JavaScript 标识符。 比如,你这个管道的名字是exponentialStrength
。
PipeTransform 接口
transform 方法是管道的基本要素。 PipeTransform接口中定义了它,并用它指导各种工具和编译器。
理论上说,它是可选的。Angular 不会管它,而是直接查找并执行 transform 方法。
请注意以下几点:
-
你使用自定义管道的方式和内置管道完全相同。
-
你必须在
AppModule
的declarations
数组中包含这个管道
纯(pure)管道与非纯(impure)管道
有两类管道:纯的与非纯的。 默认情况下,管道都是纯的。以前见到的每个管道都是纯的。 通过把它的 pure
标志设置为 false
,你可以制作一个非纯管道。你可以像这样让 FlyingHeroesPipe
变成非纯的:
@Pipe({ name: 'flyingHeroesImpure', pure: false })
纯管道:
Angular 只有在它检测到输入值发生了纯变更时才会执行纯管道。 纯变更是指对原始类型值(String
、Number
、Boolean
、Symbol
)的更改, 或者对对象引用(Date
、Array
、Function
、Object
)的更改。
非纯管道
Angular 会在每个组件的变更检测周期中执行非纯管道。 非纯管道可能会被调用很多次,和每个按键或每次鼠标移动一样频繁。
非纯 AsyncPipe
AsyncPipe
接受一个 Promise
或 Observable
作为输入,并且自动订阅这个输入,最终返回它们给出的值。
AsyncPipe
管道是有状态的。 该管道维护着一个所输入的 Observable
的订阅,并且持续从那个 Observable
中发出新到的值。
下面例子使用该 async
管道把一个消息字符串(message$
)的 Observable
绑定到视图中:
import { Component } from '@angular/core'; import { Observable, interval } from 'rxjs'; import { map, take } from 'rxjs/operators'; @Component({ selector: 'app-hero-message', template: ` <h2>Async Hero Message and AsyncPipe</h2> <p>Message: {{ message$ | async }}</p> <button (click)="resend()">Resend</button>`, }) export class HeroAsyncMessageComponent { message$: Observable<string>; private messages = [ 'You are my hero!', 'You are the best hero!', 'Will you be my hero?' ]; constructor() { this.resend(); } resend() { this.message$ = interval(500).pipe( map(i => this.messages[i]), take(this.messages.length) ); } }
一个非纯而且带缓存的管道
来写更多的非纯管道:一个向服务器发起 HTTP 请求的管道。
时刻记住,非纯管道可能每隔几微秒就会被调用一次。 如果你不小心点,这个管道就会发起一大堆请求“攻击”服务器。
下面这个管道只有当所请求的 URL 发生变化时才会向服务器发起请求。它会缓存服务器的响应。 代码如下,它使用Angular http客户端来接收数据。
import { Pipe, PipeTransform } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Pipe({ name: 'fetch', pure: false }) export class FetchJsonPipe implements PipeTransform { private cachedData: any = null; private cachedUrl = ''; constructor(private http: HttpClient) { } transform(url: string): any { if (url !== this.cachedUrl) { this.cachedData = null; this.cachedUrl = url; this.http.get(url).subscribe( result => this.cachedData = result ); } return this.cachedData; } }