如果对 RxJS 的 operators (操作符) 不熟悉的话,建议读者在阅读本文时,先阅读 RxJS – Observables, observers 和 operators 简介 这篇文章。
基础知识
什么是 Operator
Operator 是一个函数,它接收一个 Observable 对象,然后返回一个新的 Observable 对象。当我们订阅新返回的 Observable 对象时,它内部会自动订阅前一个 Observable 对象。
如何为 Observable 添加操作符
比较常见的有以下三种方式:
1.使用 ES7 函数绑定运算符 ::
(可使用 BabelJS 进行转换)
someObservable::mySimpleOperator(x => x + '!');
2.继承 Observable 类,并重写 lift()
方法
class MyObservable extends Observable {
lift(operator) {
const observable = new MyObservable(); //<-- important part here
observable.source = this;
observable.operator = operator;
return observable;
}
// put it here .. or ..
customOperator() {
/* do things and return an Observable */
}
}
// ... put it here...
MyObservable.prototype.mySimpleOperator = mySimpleOperator;
3.直接添加到 Observable.prototype
对象上
Observable.prototype.mySimpleOperator = mySimpleOperator;
someObservable.mySimpleOperator(x => x + '!');
自定义 toJSON 操作符
当我们使用 Angular HTTP 服务时,我们需要调用 Response 对象的 json()
方法把服务端接口返回的数据,转换为 JSON 对象,例如:
this.http.get('https://api.github.com/orgs/angular/members?page=1&per_page=5')
.map(res => res.json());
对于每个接口,我们都需要调用 map 操作符对返回的数据做对应的处理。那能不能简化这个操作呢?答案是有的,我们可以通过自定义一个 toJSON 操作符来简化上述的过程。具体实现如下:
function toJSON<T>(): Observable<T> {
return this.map(( response : Response ) => response.json());
}
上面代码中,this
指向源 Observable 对象,即调用 http 对象的 get()
方法后返回的 Observable 对象。此外我们直接返回了调用 map()
操作符后新建的 Observable 对象。
为了能够使用我们自定义的 toJSON
操作符,我们需要把它添加到 Observable 的原型对象上:
Observable.prototype.toJSON = toJSON;
最后的一件事是我们需要添加以下的定义:
declare module "rxjs/Observable" {
interface Observable<T> {
toJSON : typeof toJSON;
}
}
完整示例
custom-operators.ts
import { Observable } from 'rxjs/Observable';
function toJSON<T>(): Observable<T> {
return this.map(( response : Response ) => response.json());
}
Observable.prototype.toJSON = toJSON;
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from "@angular/http";
import { AppComponent } from './app.component';
import './custom-operators';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
import { Observable } from 'rxjs/Observable';
import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
interface Member {
id: string;
login: string;
avatar_url: string;
}
@Component({
selector: 'app-root',
template: `
<h3>Angular Orgs Members</h3>
<ul *ngIf="members">
<li *ngFor="let member of members;">
<p>
<img [src]="member.avatar_url" width="48" height="48"/>
ID:<span>{{member.id}}</span>
Name: <span>{{member.login}}</span>
</p>
</li>
</ul>
`
})
export class AppComponent implements OnInit {
members: Member[];
constructor(private http: Http) { }
ngOnInit() {
this.http.get(`https://api.github.com/orgs/angular/members?page=1&per_page=5`)
.toJSON<Member[]>() // 使用自定义 toJSON 操作符
.subscribe(data => {
if (data) this.members = data;
});
}
}
typings.d.ts
// src/typings.d.ts (在该文件下,新增以下内容)
export declare function toJSON<T>(): Observable<T>;
declare module "rxjs/Observable" {
interface Observable<T> {
toJSON : typeof toJSON;
}
}