一直想写关于 Angular 1.x 与 Angular 2.x (Angular 4.x 已发布) 区别的文章,方便 Angular 1.x 的用户快速的过渡到 Angular 2.x。在浏览文章的时候,发现 Todd Motto 大神,已经写了相关的系列文章。英文好的同学,建议直接阅读 Creating a custom filter (pipe) in Angular 原文哈,因为我并不打算完整地翻译。废话不多说,接下来我们开始进入正题。
目录
-
Angular 1.x
Creating a custom filter
Using filters in template
Passing arguments to filters
Filtering in Controllers with $filter()
-
Angular 2
Creating a custom pipe
Using pipes in template
Passing arguments to pipes
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,相信你已经知道了答案了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。