填寫這份《一分鐘調查》,幫我們(開發組)做得更好!去填寫Home

用管道轉換資料

Transforming Data Using Pipes

管道用來對字串、貨幣金額、日期和其他顯示資料進行轉換和格式化。管道是一些簡單的函式,可以在範本表示式中用來接受輸入值並返回一個轉換後的值。例如,你可以使用一個管道把日期顯示為 1988 年 4 月 15 日,而不是其原始字串格式。

Use pipes to transform strings, currency amounts, dates, and other data for display. Pipes are simple functions you can use in template expressions to accept an input value and return a transformed value. Pipes are useful because you can use them throughout your application, while only declaring each pipe once. For example, you would use a pipe to show a date as April 15, 1988 rather than the raw string format.

本主題中使用的範例應用,參閱現場演練 / 下載範例

For the sample app used in this topic, see the現場演練 / 下載範例.

Angular 為典型的資料轉換提供了內建的管道,包括國際化的轉換(i18n),它使用本地化資訊來格式化資料。資料格式化常用的內建管道如下:

Angular provides built-in pipes for typical data transformations, including transformations for internationalization (i18n), which use locale information to format data. The following are commonly used built-in pipes for data formatting:

  • DatePipe:根據本地環境中的規則格式化日期值。

    DatePipe: Formats a date value according to locale rules.

  • UpperCasePipe:把文字全部轉換成大寫。

    UpperCasePipe: Transforms text to all upper case.

  • LowerCasePipe:把文字全部轉換成小寫。

    LowerCasePipe: Transforms text to all lower case.

  • CurrencyPipe:把數字轉換成貨幣字串,根據本地環境中的規則進行格式化。

    CurrencyPipe: Transforms a number to a currency string, formatted according to locale rules.

  • DecimalPipe:把數字轉換成帶小數點的字串,根據本地環境中的規則進行格式化。

    DecimalPipe: Transforms a number into a string with a decimal point, formatted according to locale rules.

  • PercentPipe:把數字轉換成百分比字串,根據本地環境中的規則進行格式化。

    PercentPipe: Transforms a number to a percentage string, formatted according to locale rules.

你還可以建立管道來封裝自訂轉換,並在範本表示式中使用自訂管道。

You can also create pipes to encapsulate custom transformations and use your custom pipes in template expressions.

先決條件

Prerequisites

要想使用管道,你應該對這些內容有基本的瞭解:

To use pipes you should have a basic understanding of the following:

在範本中使用管道

Using a pipe in a template

要應用管道,請如下所示在範本表示式中使用管道運算子(|),緊接著是該管道的名字,對於內建的 DatePipe它的名字是 date 。這個例子中的顯示如下:

To apply a pipe, use the pipe operator (|) within a template expression as shown in the following code example, along with the name of the pipe, which is date for the built-in DatePipe. The tabs in the example show the following:

  • app.component.html 在另一個單獨的範本中使用 date 來顯示生日。

    app.component.html uses date in a separate template to display a birthday.

  • hero-birthday1.component.ts 使用相同的管道作為元件內嵌範本的一部分,同時該元件也會設定生日值。

    hero-birthday1.component.ts uses the same pipe as part of an in-line template in a component that also sets the birthday value.

