angular学习之路10-管道

模板中使用“|”操作符来使用管道:

<p>The hero's birthday is {{ birthday | date }}</p>

如果管道具有参数,用冒号:来分隔参数,具有多个参数,参数之间也用冒号来分隔,例如:

一参数:<p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </p>

管道的参数值可以是任何有效的模板表达式(参见模板语法中的模板表达式部分),比如字符串字面量或组件的属性。 换句话说,借助属性绑定,你也可以像用绑定来控制生日的值一样,控制生日的显示格式。

来写第二个组件,它把管道的格式参数绑定到该组件的 format 属性。这里是新组件的模板:

template: `
  <p>The hero's birthday is {{ birthday | date:format }}</p>
  <button (click)="toggleFormat()">Toggle Format</button>


你还能在模板中添加一个按钮,并把它的点击事件绑定到组件的 toggleFormat() 方法。 此方法会在短日期格式('shortDate')和长日期格式('fullDate')之间切换组件的 format 属性。

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; }
}

链式管道

你可以把管道串联在一起,以组合出一些潜在的有用功能。 下面这个例子中,要把 birthday 串联到 DatePipe 管道,然后又串联到 UpperCasePipe,这样就可以把生日显示成大写形式了。 生日被显示成了APR 15, 1988

The chained hero's birthday is
{{ birthday | date | uppercase}}

下面这个显示FRIDAY, APRIL 15, 1988的例子用同样的方式链接了这两个管道,而且同时还给 date 管道传进去一个参数

The chained hero's birthday is
{{  birthday | date:'fullDate' | uppercase}}

自定义管道

你还可以写自己的自定义管道。 下面就是一个名叫 ExponentialStrengthPipe 的管道,它可以放大英雄的能力:

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?: number): number {
    return Math.pow(value, isNaN(exponent) ? 1 : exponent);
  }
}

在这个管道的定义中体现了几个关键点:

  • 管道是一个带有“管道元数据(pipe metadata)”装饰器的类。

  • 这个管道类实现了 PipeTransform 接口的 transform 方法,该方法接受一个输入值和一些可选参数,并返回转换后的值。

  • 当每个输入值被传给 transform 方法时,还会带上另一个参数,比如你这个管道就有一个 exponent(放大指数) 参数。

  • 可以通过 @Pipe 装饰器来告诉 Angular:这是一个管道。该装饰器是从 Angular 的 core 库中引入的。

  • 这个 @Pipe 装饰器允许你定义管道的名字,这个名字会被用在模板表达式中。它必须是一个有效的 JavaScript 标识符。 比如,你这个管道的名字是 exponentialStrength

请注意以下几点:

  • 你使用自定义管道的方式和内置管道完全相同。

  • 你必须把这个管道添加到 AppModule 的 declarations 数组中。

  • 你必须在 AppModule 的 declarations 数组中包含这个管道。

管道与变更检测

Angular 通过变更检测过程来查找绑定值的更改,并在每一次 JavaScript 事件之后运行:每次按键、鼠标移动、定时器以及服务器的响应。 这可能会让变更检测显得很昂贵,但是 Angular 会尽可能降低变更检测的成本。

当使用管道时,Angular 会选用一种更简单、更快速的变更检测算法。

纯(pure)管道与非纯(impure)管道

有两类管道:的与非纯的。 默认情况下,管道都是纯的。以前见到的每个管道都是纯的。 通过把它的 pure 标志设置为 false,你可以制作一个非纯管道。你可以像这样让 FlyingHeroesPipe 变成非纯的:

纯管道

Angular 只有在它检测到输入值发生了纯变更时才会执行纯管道。 纯变更是指对原始类型值(StringNumberBooleanSymbol)的更改, 或者对对象引用(DateArrayFunctionObject)的更改。

Angular 会忽略(复合)对象内部的更改。 如果你更改了输入日期(Date)中的月份、往一个输入数组(Array)中添加新值或者更新了一个输入对象(Object)的属性,它都不会调用纯管道。

这可能看起来是一种限制,但它保证了速度。 对象引用的检查是非常快的(比递归的深检查要快得多),所以 Angular 可以快速的决定是否应该跳过管道执行和视图更新。

因此,如果要和变更检测策略打交道,就会更喜欢用纯管道。 如果不能,你就可以转回到非纯管道。

非纯管道

Angular 会在每个组件的变更检测周期中执行非纯管道。 非纯管道可能会被调用很多次,和每个按键或每次鼠标移动一样频繁。

要在脑子里绷着这根弦,必须小心翼翼的实现非纯管道。 一个昂贵、迟钝的管道将摧毁用户体验。

你把它从 FlyingHeroesPipe 中继承下来,以证明无需改动内部代码。 唯一的区别是管道元数据中的 pure 标志。

非纯 AsyncPipe

Angular 的 AsyncPipe 是一个有趣的非纯管道的例子。 AsyncPipe 接受一个 Promise 或 Observable 作为输入,并且自动订阅这个输入,最终返回它们给出的值。

AsyncPipe 管道是有状态的。 该管道维护着一个所输入的 Observable 的订阅,并且持续从那个 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)
    );
  }
}

这个 Async 管道节省了组件的样板代码。 组件不用订阅这个异步数据源,而且不用在被销毁时取消订阅(如果订阅了而忘了反订阅容易导致隐晦的内存泄露)。

一个非纯而且带缓存的管道

来写更多的非纯管道:一个向服务器发起 HTTP 请求的管道。

时刻记住,非纯管道可能每隔几微秒就会被调用一次。 如果你不小心点,这个管道就会发起一大堆请求“攻击”服务器

下面这个管道只有当所请求的 URL 发生变化时才会向服务器发起请求。它会缓存服务器的响应。 代码如下,它使用Angular http客户端来接收数据

import { HttpClient }          from '@angular/common/http';
import { Pipe, PipeTransform } from '@angular/core';

@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;
  }
}

接下来在一个测试挽具组件中演示一下它,该组件的模板中定义了两个使用到此管道的绑定,它们都从 heroes.json 文件中取得英雄数据。

import { Component } from '@angular/core';

@Component({
  selector: 'app-hero-list',
  template: `
    <h2>Heroes from JSON File</h2>

    <div *ngFor="let hero of ('assets/heroes.json' | fetch) ">
      {{hero.name}}
    </div>

    <p>Heroes as JSON:
      {{'assets/heroes.json' | fetch | json}}
    </p>`
})
export class HeroListComponent { }

JsonPipe

第二个绑定除了用到 FetchPipe 之外还链接了更多管道。 它通过串联上内置管道 JsonPipe 来把英雄数据显示成了 JSON 格式。

纯管道与纯函数

纯管道使用纯函数。 纯函数是指在处理输入并返回结果时,不会产生任何副作用的函数。 给定相同的输入,它们总是返回相同的输出。

在本章前面讨论的管道都是用纯函数实现的。 内置的 DatePipe 就是一个用纯函数实现的纯管道。ExponentialStrengthPipe 是如此, FlyingHeroesComponent 也是如此。 不久前你刚看过的 FlyingHeroesImpurePipe就是一个用纯函数实现的非纯管道

但是一个纯管道必须总是用纯函数实现。忽略这个警告将导致失败并带来一大堆这样的控制台错误:表达式在被检查后被变更。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值