Angular 2 Pipe vs Angular 1 Filter

一直想寫關於 Angular 1.x 與 Angular 2.x (Angular 4.x 已發佈) 區別的文章,方便 Angular 1.x 的用戶快速的過渡到 Angular 2.x。在瀏覽文章的時候,發現 Todd Motto 大神,已經寫了相關的系列文章。英文好的同窗,建議直接閱讀 Creating a custom filter (pipe) in Angular 原文哈,由於我並不打算完整地翻譯。廢話很少說,接下來咱們開始進入正題。javascript

目錄

  • Angular 1.xjava

    • Creating a custom filterweb

    • Using filters in templatetypescript

    • Passing arguments to filtersbootstrap

    • Filtering in Controllers with $filter()segmentfault

  • Angular 2app

    • Creating a custom pipeide

    • Using pipes in template性能

    • Passing arguments to pipesthis

    • Filtering in Component classes with pipes

Angular 1.x

首先咱們先來介紹一下,自定義過濾器要實現的功能,即要對如下模板中 {{}} 插值表達式中顯示的數據進行格式化 (添加相應後綴)。

<!-- template code -->
<p>You came {{ '1' }}</p>
<p>You came {{ '2' }}</p>

格式化爲:

<!-- when compiled -->
<p>You came 1st</p>
<p>You came 2nd</p>

瞭解完需求後,接下來咱們開始來實現該功能。

Creating a custom filter

在 Angular 1.x 中,咱們經過 filter() API 來建立自定義過濾器,具體代碼以下:

const ordinal = () => {
  return value => {
    var suffix = ''; // 後綴名
    var last = value % 10;
    var specialLast = value % 100;
    if (!value || value < 1) {
      return value;
    }
    if (last === 1 && specialLast !== 11) {
      suffix = 'st';
    } else if (last === 2 && specialLast !== 12) {
      suffix = 'nd';
    } else if (last === 3 && specialLast !== 13) {
      suffix = 'rd';
    } else {
      suffix = 'th';
    }
    return value + suffix;
  };
};

angular
  .module('app') // 獲取已建立的app模塊
  .filter('ordinal', ordinal); // 使用filter API建立ordinal過濾器

Using filters in template

const app = {
  template: `
    <div>
      <ul>
        <li ng-repeat="num in $ctrl.numbers">
          {{ num | ordinal }}
        </li>
      </ul>
    </div>
  `,
  controller() {
    this.numbers = [
      1,2,3,4,5,6,7,8,9,10,
      11,12,13,14,15,16,17,18,19,20
    ];
  }
};

angular
  .module('app')
  .component('app', app);

以上代碼運行後的結果 - Plunker

Passing arguments to filters

爲了讓咱們的 ordinal 過濾器更加靈活,它應該能夠根據用戶設置的參數,來動態的顯示內容。接下來咱們來看一下,Angular 1.x 中如何傳遞參數給過濾器。

const ordinal = () => {
  // passing another argument
  return (value, anotherValue) => {
    // do something with `value` and `anotherValue`
    // and return a new value
  };
};

angular
  .module('app')
  .filter('ordinal', ordinal);

更新後的 app 組件,以下:

const app = {
  template: `
    <div>
      <input ng-model="searchValue">
      <ul>
        <li ng-repeat="num in $ctrl.numbers">
          {{ num | ordinal:searchValue }}
        </li>
      </ul>
    </div>
  `,
  ...
};

Filtering in Controllers with $filter()

除了在模板中使用 | 進行數據過濾外,咱們還能夠在 Controller 中使用 $filter 服務,進行數據處理。出於性能考慮,更推薦在 Controller 中使用 $filter 服務進行數據處理 (詳細信息請參考 - Using Controller $filters to prevent $digest performance issues)。使用 $filter() 服務的具體示例以下:

const app = {
  template: `
    <div>
      <ul>
        <li ng-repeat="num in $ctrl.numbers">
          {{ num }}
        </li>
      </ul>
    </div>
  `,
  controller($filter) {
    let numbers = [
      1,2,3,4,5,6,7,8,9,10,
      11,12,13,14,15,16,17,18,19,20
    ];
    // iterate the existing collection before binding
    // returns a new filtered collection
    this.numbers = numbers.map(number => $filter('ordinal')(number));
  }
};

Angular 2

在 Angular 2 中,已經沒有了 filter 過濾器,取而代之的是 pipe 管道。接下來咱們來看一下,如何自定義管道。

Creating a custom pipe

ordinal.pipe.ts

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

