我怎样才能 "monkey patch" 成为 Zone.js 的 Observable?

How can I "monkey patch" an Observable for Zone.js?

我正在创建一个 Angular 2 组件,并且 Angular 的更改检测在使用特定 Observable 模式时对我不起作用。它看起来像这样:

    let getResult$ = this.http.get('/api/identity-settings');

    let manager$ = getResult$
        .map((response) => /* -- create manager object -- */);

    let signinResponse$ = manager$
        .flatMap(manager => manager.processSigninResponse());

    this.readyToLogin$ = manager$.map(() => true).startWith(false);
    this.isLoggedIn$ = signinResponse$.map(() => true).startWith(false);

然后在我的模板中:

<h1>Ready to Log In: {{readyToLogin$ | async}}</h1>
<h1>Logged In: {{isLoggedIn$ | async}}</h1>

由于 readyToLogin$ Observable 是基于一组同步操作,这些操作是为了响应 http.get() 而发生的(Angular "monkey patches" 以确保它知道何时需要检测更改),"Ready to Log In" 消息会在适当的时候切换到 true

但是,由于 processSignInResponse() 生成 Promise<>,任何订阅 flatMap 结果的事件都与 http 请求的完成事件异步发生。因此,它需要手动干预来通知我的组件区域它需要检查更改。

如何以 NgZone 知道的方式包装 signInResponse$ observable,以便在解决任何订阅后检测更改?

更新

Brandon 的回答一直有效,直到我更新到 RC5,此时事情再次停止工作。结果 the 3rd-party library I was using borked Zone.js。一旦解决了这个问题,就根本不需要使用变通办法了——内置的猴子补丁就可以了!

您可以使用 zone.run()

强制代码进入 Angulars 区域
constructor(private zone:NgZone) {}

someMethod() {
  signinResponse$.subscribe(value => {
    zone.run(() => doSomething());
  });
}

您可以制作一个新的 observeOnZone 运算符,可用于 "monkey patch" 任何可观察对象。类似于:

Rx.Observable.prototype.observeOnZone = function (zone) {
    return Observable.create(observer => {
        var onNext = (value) => zone.run(() => observer.next(value));
        var onError = (e) => zone.run(() => observer.error(e));
        var onComplete = () => zone.run(() => observer.complete());
        return this.subscribe(onNext, onError, onComplete);
    });
};

并像这样使用它:

this.isLoggedIn$ = signinResponse$.map(() => true).startWith(false).observeOnZone(zone);

对于带有 pipable 运算符的 RxJs 6:

private runInZone(zone) {
  return function mySimpleOperatorImplementation(source) {
    return Observable.create(observer => {
      const onNext = (value) => zone.run(() => observer.next(value));
      const onError = (e) => zone.run(() => observer.error(e));
      const onComplete = () => zone.run(() => observer.complete());
      return source.subscribe(onNext, onError, onComplete);
    });
  };
}

用法:

$someObservable.pipe(runInZone(zone));