【问题标题】:Observables in AngularAngular 中的可观察对象
【发布时间】:2020-09-11 10:51:03
【问题描述】:

我正在尝试使用 Observable 来观察另一个类中特定变量的更新。将作为观察部分的 A 类如下所示:

currentSheetNumber = 0;

public updateSheetNumber(): any {
  const measurementDataObservable = new Observable(observer => {
    observer.next(this.currentSheetNumber);
  });
  return measurementDataObservable;
}

想观察变化的B类代码如下:

sheetNumberObserver;
sheetNumber: number = 0;

ngAfterViewInit(): void {
  this.sheetNumberObserver = this.measurementDataService.updateSheetNumber();
  this.sheetNumberObserver.subscribe((sheets) => {
    this.sheetNumber = sheets;
  });
}

不幸的是,当我更改 A 类中的值 currentSheetNumber 时,B 类的值 sheetNumber 没有改变。

有人知道问题可能是什么吗?

【问题讨论】:

  • updateSheetNumber 每次都会创建一个 new 可观察对象,这似乎不对。任何订阅者都只会得到一个值。您可能希望基于 A 中的 subject 公开一个 observable,参见例如blog.jonrshar.pe/2017/Apr/09/async-angular-data.html 我在这里展示了如何使用这种模式。
  • 您指出的那行与我的代码无关,它只是为测试目的而编写的。我从这个问题中删除了它。
  • 我说的是measurementDataObservable = new Observable
  • 您使用的是哪个 Angular 版本?
  • 我正在使用 Angular 9

标签: javascript angular typescript observable


【解决方案1】:

正如@jonrsharpe 指出的那样,就目前的情况而言,您的 observable 只会发出一个值。听起来您想要的是一种在currentSheetNumber 更改时通知订阅者的方法。这是一种可能性,分两步。首先,按照@jonrsharpe 的建议,让我们从您的服务中公开Subject(这是一种特殊的可观察对象):

public currentSheetNumber$ = new Subject();

那么,在 B 类:

ngAfterViewInit() {
  this.sheetNumberObserver = this.measurementDataService.currentSheetNumber$.subscribe((sheets) => {
    this.sheetNumber = sheets;
  });
}

此时,每当一个值“下一个”到 currentSheetNumber$ 主题上时,this.sheetNumber(在 B 类中)都会相应地更新。

但是,目前,currentSheetNumber$ 主题与您的currentSheetNumber 变量(在您的服务中)没有任何关联。也就是说,当您更改currentSheetNumber 时,currentSheetNumber$ 没有任何反应。我们需要将这两者“绑定”在一起,以便currentSheetNumber 的更新由currentSheetNumber$ 发出。

一种方法是添加一个特殊的方法来分配currentSheetNumber,并且使用该方法修改这个变量。在这个方法中,我们可以更新currentSheetNumber 将新值发送给我们主题的订阅者:

setCurrentSheetNumber(value: number) {
  // Update the value of `currentSheetNumber`...
  this.currentSheetNumber = value;
  // ...and send the new value to subscribers of `currentSheetNumber$`
  this.currentSheetNumber$.emit(value);
}

这需要您将任何对currentSheetNumber 的分配替换为对setCurrentSheetNumber 的调用。

这应该可行,但它涉及遵循我们无法强制执行的规则(不是对变量进行“直接”分配)。这是难以诊断的错误的潜在来源。

幸运的是,有一种方法可以稍微调整当前设置,以提供更简单的体验。我们可以用一对“getter/setter”方法替换currentSheetNumber变量。这将允许我们在分配给currentSheetNumber 时添加额外的“下一个”逻辑,而不必担心调用单独的方法:

get currentSheetNumber(): value {
  // What goes here?
}

set currentSheetNumber(value: number) {
  this.currentSheetNumber$.next(value);
}

现在,每当进行像 this.currentSheetNumber = 42 这样的赋值时,新值(在本例中为 42)将发送给 currentSheetNumber$ 的所有订阅者。我们快到了。

我们需要解决的最后一个问题是:既然我们已经用 getter/setter 对替换了 currentSheetNumber,那么我们实际上在哪里存储这个值?我们有几个选择。一种是添加另一个变量,比如_currentSheetNumber,我们用它来维护当前值,但我们从不直接与之交互。看起来像这样:

_currentSheetNumber = 0;

get currentSheetNumber(): number {
  return this._currentSheetNumber;
}

set currentSheetNumber(value: number) {
  this._currentSheetNumber = value;
  this.currentSheetNumber$.next(value);
}

这不仅增加了我们现在需要考虑的额外变量,而且我们又回到了必须遵守与以前类似的分配规则:我们需要永远分配给_currentSheetNumber直接地。必须有更好的方法。

有。我们需要做的就是使用BehaviorSubject 而不是仅仅使用SubjectBehaviorSubject 就像一个主题,但它“记住”它的当前值,可以通过它的 getValue 方法访问。看起来是这样的:

public currentSheetNumber$ = new BehaviorSubject(0);

get currentSheetNumber(): number {
  return this.currentSheetNumber$.getValue();
}

set currentSheetNumber(value: number) {
  this.currentSheetNumber$.emit(value);
}

应该这样做,但有一个重要的警告可能会导致错误和挫败感。 BehaviorSubject 在订阅时会发出其当前值。在许多情况下,这可能不是您想要的,并且可能会迫使您添加一些杂项(例如跳过订阅中的第一个发射值等)。在这种情况下,您最好使用_currentSheetNumber 策略,尽管它有缺陷。

(或者,您可以直接currentSheetNumber$ 行为主体交互并避免这种情况,编写一个保持其当前状态的自定义主体,但不会将其发送给新订阅者)

【讨论】:

  • 非常感谢您的详细解释。它绝对帮助我解决了我的问题。 :)
  • 干杯,很高兴我能帮上忙!
猜你喜欢
  • 2018-04-11
  • 1970-01-01
  • 2019-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
  • 2018-04-13
相关资源
最近更新 更多