@Pipe({
    name: 'ordinal'
})
export class OrdinalPipe implements PipeTransform {
    transform(value: number): string {
        let suffix = '';
        let last = value % 10;
        let specialLast = value % 100;
        if (!value || value < 1) {
            return '' + value;
        }
        if (last === 1 && specialLast !== 11) {
            suffix = 'st';
        } else if (last === 2 && specialLast !== 12) {
            suffix = 'nd';
        } else if (last === 3 && specialLast !== 13) {
            suffix = 'rd';
        } else {
            suffix = 'th';
        }
        return value + suffix;
    }
}

經過以上示例,咱們來總結一下自定義管道的步驟。自定義管道分爲兩個步驟:

  • 使用 @Pipe 裝飾器定義 Pipe 的 metadata 信息,如 Pipe 的名稱 - 即 name 屬性

  • 實現 PipeTransform 接口中定義的 transform 方法

Using pipes in template

爲了可以在組件中使用已建立的 pipe (管道),咱們必須在設置 @NgModule metadata 信息時,在 declarations 屬性中添加已建立的管道。

app.component.ts

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

@Component({
  selector: 'exe-app',
  template: `
    <div>
      <ul>
        <li *ngFor="let num of numbers">
          {{ num | ordinal }}
        </li>
      </ul>
    </div>
    `
})
export class AppComponent {
  numbers: Array<number>;

  constructor() {
    this.numbers = [
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
      11, 12, 13, 14, 15, 16, 17, 18, 19, 20
    ];
  }
}

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { OrdinalPipe } from './ordinal.pipe';

@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent, OrdinalPipe],
  bootstrap: [AppComponent]
})
export class AppModule { }

Passing arguments to pipes

在 Angular 2 中,咱們也能夠爲管道添加參數,具體示例以下:

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

@Pipe({
  name: 'ordinal'
})
export class OrdinalPipe implements PipeTransform {
  // passing another argument
  transform(value: number, anotherValue: string): string {
    // do something with `value` and `anotherValue`
    // and return a new value
  }
}

Filtering in Component classes with pipes

在 Angular 1.x 中,咱們推薦在 Controller 中使用 $filter() 以提升程序的性能。在 Angular 2 中,咱們一樣也能夠在組件類中,經過注入 pipe 實例,而後進行數據處理。具體示例以下:

import { Component } from '@angular/core';
import { OrdinalPipe } from './ordinal.pipe';

@Component({
  selector: 'exe-app',
  template: `
    <div>
      <ul>
        <li *ngFor="let num of numbers">
          {{ num }}
        </li>
      </ul>
    </div>
    `,
  providers: [OrdinalPipe]
})
export class AppComponent {
  numbers: Array<string>;

  constructor(private pipe: OrdinalPipe) {
    let numbers = [
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
      11, 12, 13, 14, 15, 16, 17, 18, 19, 20
    ];
    this.numbers = numbers.map(number => this.pipe.transform(number));
  }
}

以上示例爲了可以經過構造注入的方式,注入 OrdinalPipe 管道實例,咱們必須在組件的 metadata 信息中,定義 providers 屬性。

我有話說

1.Angular 2 中要掌握管道,還須要瞭解哪些知識點?

要全面掌握 Angular 2 中的管道,你還需瞭解如下內容:

  • Angular 2 內建管道使用及分類

  • 管道鏈

  • 管道分類 (pure & impure)

  • @Pipe 裝飾器與管道的執行 (可選)

詳細內容請參考 - Angular 2 Pipe

2.在組件類中注入管道實例,若是沒有在組件的 metadata 信息中,定義 providers 屬性會出現什麼問題?(該問題,初學者能夠先略過哈)

在回答這個問題以前,咱們先來看一下未設置 providers 屬性和已設置 providers 屬性,編譯後的代碼:

未設置 providers 屬性 :

View_AppComponent_Host0.prototype.injectorGetInternal =  // 注入器獲取內部依賴項
  function(token,requestNodeIndex,notFoundResult) { 
    var self = this;
    if (((token === jit_AppComponent1) && (0 === requestNodeIndex))) { 
      return self._AppComponent_0_3.context; 
    }
    return notFoundResult;
};

已設置 providers 屬性 :

View_AppComponent_Host0.prototype.injectorGetInternal =   // 注入器獲取內部依賴項
  function(token,requestNodeIndex,notFoundResult) {
    var self = this;
    if (((token === jit_OrdinalPipe1) && (0 === requestNodeIndex))) { 
      return self._OrdinalPipe_0_3; 
    }
    if (((token === jit_AppComponent2) && (0 === requestNodeIndex))) { 
      return self._AppComponent_0_4.context; 
    }
    return notFoundResult;
};

咱們再來看一下未設置 providers 屬性 時,控制檯的輸出結果:

圖片描述

爲何提示 No provider for OrdinalPipe,相信你已經知道了答案了。

相關文章
相關標籤/搜索