【问题标题】:Observable pipe not called when source Observable is updated更新源 Observable 时未调用 Observable 管道
【发布时间】:2021-01-26 21:41:20
【问题描述】:

我有两个 Observable,一个依赖于另一个的数据。

当第一个 Observable 上的数据发生变化时,它应该更新第二个 Observable。

很遗憾,这并没有像我预期的那样工作。

这是主要的 Observable(工作正常)

export class LocaleService {
  locale$: Observable<string>;

  constructor(private router: Router) {
    this.locale$ = router.events.pipe(
      filter(evnt => envt instanceof ActivationEnd),
      map((evnt: ActivationEnd) => evnt.snapshot.paramMap.get('locale')),
      shareReplay(1),
    );
  }
}

这是第二个 Observable,它应该根据第一个 Observable 的值更新(见上):

// This type is more complex, but made it simple for demonstrating the problem
interface ICountries { code: string; }

export class DataService {
  countries$: Observable<ICountries>;

  constructor(private http: HttpClient, private localService: LocaleService) {
    this.localeService.locale$.subscribe(locale => {
      // this is only here for debug purposes, showing it does change
      console.log('changed locale', locale);
    });

    this.countries$ = localeService.locale$.pipe(
      tap(locale => { console.log('http updated', locale); }), // called first time only, not on locale changes...
      switchMap(locale => this.http.get<ICountries>(`http://localhost:8080/countries?lang=${locale}`)),
      shareReplay(1),
    );
  }
}

如果重要的话,我使用countries$ 的方式如下(再次简化以说明问题):

@Component({
  template: `<ng-container *ngIf="countries$ | async as countries">{{ countries.code }}</ng-container>`,
})
export class CountryComponent implements OnInit {
  countries$: Observable<ICountries>;

  constructor(private dataService: DataService) {
    this.countries$ = dataService.countries$;
  }
}

我已经为此苦苦挣扎了几天,在 StackOverflow、许多其他论坛上寻找答案,并询问了更频繁地使用 Observables 的朋友,但我似乎无法解决这个问题。任何帮助将不胜感激!

【问题讨论】:

  • @MichaelD 正确,只有第一次调用 tap 而不是 locale$ 更改后。我尝试在 LocaleService 上删除 shareReply(1) 但这不起作用,因为 DataService 然后没有得到任何更新。还是我误会了你?
  • 你试过从 local$ observable 中删除 shareReplay(1) 吗?
  • @FatehMohamed - MichaelD 提出了完全相同的问题,但随后删除了他的问题。请参阅我在您问题上方的评论。
  • 你订阅你的 observable 吗?也许dataService.countries$.subscribe(); 某个我们在这里看不到的地方?真的,shareReplay(1) 应该有点像 subscribe(),(它应该导致流评估)但它真的不应该那样使用。不过只是猜测。
  • @MrkSef 在您所看到的之外没有订阅,只是模板中的一个大型数据结构。我尝试在countries$ 中添加一个额外的.subscribe(),但不幸的是它没有任何区别。

标签: angular rxjs angular2-observables rxjs-observables


【解决方案1】:

我不太确定,但您可以尝试将 observables 拆分为两个不同的部分。目前它是像下面这样的单流

this.countries = localeService.locale$.pipe(
  tap(locale => { console.log('http updated', locale); }), // called first time only, not on locale changes...
  switchMap(locale => this.http.get<ICountries>(`http://localhost:8080/countries?lang=${locale}`)),
  shareReplay(1),
  tap(locale => { console.log('http updated', locale); }), // called first time only, not on locale changes...
  switchMap(locale => this.http.get<ICountries>(`http://localhost:8080/countries?lang=${locale}`)),
  shareReplay(1)
);

这是我的建议。我刚刚使用带有缓冲区 1 的ReplaySubject 将可观察流拆分为两个不同的流(与问题中的shareReplay 相同)

LocaleService

export class LocaleService {
  localeSource = new ReplaySubject<string>(1);
  locale$ = this.localeSource.asObservable();

  constructor(private router: Router) {
    router.events.pipe(
      filter(evnt => envt instanceof ActivationEnd),
      map((evnt: ActivationEnd) => evnt.snapshot.paramMap.get('locale'))
    ).subscribe(event => this.localeSource.next(event));
  }
}

数据服务

// This type is more complex, but made it simple for demonstrating the problem
interface ICountries { code: string; }

export class DataService {
  countriesSource = new ReplaySubject<ICountries>(1);
  countries$ = this.countriesSource.asObservable();

  constructor(private http: HttpClient, private localeService: LocaleService) {
    this.localeService.locale$.pipe(
      tap(locale => { console.log('http updated', locale); }),
      switchMap(locale => this.http.get<ICountries>(`http://localhost:8080/countries?lang=${locale}`))
    ).subscribe(countries => this.countriesSource.next(countries));
  }
}

CountryComponent

@Component({
  template: `<ng-container *ngIf="(countries$ | async) as countries">{{ countries?.code }}</ng-container>`,
})
export class CountryComponent implements OnInit {
  countries$: Observable<ICountries>;

  constructor(private dataService: DataService) {
    this.countries$ = this.dataService.countries$;
  }
}

【讨论】:

  • 我猜你的意思是删除this.locale$ = this.countries$ = ?不管怎样,我试过了,在纸上听起来不错,但不幸的是我仍然得到完全相同的结果。
  • @JustinN:你是对的。我忘了删除服务构造函数中的分配。我已经更新了答案。所以你的意思是switchMap 是在DataService 中触发的,但tap 不是?
  • 我的意思是两个 DataService 都从 locale$ 看到第一个值,但它永远不会收到更新(tapswitchMap 都没有)。但是,如果我添加 localService.locale$.subscribe(console.log) - 我看到 locale$ 实际上发生了变化,它只是永远不会级联到 DataServices 的管道方法。
  • 听起来很奇怪。
  • 非常如此,这也是为什么我决定尝试 SO 寻求帮助的原因,因为我无法弄清楚,我也无法问其他任何人......?
猜你喜欢
  • 1970-01-01
  • 2013-11-16
  • 2013-05-16
  • 2019-07-27
  • 2018-01-23
  • 1970-01-01
  • 2021-12-25
  • 1970-01-01
  • 2020-04-02
相关资源
最近更新 更多