Pipes 是一個簡單的涵式,主要的功能是將我們要顯示的值/數據以另一種型式呈現,內建的 pipes 功能有大/小寫轉換、貨幣轉成本地格式、數字轉百分比、日期格式轉換,或是將值轉換成 JSON 格式,等等….

這些內建 pipes 最大的特點:

  • 可以直接在 Template 中使用,無須在 Component 內撰寫轉換邏輯。
  • 同樣的 pipe ,可以重複使用在不同的地方。

內建的 pipes,已經可以解決大部分的需求,但專案是多變的,客戶會提出怎樣的需求,我們通常無法左右,若轉換需求內建 Pipes 辦不到,且很多地方會用到,難道只能靠共用函式處理嗎?其實,我們可以使用更優雅的方式解決,以下我就來示範如何客製化 Pipe 元件。

目前有個情境是這樣的:將英哩轉公里。

  1. 建立新專案

  2. app.component.html,範本如下:

    1
    2
    3
    4
    <label>英里(mile):</label>
    <input (input)="onConvert($event.target.value)" />
    <p></p>
    公里(km):{{ distance }}
  3. app.component.ts

    1
    2
    3
    4
    5
    6
    7
    8
    export class AppComponent {

    distance = 0;

    onConvert(value: string) {
    this.distance = +value;
    }
    }
  4. 呈現結果:

  5. 使用 Angular CLI 建立新的 pipe

    1
    ng g pipe convert --skip-tests

    g – generate
    pipe – 建立的類別為 pipe
    convert – 自訂 pipe 的名稱
    skip-tests – 不需要測試檔

  6. Angular 會自動將 pipe 加入到根模組(app.module.ts)的 declarations陣列

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @NgModule({
    declarations: [
    AppComponent,
    ConvertPipe
    ],
    imports: [
    BrowserModule
    ],
    providers: [],
    bootstrap: [AppComponent]
    })
  7. 檢視新增的 convert.pipe.ts 結構

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Pipe({
    name: 'convert'
    })
    export class ConvertPipe implements PipeTransform {

    transform(value: unknown, ...args: unknown[]): unknown {
    return null;
    }
    }

    @Pipe: 這是 decorator,將此 class 標記為 pipe。
    PipeTransform:讓 class 實作此 PipeTransform 介面,呼叫此介面的 transform() 函式,執行轉換。
    transform():轉換函式,該函式會將綁定的值,作為第一個引數,也就是 Template 中的 distance,並回傳轉換後的值。

  8. transform() 內實現如何轉換:

    1
    2
    3
    transform(value: number, ...args: string[]): unknown {
    return value * 1.06;
    }

    我們將參數 value 的型別,由 unknown 改為 number

  9. 如同 @Componentselector 一樣,在 Template 找到 @Pipename ,我們就可以使用客製化 pipe:

    1
    公里(km):{{ distance | convert }}
  10. 結果:

  11. 如果需求改成可以將將英哩轉公里、碼、呎,那該如何判斷需轉成何種單位?這時會需要第二個參數(…args)

    1
    2
    3
    transform(value: number, ...args: string[]) {
    console.log(args);
    }

    …args 是陣列型別,我們改成字串陣列。
    如果 pipe 接受多個參數,需使用冒號(:)隔開

    1
    公里(km):{{ distance | convert: "km":"yd" }}
  12. 輸入的引數會以陣列的形式呈現

  13. 第二個參數,我們使用字串即可,再搭配 switch 篩選單位換算
    convert.pipe.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    transform(value: number, unit: string) {
    switch (unit) {
    case 'km':
    return value * 1.60935;
    case 'yd':
    return value * 1760;
    case 'ft':
    return value * 5280;
    default:
    return value;
    }
    }

    app.component.html

    1
    2
    3
    4
    5
    6
    7
    8
    <label>英里(mile):</label>
    <input (input)="onConvert($event.target.value)" />
    <p></p>
    公里(km):{{ distance | convert: "km" }}
    <p></p>
    碼(yd):{{ distance | convert: "yd" }}
    <p></p>
    英呎(ft):{{ distance | convert: "ft" }}
  14. 結果:

以上就是客製化 pipe 的示範,

優點:

  • 可以在不同地方共用同一個 pipe,不須再撰寫重複邏輯。
  • 若是複雜的邏輯,pipe 可以將邏輯抽出來,簡化 component 內的程式碼,讓 component 更好維護。