我怎样才能 "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));
我正在创建一个 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()
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));