<p>The hero's birthday is {{ birthday | date }}</p>import { Component } from '@angular/core'; @Component({ selector: 'app-hero-birthday', template: `<p>The hero's birthday is {{ birthday | date }}</p>` }) export class HeroBirthdayComponent { birthday = new Date(1988, 3, 15); // April 15, 1988 -- since month parameter is zero-based }
      
      <p>The hero's birthday is {{ birthday | date }}</p>
    

該元件的 birthday 值透過管道運算子(|)流向 date函式。

The component's birthday value flows through the pipe operator, | to the datefunction.

使用引數和管道鏈來格式化資料

Transforming data with parameters and chained pipes

可以用可選引數微調管道的輸出。例如,你可以使用 CurrencyPipe和國家程式碼(如 EUR)作為引數。範本表示式 {{ amount | currency:'EUR' }} 會把 amount 轉換成歐元。緊跟在管道名稱( currency )後面的是冒號(:)和引數值('EUR')。

Use optional parameters to fine-tune a pipe's output. For example, you can use the CurrencyPipewith a country code such as EUR as a parameter. The template expression {{ amount | currency:'EUR' }} transforms the amount to currency in euros. Follow the pipe name (currency) with a colon (:) and the parameter value ('EUR').

如果管道能接受多個引數,就用冒號分隔這些值。例如,{{ amount | currency:'EUR':'Euros '}} 會把第二個引數(字串 'Euros ')新增到輸出字串中。你可以使用任何有效的範本表示式作為引數,比如字串字面量或元件的屬性。

If the pipe accepts multiple parameters, separate the values with colons. For example, {{ amount | currency:'EUR':'Euros '}} adds the second parameter, the string literal 'Euros ', to the output string. You can use any valid template expression as a parameter, such as a string literal or a component property.

有些管道需要至少一個引數,並且允許使用更多的可選引數,比如 SlicePipe。例如, {{ slice:1:5 }} 會建立一個新陣列或字串,它以第 1 個元素開頭,並以第 5 個元素結尾。

Some pipes require at least one parameter and allow more optional parameters, such as SlicePipe. For example, {{ slice:1:5 }} creates a new array or string containing a subset of the elements starting with element 1 and ending with element 5.

範例:格式化日期

Example: Formatting a date

下面的例子顯示了兩種不同格式('shortDate''fullDate')之間的切換:

The tabs in the following example demonstrates toggling between two different formats ('shortDate' and 'fullDate'):

  • app.component.html 範本使用 DatePipe(名為 date)的格式引數把日期顯示為 04/15/88

    The app.component.html template uses a format parameter for the DatePipe(named date) to show the date as 04/15/88.

  • hero-birthday2.component.ts 元件把該管道的 format 引數繫結到 template 中元件的 format 屬性,並添加了一個按鈕,其 click 事件繫結到了該元件的 toggleFormat() 方法。

    The hero-birthday2.component.ts component binds the pipe's format parameter to the component's format property in the template section, and adds a button for a click event bound to the component's toggleFormat() method.

  • hero-birthday2.component.ts 元件的 toggleFormat() 方法會在短格式('shortDate')和長格式('fullDate')之間切換該元件的 format 屬性。

    The hero-birthday2.component.ts component's toggleFormat() method toggles the component's format property between a short form ('shortDate') and a longer form ('fullDate').

<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 -- since month parameter is zero-based toggle = true; // start with true == shortDate get format() { return this.toggle ? 'shortDate' : 'fullDate'; } toggleFormat() { this.toggle = !this.toggle; } }
      
      <p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </p>
    

點選 Toggle Format 按鈕可以在 04/15/1988Friday, April 15, 1988 之間切換日期格式,如圖 1 所示。

Clicking the Toggle Format button alternates the date format between 04/15/1988 and Friday, April 15, 1988 as shown in Figure 1.

圖 1. 單擊該按鈕切換日期格式

Figure 1. Clicking the button toggles the date format

關於 date 管道的格式選項,參閱 DatePipe

For date pipe format options, see DatePipe.

範例:透過串聯管道應用兩種格式

Example: Applying two formats by chaining pipes

你可以對管道進行串聯,以便一個管道的輸出成為下一個管道的輸入。

You can chain pipes so that the output of one pipe becomes the input to the next.

在下面的範例中,串聯管道首先將格式應用於一個日期值,然後將格式化之後的日期轉換為大寫字元。 src/app/app.component.html 範本的第一個標籤頁把 DatePipeUpperCasePipe 的串聯起來,將其顯示為 APR 15, 1988src/app/app.component.html 範本的第二個標籤頁在串聯 uppercase 之前,還把 fullDate 引數傳給了 date,將其顯示為 FRIDAY, APRIL 15, 1988

In the following example, chained pipes first apply a format to a date value, then convert the formatted date to uppercase characters. The first tab for the src/app/app.component.html template chains DatePipe and UpperCasePipe to display the birthday as APR 15, 1988. The second tab for the src/app/app.component.html template passes the fullDate parameter to date before chaining to uppercase, which produces FRIDAY, APRIL 15, 1988.

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

為自訂資料轉換建立管道

Creating pipes for custom data transformations

建立自訂管道來封裝那些內建管道沒有提供的轉換。然後你就可以在範本表示式中使用你的自訂管道,就像內建管道一樣,把輸入值轉換成顯示輸出。

Create custom pipes to encapsulate transformations that are not provided with the built-in pipes. You can then use your custom pipe in template expressions, the same way you use built-in pipes—to transform input values to output values for display.

把一個類別標記為一個管道

Marking a class as a pipe

要把類別標記為管道並提供配置元資料,請把 @Pipe裝飾器應用到這個類別上。管道類別名稱是 UpperCamelCase(類別名稱的一般約定),相應的 name 字串是 camelCase 的。不要在 name 中使用連字元。詳細資訊和更多範例,請參閱管道名稱

To mark a class as a pipe and supply configuration metadata, apply the @Pipedecorator to the class. Use UpperCamelCase (the general convention for class names) for the pipe class name, and camelCase for the corresponding name string. Do not use hyphens in the name. For details and more examples, see Pipe names.

在範本表示式中使用 name 就像在內建管道中一樣。

Use name in template expressions as you would for a built-in pipe.

使用 PipeTransform 介面

Using the PipeTransform interface

在自訂管道類別中實現 PipeTransform介面來執行轉換。

Implement the PipeTransforminterface in your custom pipe class to perform the transformation.

Angular 呼叫 transform 方法,該方法使用繫結的值作為第一個引數,把其它任何引數都以列表的形式作為第二個引數,並返回轉換後的值。

Angular invokes the transform method with the value of a binding as the first argument, and any parameters as the second argument in list form, and returns the transformed value.

範例:指數級轉換

Example: Transforming a value exponentially

在遊戲中,你可能希望實現一種指數級轉換,以指數級增加英雄的力量。例如,如果英雄的得分是 2,那麼英雄的能量會指數級增長 10 次,最終得分為 1024。你可以使用自訂管道進行這種轉換。

In a game, you may want to implement a transformation that raises a value exponentially to increase a hero's power. For example, if the hero's score is 2, boosting the hero's power exponentially by 10 produces a score of 1024. You can use a custom pipe for this transformation.

下列程式碼範例顯示了兩個元件定義:

The following code example shows two component definitions:

  • exponential-strength.pipe.ts 透過一個執行轉換的 transform 方法定義了一個名為 exponentialStrength 的自訂管道。它為傳給管道的引數定義了 transform 方法的一個引數(exponent)。

    The exponential-strength.pipe.ts component defines a custom pipe named exponentialStrength with the transform method that performs the transformation. It defines an argument to the transform method (exponent) for a parameter passed to the pipe.

  • power-booster.component.ts 元件示範瞭如何使用該管道,指定了一個值( 2 )和一個 exponent 引數( 10 )。輸出結果如圖 2 所示。

    The power-booster.component.ts component demonstrates how to use the pipe, specifying a value (2) and the exponent parameter (10). Figure 2 shows the output.

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); } }import { Component } from '@angular/core'; @Component({ selector: 'app-power-booster', template: ` <h2>Power Booster</h2> <p>Super power boost: {{2 | exponentialStrength: 10}}</p> ` }) export class PowerBoosterComponent { }
      
      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);
  }
}
    

圖 2. exponentialStrength 管道的輸出

Figure 2. Output from the exponentialStrength pipe

要檢查 exponentialStrength 管道的行為,請檢視現場演練 / 下載範例,並在範本中修改值和可選的指數引數。

To examine the behavior the exponentialStrength pipe in the現場演練 / 下載範例, change the value and optional exponent in the template.

透過管道中的資料繫結來檢測變更

Detecting changes with data binding in pipes

你可以透過帶有管道的資料繫結來顯示值並響應使用者操作。如果是原始型別的輸入值,比如 StringNumber ,或者是物件參考型的輸入值,比如 DateArray ,那麼每當 Angular 檢測到輸入值或參考有變化時,就會執行該輸入管道。

You use data binding with a pipe to display values and respond to user actions. If the data is a primitive input value, such as String or Number, or an object reference as input, such as Date or Array, Angular executes the pipe whenever it detects a change for the input value or reference.

比如,你可以修改前面的自訂管道範例,透過 ngModel 的雙向繫結來輸入數量和提升因子,如下面的程式碼範例所示。

For example, you could change the previous custom pipe example to use two-way data binding with ngModel to input the amount and boost factor, as shown in the following code example.

import { Component } from '@angular/core'; @Component({ selector: 'app-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> ` }) export class PowerBoostCalculatorComponent { power = 5; factor = 1; }
src/app/power-boost-calculator.component.ts
      
      import { Component } from '@angular/core';

