RxJs: Как условно связать наблюдаемый объект BehaviorSubject?

У меня есть наблюдаемая служба данных (UserService), которая возвращает текущего пользователя, вошедшего в систему. Я следовал этому руководству - https://coryrylan.com/blog/angular-observable-data-services, в котором описывается использование BehaviorSubject для немедленного возврата текущего пользователя по умолчанию, а затем выдачи реального currentUser после его загрузки или изменения. Сервис в основном такой ...

private _currentUser: BehaviorSubject<User> = new BehaviorSubject(new User());
public currentUser: Observable<User> = this._currentUser.asObservable();

constructor(private http: Http) {}

loadUser() { // app.component onInit and login component call this
  return this.http.get('someapi.com/getcurrentuser')
  .map(response => <User>this.extractData(response))
  .do(
    (user) => {
      this.dataStore.currentUser = user;
      this._currentUser.next(Object.assign(new User(), this.dataStore).currentUser);
    },
    (error) => this.handleError(error)
  )
  .catch(error -> this.handleError(error));
}

У меня возникают проблемы, когда пользователь нажимает F5, чтобы перезагрузить весь спа. Когда компонент-потребитель подписывается на currentUser в UserService, он немедленно получает пользователя по умолчанию, в то время как UserService ожидает вызова api для получения фактического пользователя. В момент завершения вызова api UserService отправляет реального пользователя, и все подписчики получают настоящего пользователя. Однако первое значение, генерируемое BehaviorSubject, является значением по умолчанию и всегда имеет идентификатор «undefined», поэтому мы пока не можем сделать следующий вызов api. Фактически, когда приходит реальный пользователь, и я МОГУ сделать действительный вызов, используя user.id, цепная подписка никогда не происходит, и я не получаю значения из ответа.

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

this.userService.currentUser
  .flatMap((user) => {
    this.user = user;
    // Need to NOT call this if the user does not have an id!!!
    this.someOtherService.getSomethingElse(user.id); // user.id is always undefined the first time
  })
  .subscribe((somethingElse) => {
    // This never gets called, even after the real user is emitted by the UserService 
    // and I see the getSomethingElse() call above get executed with a valid user.id
    this.somethingElse = somethingElse;
  });

person Tim Hardy    schedule 25.03.2017    source источник


Ответы (1)


Если вы хотите игнорировать пользовательские экземпляры, у которых нет id, используйте _ 2_ оператор:

import 'rxjs/add/operator/filter';

this.userService.currentUser
  .filter((user) => Boolean(user.id))
  .flatMap((user) => {
    this.user = user;
    this.someOtherService.getSomethingElse(user.id);
  })
  .subscribe((somethingElse) => {
    this.somethingElse = somethingElse;
  });

Что касается «почему подписка никогда не срабатывает», это, вероятно, связано с ошибкой, связанной с неопределенным id. Вы передаете subscribe только функцию next, поэтому любые ошибки не будут обрабатываться. И если произойдет ошибка, наблюдаемый прекратит работу и отменит подписку на всех подписчиков - как именно так ведут себя наблюдаемые - поэтому любые последующие пользователи с определенными свойствами id не будут приняты.

person cartant    schedule 25.03.2017
comment
Как раз то, что мне было нужно. Спасибо. - person Tim Hardy; 28.03.2017