【问题标题】:Angular 2 + OnPush: Change detection does not work on pouchdb callbacksAngular 2 + OnPush:更改检测不适用于 pouchdb 回调
【发布时间】:2017-01-22 12:50:46
【问题描述】:

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

我有一个只计算数据库更改次数的数据库类:

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

但这不起作用:数字卡住了,即使有数据库更改(控制台输出显示数据库更改的次数)。但是,如果我点击某个东西,数字会突然跳到正确的值。

我觉得这很令人惊讶,因为我认为异步管道通过订阅一个 observable 来工作,一旦这个 observable 发出一些东西,模板内的值就会更新。显然,这在这里不起作用,因为我知道 observable 发出了一些东西(正如我从控制台输出中看到的那样),但更改并未反映在模板中。

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

其他信息

为了确定是 pouchDB Angular 的组合导致了问题,我改用了这个函数

sync(url, options) {
  window.setInterval(this.changed.bind(this), 1000);
}

这会导致模板内的值每秒正确更新。

编辑

我发现这个伪代码

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

解决了这个问题,因为它可能每秒触发一次更改检测。我不明白的是,为什么异步管道没有它就无法检测到更改?

【问题讨论】:

  • 在我看来肯定还有更多。我会删除 ChangeDetectionStrategy.OnPush 以确保这确实是问题的一部分。你确认了吗?
  • 直觉:您正在查看数据库的两个实例。模板中使用的那个与调用 .sync 的那个不同。
  • 感谢您的回答!你是对的,不是 OnPush 负责,ChangeDetectionStrategy.Default 也是如此。但是,我完全确定没有两个不同的数据库实例。我注意到另一件事:如果我执行这个虚拟代码,一切正常:window.setInterval(() => {}, 1000);所以据我了解,这似乎确实是变化检测的问题, setInterval 每秒触发一次变化检测。但是我不明白为什么异步管道没有它就无法检测到更改...
  • 如果异步管道不调用更改检测,我会认为这是一个错误。 setInverval() 可能与 setTimeout()ApplicationRef.tick() 相同,这会导致应用程序范围的更改检测

标签: angular pouchdb angular2-changedetection


【解决方案1】:

TL;DR:代码在角度区域之外运行,因此在下一个变化检测周期之前,角度不会处理变化。

使用 chrome 开发工具,在调用更改时放置一个断点。请注意,如果您发现 pouchdb 调用和 setInterval 之间的调用堆栈有所不同。具体来说, setInterval 可能有一些围绕调用的区域绑定,而 pouchdb 调用没有。如果是这样,您可以:

  • 查看初始化顺序 - see this SO - 或者是否可以对 pouchdb 进行猴子修补以在角度区域内调用承诺。
  • 在数据库构造函数中注入private zone: NgZone 并在changed() 中使用this.zone.run(() => this.foo$.next(this.foo$.value + 1)); 以保证您进入角度区域。

如果所有其他方法都失败了,请屈服并手动标记更改检测 (using ChangeDetectionRef)。然而,由于这必须在使用 db 的组件中完成,这绝对是一个可怕的抽象泄漏。

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

【讨论】:

  • 谢谢,zone.run() 可以作为一种解决方法。一旦我有更多的时间,我会详细研究这个问题,并会告诉你我发现了什么
猜你喜欢
  • 2020-10-04
  • 2017-09-28
  • 1970-01-01
  • 2017-08-08
  • 1970-01-01
  • 2021-03-06
  • 2019-09-18
  • 2020-10-31
  • 2017-06-07
相关资源
最近更新 更多