Наблюдаемый канал не вызывается при обновлении исходного наблюдаемого

#angular #rxjs #angular2-observables #rxjs-observables

#угловой #rxjs #angular2-наблюдаемые #rxjs-наблюдаемые

Вопрос:

У меня есть две наблюдаемые, одна зависит от данных из другой.

Когда данные в первом наблюдаемом изменяются, он должен обновлять второй наблюдаемый.

К сожалению, это работает не так, как я ожидаю.

Вот основной наблюдаемый (который работает нормально)

 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),
    );
  }
}
  

Вот второе наблюдаемое, которое должно обновляться на основе значения первого наблюдаемого (см. Выше):

 // 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, на многих других форумах и спрашивал друзей, которые чаще используют наблюдаемые, но, похоже, я не могу решить эту проблему. Любая помощь будет с благодарностью!

Комментарии:

1. @MichaelD правильно, tap вызывается только в первый раз, а не после изменения locale $. Я попытался удалить shareReply(1) LocaleService, но это не сработало, потому что DataService тогда не получает никаких обновлений. Или, возможно, я вас неправильно понял?

2. вы пытались удалить shareReplay(1) из локального $ observable?

3. @FatehMohamed — MichaelD задал точно такой же вопрос, но затем удалил свой вопрос. Смотрите Мой комментарий над вашим вопросом.

4. Вы подписаны на свой наблюдаемый? Возможно dataService.countries$.subscribe(); , где-то, чего мы здесь не видим? На самом деле, shareReplay(1) он должен действовать как subscribe() , (это должно привести к оценке потока), но на самом деле его не следует использовать таким образом. Хотя просто предположение.

5. @MrkSef нет подписки за пределами того, что вы видите, просто большая структура данных в шаблоне. Я попытался добавить дополнительный .subscribe() к countries$ , но, к сожалению, это не имеет значения.

Ответ №1:

Я не совсем уверен, но вы могли бы попытаться разделить наблюдаемые на две разные части. На данный момент это один поток, как показано ниже

 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)
);
  

Вот мое предложение. Я только что разделил наблюдаемый поток на два разных потока, используя ReplaySubject буфер 1 (такой же, как 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));
  }
}
  

DataService

 // 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$;
  }
}
  

Комментарии:

1. Я полагаю, вы хотели удалить this.locale$ = и this.countries$ = ? Несмотря на это, я попробовал это, на бумаге это звучало хорошо, но, к сожалению, я все равно получаю тот же результат.

2. @JustinN: Вы правы. Я забыл удалить назначения в конструкторах служб. Я обновил ответ. То есть вы имеете switchMap в виду, что запускается в DataService , но tap нет?

3. Я имею в виду, что оба DataService видят первое значение из locale$ , но он никогда не получает обновлений ( tap ни switchMap или ). Однако, если я добавлю LocalService. locale$.subscribe(console.log) — я вижу, что locale$ действительно меняется, он просто никогда не переходит каскадом к методам канала DataServices.

4. Это звучит странно.

5. Очень похоже, и отчасти поэтому я решил попробовать SO за помощью, поскольку я не могу понять это, и никто другой, кого я могу спросить … 😀