選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
5 分鐘閱讀時間 (1019 個字)

Angular #38 - Directives and Pipes[4]

shutterstock_198004562
  • 自定義 Pipe

    • Pipe 會在 template 上針對資料作格式化

      • 實際上它就是一個 function,收的參數就是資料,並在渲染 template 之前先針對資料作一些處理
      • 這樣的行為很類似 OnInit, 當 template 被渲染時會先檢查是否有 pipe 的存在,如果有就會先把資料丟給 pipe 再渲染
      • Pipe 大致上分為兩種
        • Pure pipes
        • Impure pipes
      • 兩種 Pipe 之間的差異如下:
        • 持有狀態與否, impure pipes 會持有狀態
        • Pure pipe 只在傳入 pipe 的值異動時作用, 而 impure pipes 在每一次 change detection 被觸發時都會作用
        • 前面提到的差異會決定你要定義哪一種 Pipe,然而絕大多數的情境下 Pure pipe 就足以應對
        • 之所以叫 Pure pipes 是因為它實作的是一個 pure function(不帶狀態的函數,換句話說只處理I/O,且給定同樣的 input,總是會有同樣的 output)
    • 話不亦遲,來建個 pipe

        ng g p pipes/change
      • 這條指令會建立 src/pipes/change.pipe.ts

      • 接著實作內容:

          import { Pipe, PipeTransform } from '@angular/core';
          import { CurrencyPipe, PercentPipe } from '@angular/common';
          import { StockInterface } from '../services/stocks.service';
        
          @Pipe({
            name: 'change'
          })
          export class ChangePipe implements PipeTransform {
        
            constructor(
              private currencyPipe: CurrencyPipe,
              private percentPipe: PercentPipe
            ) {}
        
            transform(stock: StockInterface, showPercent: boolean = true): any {
              let value = `${this.currencyPipe.transform(stock.change, 'USD', 'symbol', '.2')}`;
              if(showPercent) {
                value += `${this.percentPipe.transform(stock.changeInPercent, '.2')}`;
              }
              return value;
            }
        
          }
        • 在建構子注入 CurrencyPipe 與 PercentPipe,為的是透過既有的 Pipe 同時處理兩種轉換(同時)
        • Pipe 一樣需要 Decorator,且 name 必須為唯一值,但還需要再多實作 PipeTransform 這個介面,這個介面有一個方法 transform
        • transform 的第一個參數是原本的值,後面就是任意數量的任意物件,並回傳任意型態的值
        • 以此 ChangePipe 的實作來看,就是如果第二個參數是 true,就會多透過 PercentPipe 百分比的處理
      • 接著我們就可以在 SummaryCopmonent 內使用 ChangePipe

          {{ stock | change }}
        • 相較於原本的超長版本簡單許多
        • 這樣的優點就是共用性,缺點也會是共用性,不過沒人限制只能定義一個 Pure Pipe 就是了
    • 接著來建立一個 impure pipe

      • Impure 的原因不是因為不單純,是因為每一次即使給相同的 input,也不會得到相同的 output

      • 再次提醒,每次 change detection 發生時,就會觸發 impure pipe 的作用

      • 接著來建立一個叫 ChangeDetectorPipe 的 impure pipe

          ng g p pipes/change-detector
      • 並實作內容如下:

          import { Pipe, PipeTransform } from '@angular/core';
          @Pipe({
            name: 'changeDetector',
            pure: false
          })
          export class ChangeDetectorPipe implements PipeTransform {
            count: number = 0;
            transform(value: any, args?: any): any {
              this.count++;
              console.log(`Component change detection executed ${this.count} times`);
              return value;
            }
          }
        • 特別留意 pure 被設為 false
        • number 這個狀態現在是被 pipe 所維護的,且每一次觸發 transform,其值都會被遞增
      • 再來調整 app.component.html

          <span class="mdl-layout-title">{{'Stock Tracker' | changeDetector}}</span>
        • 打開 console,你應該已經可以看到一些訊息被印出(e.g. component change detection executed 9 times)
        • 這個 pipe 看起來很無用,不過也證明了它的確是跟著 change detection 一同作用的
        • 實際上 Angular 有內建一個 AsyncPipe,它會接收一個 Observable 或 Promise,並在值非同步被回傳回來時顯示在 view 上
      • 接著來建立一個會從後端 API 取值並回填到 view 上的 pipe

          ng g p pipes/news
      • 調整其實作如下:

          import { Pipe, PipeTransform } from '@angular/core';
          import { StocksService } from '../services/stocks.service';
        
          @Pipe({
            name: 'news',
            pure: false
          })
          export class NewsPipe implements PipeTransform {
            cachedSource: string = '';
            news: string = 'loading...';
        
            constructor(private service: StocksService) { }
            transform(source: string, args?: any): any {
              if (source !== this.cachedSource) {
                this.cachedSource = source;
                this.service.getNewsSnapshot(source).subscribe(news => {
                  this.news = `<a href="/${news.url}" target="_blank">${news.title}</a>`;
                });
              }
              return this.news;
            }
          }
        • 同樣的,pure 為 false,因此是 inpure pipe
        • 這個 pipe 維護兩個 state,cachedSourcenews
        • 因為每次 change detection 都會觸發這個 pipe,所以只有在 source 真的有異動的時候才重新打後端 API 取資料回傳
      • 接著就是在 AppComponent 使用這個 pipe

          <div class="mdl-layout-spacer"></div>
        <span>Top News Headline: <span [innerHTML]="'cnbc' | news"></span></span>
        • 每次 AppComponent 載入就都會看到一個 link
        • 雖然這樣看起很酷,但實際上用一個 Component 來處理會比較好,因為 Impure pipe 相對難維護
  • 小結:

    • Pipe 就到此告一段落了
      • 從這幾篇我們知道了 Pipe 有兩種
      • 我們也理解到 pure pipe 足以應付絕大多數的情境
      • Impure pipe 唯一有用的大概就是 AsyncPipe 了,因為會有很多的情境我們需要從後端取資料來顯示 e.g. i18n
Java Concurrency #1 - Concurrency?
Angular # 37 - Directives and Pipes[3]

相關文章

 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2025/06/09, 週一

Captcha 圖像