@Component({
  selector: 'app-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>
  `
})
export class PowerBoostCalculatorComponent {
  power = 5;
  factor = 1;
}
    

每當使用者改變 “normal power” 值或 “boost factor” 時,就會執行 exponentialStrength 管道,如圖 3 所示。

The exponentialStrength pipe executes every time the user changes the "normal power" value or the "boost factor", as shown in Figure 3.

圖 3. 更改 exponentialStrength 管道的數值和提升因子

Figure 3. Changing the amount and boost factor for the exponentialStrength pipe

Angular 會檢測每次變更,並立即執行該管道。對於原始輸入值,這很好。但是,如果要在複合物件中更改某些內部值(例如日期中的月份、陣列中的元素或物件中的屬性),就需要了解變更檢測的工作原理,以及如何使用 impure(非純)管道。

Angular detects each change and immediately runs the pipe. This is fine for primitive input values. However, if you change something inside a composite object (such as the month of a date, an element of an array, or an object property), you need to understand how change detection works, and how to use an impure pipe.

變更檢測的工作原理

How change detection works

Angular 會在每次 DOM 事件(每次按鍵、滑鼠移動、計時器滴答和伺服器響應)之後執行的變更檢測過程中查詢對資料繫結值的更改。下面這段不使用管道的例子示範了 Angular 如何利用預設的變更檢測策略來監控和更新 heroes 陣列中每個英雄的顯示效果。範例顯示如下:

Angular looks for changes to data-bound values in a change detection process that runs after every DOM event: every keystroke, mouse move, timer tick, and server response. The following example, which doesn't use a pipe, demonstrates how Angular uses its default change detection strategy to monitor and update its display of every hero in the heroes array. The example tabs show the following:

  • flying-heroes.component.html (v1) 範本中, *ngFor 會重複顯示英雄的名字。

    In the flying-heroes.component.html (v1) template, the *ngFor repeater displays the hero names.

  • 與之相伴的元件類別 flying-heroes.component.ts (v1) 提供了一些英雄,把這些英雄新增到陣列中,並重置了該陣列。

    Its companion component class flying-heroes.component.ts (v1) provides heroes, adds heroes into the array, and resets the array.

New hero: <input type="text" #box (keyup.enter)="addHero(box.value); box.value=''" placeholder="hero name"> <button (click)="reset()">Reset</button> <div *ngFor="let hero of heroes"> {{hero.name}} </div>export class FlyingHeroesComponent { heroes: any[] = []; canFly = true; constructor() { this.reset(); } addHero(name: string) { name = name.trim(); if (!name) { return; } const hero = {name, canFly: this.canFly}; this.heroes.push(hero); } reset() { this.heroes = HEROES.slice(); } }
      
      New hero:
  <input type="text" #box
          (keyup.enter)="addHero(box.value); box.value=''"
          placeholder="hero name">
  <button (click)="reset()">Reset</button>
  <div *ngFor="let hero of heroes">
    {{hero.name}}
  </div>
    

每次使用者新增一個英雄時,Angular 都會更新顯示內容。如果使用者點選了 Reset 按鈕,Angular 就會用原來這些英雄組成的新陣列來替換 heroes ,並更新顯示。如果你新增刪除或更改了某個英雄的能力,Angular 也會檢測這些變化並更新顯示。

Angular updates the display every time the user adds a hero. If the user clicks the Reset button, Angular replaces heroes with a new array of the original heroes and updates the display. If you add the ability to remove or change a hero, Angular would detect those changes and update the display as well.

然而,如果對於每次更改都執行一個管道來更新顯示,就會降低你應用的效能。因此,Angular 會使用更快的變更檢測演算法來執行管道,如下一節所述。

However, executing a pipe to update the display with every change would slow down your app's performance. So Angular uses a faster change-detection algorithm for executing a pipe, as described in the next section.

檢測原始型別和物件參考的純變更

Detecting pure changes to primitives and object references

透過預設情況下,管道會定義成純的(pure),這樣 Angular 只有在檢測到輸入值發生了純變更時才會執行該管道。純變更是對原始輸入值(比如 StringNumberBooleanSymbol )的變更,或是對物件參考的變更(比如 DateArrayFunctionObject)。

By default, pipes are defined as pure so that Angular executes the pipe only when it detects a pure change to the input value. A pure change is either a change to a primitive input value (such as String, Number, Boolean, or Symbol), or a changed object reference (such as Date, Array, Function, or Object).

純管道必須使用純函式,它能處理輸入並返回沒有副作用的值。換句話說,給定相同的輸入,純函式應該總是返回相同的輸出。

A pure pipe must use a pure function, which is one that processes inputs and returns values without side effects. In other words, given the same input, a pure function should always return the same output.

使用純管道,Angular 會忽略複合物件中的變化,例如往現有陣列中新增的元素,因為檢查原始值或物件參考比對物件中的差異進行深度檢查要快得多。Angular 可以快速判斷是否可以跳過執行該管道並更新檢視。

With a pure pipe, Angular ignores changes within composite objects, such as a newly added element of an existing array, because checking a primitive value or object reference is much faster than performing a deep check for differences within objects. Angular can quickly determine if it can skip executing the pipe and updating the view.

但是,以陣列作為輸入的純管道可能無法正常工作。為了示範這個問題,修改前面的例子來把英雄列表過濾成那些會飛的英雄。在 *ngFor 中使用 FlyingHeroesPipe ,程式碼如下。這個例子的顯示如下:

However, a pure pipe with an array as input may not work the way you want. To demonstrate this issue, change the previous example to filter the list of heroes to just those heroes who can fly. Use the FlyingHeroesPipe in the *ngFor repeater as shown in the following code. The tabs for the example show the following:

  • 帶有新管道的範本(flying-heroes.component.html (flyers))。

    The template (flying-heroes.component.html (flyers)) with the new pipe.

  • FlyingHeroesPipe 自訂管道實現(flying-heroes.pipe.ts)。

    The FlyingHeroesPipe custom pipe implementation (flying-heroes.pipe.ts).

<div *ngFor="let hero of (heroes | flyingHeroes)"> {{hero.name}} </div>import { Pipe, PipeTransform } from '@angular/core'; import { Flyer } from './heroes'; @Pipe({ name: 'flyingHeroes' }) export class FlyingHeroesPipe implements PipeTransform { transform(allHeroes: Flyer[]) { return allHeroes.filter(hero => hero.canFly); } }
      
      <div *ngFor="let hero of (heroes | flyingHeroes)">
  {{hero.name}}
</div>
    

該應用現在展示了意想不到的行為:當用戶添加了會飛的英雄時,它們都不會出現在 “Heroes who fly” 中。發生這種情況是因為新增英雄的程式碼會把它 push 到 heroes 陣列中:

The app now shows unexpected behavior: When the user adds flying heroes, none of them appear under "Heroes who fly." This happens because the code that adds a hero does so by pushing it onto the heroes array:

this.heroes.push(hero);
src/app/flying-heroes.component.ts
      
      this.heroes.push(hero);
    

而變更檢測器會忽略對陣列元素的更改,所以管道不會執行。

The change detector ignores changes to elements of an array, so the pipe doesn't run.

Angular 忽略了被改變的陣列元素的原因是對陣列的參考沒有改變。由於 Angular 認為該陣列仍是相同的,所以不會更新其顯示。

The reason Angular ignores the changed array element is that the reference to the array hasn't changed. Since the array is the same, Angular does not update the display.

獲得所需行為的方法之一是更改物件參考本身。你可以用一個包含新更改過的元素的新陣列替換該陣列,然後把這個新陣列作為輸入傳給管道。在上面的例子中,你可以建立一個附加了新英雄的陣列,並把它賦值給 heroes。 Angular 檢測到了這個陣列參考的變化,並執行了該管道。

One way to get the behavior you want is to change the object reference itself. You can replace the array with a new array containing the newly changed elements, and then input the new array to the pipe. In the above example, you can create an array with the new hero appended, and assign that to heroes. Angular detects the change in the array reference and executes the pipe.

總結一下,如果修改了輸入陣列,純管道就不會執行。如果替換了輸入陣列,就會執行該管道並更新顯示,如圖 4 所示。

To summarize, if you mutate the input array, the pure pipe doesn't execute. If you replace the input array, the pipe executes and the display is updated, as shown in Figure 4.

圖 4. flyingHeroes 管道把顯示過濾為會飛的英雄

Figure 4. The flyingHeroes pipe filtering the display to flying heroes

上面的例子示範瞭如何更改元件的程式碼來適應某個管道。

The above example demonstrates changing a component's code to accommodate a pipe.

為了讓你的元件更簡單,獨立於那些使用管道的 HTML,你可以用一個不純的管道來檢測複合物件(如陣列)中的變化,如下一節所述。

To keep your component simpler and independent of HTML templates that use pipes, you can, as an alternative, use an impure pipe to detect changes within composite objects such as arrays, as described in the next section.

檢測複合物件中的非純變更

Detecting impure changes within composite objects

要在複合物件內部進行更改後執行自訂管道(例如更改陣列元素),就需要把管道定義為 impure 以檢測非純的變更。每當按鍵或滑鼠移動時,Angular 都會檢測到一次變更,從而執行一個非純管道。

To execute a custom pipe after a change within a composite object, such as a change to an element of an array, you need to define your pipe as impure to detect impure changes. Angular executes an impure pipe every time it detects a change with every keystroke or mouse movement.

雖然非純管道很實用,但要小心使用。長時間執行非純管道可能會大大降低你的應用速度。

While an impure pipe can be useful, be careful using one. A long-running impure pipe could dramatically slow down your app.

透過把 pure 標誌設定為 false 來把管道設定成非純的:

Make a pipe impure by setting its pure flag to false:

@Pipe({ name: 'flyingHeroesImpure', pure: false })
src/app/flying-heroes.pipe.ts
      
      @Pipe({
  name: 'flyingHeroesImpure',
  pure: false
})
    

下面的程式碼顯示了 FlyingHeroesImpurePipe 的完整實現,它擴充套件了 FlyingHeroesPipe 以繼承其特性。這個例子表明你不需要修改其他任何東西 - 唯一的區別就是在管道元資料中把 pure 標誌設定為 false

The following code shows the complete implementation of FlyingHeroesImpurePipe, which extends FlyingHeroesPipe to inherit its characteristics. The example shows that you don't have to change anything else—the only difference is setting the pure flag as false in the pipe metadata.

@Pipe({ name: 'flyingHeroesImpure', pure: false }) export class FlyingHeroesImpurePipe extends FlyingHeroesPipe {}import { Pipe, PipeTransform } from '@angular/core'; import { Flyer } from './heroes'; @Pipe({ name: 'flyingHeroes' }) export class FlyingHeroesPipe implements PipeTransform { transform(allHeroes: Flyer[]) { return allHeroes.filter(hero => hero.canFly); } }
      
      @Pipe({
  name: 'flyingHeroesImpure',
  pure: false
})
export class FlyingHeroesImpurePipe extends FlyingHeroesPipe {}
    

對於非純管道,FlyingHeroesImpurePipe 是個不錯的選擇,因為它的 transform 函式非常簡單快捷:

FlyingHeroesImpurePipe is a good candidate for an impure pipe because the transform function is trivial and fast:

return allHeroes.filter(hero => hero.canFly);
src/app/flying-heroes.pipe.ts (filter)
      
      return allHeroes.filter(hero => hero.canFly);
    

你可以從 FlyingHeroesComponent 派生一個 FlyingHeroesImpureComponent。如下面的程式碼所示,只有範本中的管道發生了變化。

You can derive a FlyingHeroesImpureComponent from FlyingHeroesComponent. As shown in the code below, only the pipe in the template changes.

<div *ngFor="let hero of (heroes | flyingHeroesImpure)"> {{hero.name}} </div>
src/app/flying-heroes-impure.component.html (excerpt)
      
      <div *ngFor="let hero of (heroes | flyingHeroesImpure)">
  {{hero.name}}
</div>
    

要想確認是否在使用者新增英雄時更新了顯示,請參閱現場演練 / 下載範例

To confirm that the display updates as the user adds heroes, see the現場演練 / 下載範例.

從一個可觀察物件中解開封裝資料

Unwrapping data from an observable

可觀察物件能讓你在應用的各個部分之間傳遞訊息。建議在事件處理、非同步程式設計以及處理多個值時使用這些可觀察物件。可觀察物件可以提供任意型別的單個或多個值,可以是同步的(作為一個函式為它的呼叫者提供一個值),也可以是非同步的。

Observables let you pass messages between parts of your application. Observables are recommended for event handling, asynchronous programming, and handling multiple values. Observables can deliver single or multiple values of any type, either synchronously (as a function delivers a value to its caller) or asynchronously on a schedule.

關於可觀察物件的詳細資訊和範例,請參閱可觀察物件概覽

For details and examples of observables, see the Observables Overview.

使用內建的 AsyncPipe接受一個可觀察物件作為輸入,並自動訂閱輸入。如果沒有這個管道,你的元件程式碼就必須訂閱這個可觀察物件來使用它的值,提取已解析的值、把它們公開進行繫結,並在銷燬這段可觀察物件時取消訂閱,以防止記憶體洩漏。 AsyncPipe 是一個非純管道,可以節省元件中的樣板程式碼,以維護訂閱,並在資料到達時持續從該可觀察物件中提供值。

Use the built-in AsyncPipeto accept an observable as input and subscribe to the input automatically. Without this pipe, your component code would have to subscribe to the observable to consume its values, extract the resolved values, expose them for binding, and unsubscribe when the observable is destroyed in order to prevent memory leaks. AsyncPipe is an impure pipe that saves boilerplate code in your component to maintain the subscription and keep delivering values from that observable as they arrive.

下列程式碼範例使用 async 管道將帶有訊息字串( message$ )的可觀察物件繫結到檢視中。

The following code example binds an observable of message strings (message$) to a view with the async pipe.

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) ); } }
src/app/hero-async-message.component.ts
      
      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 請求

Caching HTTP requests

為了使用 HTTP 與後端服務進行通訊HttpClient 服務使用了可觀察物件,並提供了 HTTPClient.get() 方法來從伺服器獲取資料。這個非同步方法會發送一個 HTTP 請求,並返回一個可觀察物件,它會發出請求到的響應資料。

To communicate with backend services using HTTP, the HttpClient service uses observables and offers the HTTPClient.get() method to fetch data from a server. The asynchronous method sends an HTTP request, and returns an observable that emits the requested data for the response.

AsyncPipe 所示,你可以使用非純管道 AsyncPipe 接受一個可觀察物件作為輸入,並自動訂閱輸入。你也可以建立一個非純管道來建立和快取 HTTP 請求。

As shown in the previous section, you can use the impure AsyncPipe to accept an observable as input and subscribe to the input automatically. You can also create an impure pipe to make and cache an HTTP request.

每當元件執行變更檢測時就會呼叫非純管道,在 CheckAlways 策略下會每隔幾毫秒執行一次。為避免出現效能問題,只有當請求的 URL 發生變化時才會呼叫該伺服器(如下例所示),並使用該管道快取伺服器的響應。顯示如下:

Impure pipes are called whenever change detection runs for a component, which could be every few milliseconds for CheckAlways. To avoid performance problems, call the server only when the requested URL changes, as shown in the following example, and use the pipe to cache the server response. The tabs show the following:

  • fetch 管道( fetch-json.pipe.ts )。

    The fetch pipe (fetch-json.pipe.ts).

  • 一個用於示範該請求的挽具元件(hero-list.component.ts),它使用一個範本,該範本定義了兩個到該管道的繫結,該管道會向 heroes.json 檔案請求英雄陣列。第二個繫結把 fetch 管道與內建的 JsonPipe 串聯起來,以 JSON 格式顯示同一份英雄資料。

    A harness component (hero-list.component.ts) for demonstrating the request, using a template that defines two bindings to the pipe requesting the heroes from the heroes.json file. The second binding chains the fetch pipe with the built-in JsonPipe to display the same hero data in JSON format.

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

在上面的例子中,管道請求資料時的剖面展示了如下幾點:

In the above example, a breakpoint on the pipe's request for data shows the following:

  • 每個繫結都有自己的管道實例。

    Each binding gets its own pipe instance.

  • 每個管道實例都會快取自己的 URL 和資料,並且只調用一次伺服器。

    Each pipe instance caches its own URL and data and calls the server only once.

fetchfetch-json 管道會顯示英雄,如圖 5 所示。

The fetch and fetch-json pipes display the heroes as shown in Figure 5.

圖 5. fetchfetch-json 管道顯示了這些英雄。

Figure 5. The fetch and fetch-json pipes displaying the heroes

內建的 JsonPipe 提供了一種方法來診斷一個離奇失敗的資料繫結,或用來檢查一個物件是否能用於將來的繫結。

The built-in JsonPipe provides a way to diagnose a mysteriously failing data binding or to inspect an object for future binding.

管道的優先順序

Pipes and precedence

管道運算子要比三目運算子(?:)的優先順序高,這意味著 a ? b : c | x 會被解析成 a ? b : (c | x)

The pipe operator has a higher precedence than the ternary operator (?:), which means a ? b : c | x is parsed as a ? b : (c | x). The pipe operator cannot be used without parentheses in the first and second operands of ?:.

由於這種優先順序設定,如果你要用管道處理三目元算符的結果,就要把整個表示式包裹在括號中,比如 (a ? b : c) | x

Due to precedence, if you want a pipe to apply to the result of a ternary, wrap the entire expression in parentheses; for example, (a ? b : c) | x.

<!-- use parentheses in the third operand so the pipe applies to the whole expression --> {{ (true ? 'true' : 'false') | uppercase }}
src/app/precedence.component.html
      
      <!-- use parentheses in the third operand so the pipe applies to the whole expression -->
{{ (true ? 'true' : 'false') | uppercase }}