8

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

    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“猴子补丁”以确保它知道何时需要检测更改)而发生的,因此“准备登录”消息会true在适当的时间切换到.

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

如何signInResponse$以一种NgZone知道在解决任何订阅后检测更改的方式包装可观察对象?

更新

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

4

3 回答 3

9

对于带有 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));
于 2018-05-29T11:46:13.543 回答
8

您可以创建一个新的observeOnZone运算符,用于“猴子补丁”任何可观察的。就像是:

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);
于 2016-06-02T15:58:15.457 回答
2

您可以使用强制代码进入 Angulars 区域zone.run()

constructor(private zone:NgZone) {}

someMethod() {
  signinResponse$.subscribe(value => {
    zone.run(() => doSomething());
  });
}
于 2016-06-02T14:56:28.887 回答