Angular 中自定义 toJSON 操做符

若是对 RxJS 的 operators (操做符) 不熟悉的话,建议读者在阅读本文时,先阅读 RxJS - Observables, observers 和 operators 简介 这篇文章。javascript

基础知识

什么是 Operator

Operator 是一个函数,它接收一个 Observable 对象,而后返回一个新的 Observable 对象。当咱们订阅新返回的 Observable 对象时,它内部会自动订阅前一个 Observable 对象。java

如何为 Observable 添加操做符

比较常见的有如下三种方式:git

1.使用 ES7 函数绑定运算符 :: (可以使用 BabelJS 进行转换)github

someObservable::mySimpleOperator(x => x + '!');

2.继承 Observable 类,并重写 lift() 方法typescript

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 对象上json

Observable.prototype.mySimpleOperator = mySimpleOperator;
someObservable.mySimpleOperator(x => x + '!');

自定义 toJSON 操做符

当咱们使用 Angular HTTP 服务时,咱们须要调用 Response 对象的 json() 方法把服务端接口返回的数据,转换为 JSON 对象,例如:bootstrap

this.http.get('https://api.github.com/orgs/angular/members?page=1&per_page=5')
    .map(res => res.json());

对于每一个接口,咱们都须要调用 map 操做符对返回的数据作对应的处理。那能不能简化这个操做呢?答案是有的,咱们能够经过自定义一个 toJSON 操做符来简化上述的过程。具体实现以下:segmentfault

function toJSON<T>(): Observable<T> {
  return this.map(( response : Response ) => response.json());
}

上面代码中,this 指向源 Observable 对象,即调用 http 对象的 get() 方法后返回的 Observable 对象。此外咱们直接返回了调用 map() 操做符后新建的 Observable 对象。api

为了可以使用咱们自定义的 toJSON 操做符,咱们须要把它添加到 Observable 的原型对象上:babel

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;
  }
}

参考资源