一直想寫關於 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
首先咱們先來介紹一下,自定義過濾器要實現的功能,即要對如下模板中 {{}}
插值表達式中顯示的數據進行格式化 (添加相應後綴)。
<!-- template code --> <p>You came {{ '1' }}</p> <p>You came {{ '2' }}</p>
格式化爲:
<!-- when compiled --> <p>You came 1st</p> <p>You came 2nd</p>
瞭解完需求後,接下來咱們開始來實現該功能。
在 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過濾器
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
爲了讓咱們的 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> `, ... };
除了在模板中使用 |
進行數據過濾外,咱們還能夠在 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 中,已經沒有了 filter
過濾器,取而代之的是 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 方法
爲了可以在組件中使用已建立的 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 { }
在 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 } }
在 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,相信你已經知道了答案了。