Angular 角度2+;OnPush:更改检测在数据库回调上不起作用

Angular 角度2+;OnPush:更改检测在数据库回调上不起作用,angular,pouchdb,angular2-changedetection,Angular,Pouchdb,Angular2 Changedetection,我在一个离子应用程序中使用Angular 2,并结合PockDB作为数据库。出于性能原因,我选择在任何地方使用OnPush+异步管道。现在,我偶然发现了这样一个事实,即如果PockDB事件导致了更改,那么它不会反映在模板中: 我有一个数据库类,它只计算数据库更改的数量: @Injectable() export class Database { foo$ = new BehaviorSubject(0); (...) sync(url, options) { this.p

我在一个离子应用程序中使用Angular 2,并结合PockDB作为数据库。出于性能原因,我选择在任何地方使用OnPush+异步管道。现在,我偶然发现了这样一个事实,即如果PockDB事件导致了更改,那么它不会反映在模板中:

我有一个数据库类,它只计算数据库更改的数量:

@Injectable()
export class Database {
  foo$ = new BehaviorSubject(0);

  (...)

  sync(url, options) {
    this.pouchdb.sync(url, options)
      .on('change', this.changed.bind(this));
    this.foo$.subscribe(count => console.debug('the count is', count));
  }

  changed() {
    this.foo$.next(this.foo$.value + 1);
  }
}
在模板中,我使用异步管道显示数字:

{{ db.foo$|async }}
但这不起作用:即使有数据库更改,数字也会被卡住(控制台输出显示数据库更改的数量)。但是,如果我点击某个东西,数字会突然跳到正确的值

我觉得这很惊人,因为我认为异步管道是通过订阅一个可观察对象来工作的,一旦这个可观察对象发出某种信息,模板中的值就会更新。显然,这在这里不起作用,因为我知道可观察的对象发出了一些东西(正如我从控制台输出中看到的),但是更改没有反映在模板中

你知道是什么导致这种行为/如何改变它吗

其他信息

为了确保这两个函数的组合导致了问题,我使用了这个函数

sync(url, options) {
  window.setInterval(this.changed.bind(this), 1000);
}
这会导致模板中的值每秒正确更新一次

编辑

我发现这个伪代码

window.setInterval(() => {}, 1000);

修复了该问题,因为它可能每秒触发一次更改检测。我不明白的是,为什么异步管道没有检测到更改;DR:代码在angular区域之外运行,所以angular在下一个更改检测周期之前不会处理更改

使用chromedev工具,在调用changed时放置一个断点。请注意,如果您看到PockDB调用和setInterval之间的调用堆栈存在差异。具体来说,setInterval可能在调用周围有一些区域操纵,而PockDB调用没有。如果是,你可以:

  • 查看初始化的顺序——或者是否可以对PockDB进行猴子补丁,以在angular zone内调用承诺
  • 在数据库构造函数中注入
    private zone:NgZone
    ,并使用
    this.zone.run(()=>this.foo$.next(this.foo$.value+1))内部更改(),以确保进入角度区域
如果所有其他操作都失败,请退出并手动标记更改检测()。然而,由于这必须在使用db的组件中完成,这绝对是一个可怕的抽象泄漏


享受。让我们知道你发现了什么。

在我看来,肯定还有更多。我将删除ChangeDetectionStrategy.OnPush,以确保这确实是问题的一部分。您确认了吗?直觉:您正在查看数据库的两个实例。模板中使用的与调用.sync的模板不同。谢谢您的回答!你是对的,责任不在推送,ChangeDetectionStrategy.Default也一样。但是,我完全确定没有两个不同的数据库实例。我注意到另一件事:如果我执行这个伪代码,一切都正常:window.setInterval(()=>{},1000);据我所知,这似乎确实是一个变化检测问题,setInterval每秒触发一次变化检测。但我不知道为什么AsiNC管道没有检测到它的变化……如果AsiNC管道不调用变化检测,我会认为它是一个bug。code>setInverval()
可能与
setTimeout()
ApplicationRef.tick()
相同,这会导致应用程序范围内的更改检测谢谢,zone.run()是一种解决方法。一旦我有更多的时间,我会详细研究这个问题,并让你知道我发现了什么