Несколько подписчиков Angular7 + RxJs на одну и ту же наблюдаемую - PullRequest
0 голосов
/ 26 апреля 2019

Это может быть вопрос noob, но я мог бы найти ссылку на него.

У меня есть два компонента, размещенные в разных местах, каждый компонент использует Сервис для запроса конечной точки и получения некоторых данных (например, пользователяпрофиль).

Служба возвращает наблюдаемое, мне нужно, чтобы наблюдаемое было уникальным или, по крайней мере, чтобы сделать уникальный запрос.

// Service, shared across multiple components
@Injectable()
export class SomeService {
    getData(): Observable<DataModel> {
        return this._http.get<DataResponse>('/some-route').pipe(
          map((response: DataResponse) => this.handleResponse(response)),
          share() // is this right ?
        );
    }

}

// Component requesting data
@Component({ selector: '...', ... })
export class FirstComponent implements OnInit {
  constructor(private _service: SomeService) { }

    ngOnInit() {
        this._service.getData().subscribe(
          data => {
            console.log(data);
          }
        );
      }
}

// Another component requesting the same data
@Component({ selector: '...', ... })
export class SecondComponent implements OnInit {
  constructor(private _service: SomeService) { }

    ngOnInit() {
        this._service.getData().subscribe(
          data => {
            console.log(data);
          }
        );
      }
}

Служба работает и получает данные, нозапрос отправляется дважды, я хочу отправить только один запрос.Компоненты работают одновременно (скажем, один вверху, а второй внизу экрана).Таким образом, они делают запрос одновременно.

Есть ли способ, которым служба отправила только один запрос?

И кстати, первый запрос имеет статус 200, а второй 304.

Спасибо.

ОБНОВЛЕНИЕ

Возможное решение

Пока мне удалось добавить переменную Service

private _observable: Observable<DataModel>;

Тогда при получении данных

getData(): Observable<DataModel> {
    if (!this._observable) {
        this._observable = this._http.get<DataResponse>('/some-route').pipe(
          map((response: DataResponse) => this.handleResponse(response)),
          // HERE IS THE TRICK
          publishLast(),
          refCount()
        )
    }

    return this_observable;
}

Хитрость в использовании publishLast и refCount

Какой-нибудь лучший способ / идея?

Ответы [ 4 ]

1 голос
/ 26 апреля 2019

Посмотрите на мою библиотеку ngx-rxcache.Это упрощает управление состоянием.

https://github.com/adriandavidbrand/ngx-rxcache

@Injectable()
export class SomeService {
 private dataCache = this.cache.get<DataModel>({
    id: 'some uniquie id',
    load: true,
    construct: () => this.http.get<DataResponse>('/some-route').pipe(
      map((response: DataResponse) => this.handleResponse(response))
    )
 });

 data$ = this.dataCache.value$;

 constructor(cache: RxCacheService, http: HttpClient) {}
}

Затем вы можете получить доступ к данным об услуге с помощью

this.service.data$;

https://medium.com/@adrianbrand/angular-state-management-with-rxcache-468a865fc3fb

0 голосов
/ 26 апреля 2019

Я нашел следующую схему эффективной для этого:

@Injectable()
export class SomeService {
  myDataObservable$: Observable<DataModel>;

  constructor( ) {
    this.myDataObservable$ = this.getData().pipe(shareReplay());
  }

  getData(): Observable<DataModel> {
    return this._http.get<DataResponse>('/some-route').pipe(
      map((response: DataResponse) => this.handleResponse(response))
    );
  }
}
  1. Создайте свойство (myDataObservable $) для службы, которая содержит наблюдаемое, которое будет использоваться несколькими компонентами.
  2. Иметь метод (getData ()), который выполняет http-запрос.
  3. В конструкторе вызовите метод и добавьте shareReplay ().
  4. В компонентах, которые вы используете с «this.someService.myDataObservable $ .subscribe (...)»

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

0 голосов
/ 26 апреля 2019

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

 shareReplay(1)

, чтобы ваш код выгляделкак это

getData(): Observable<DataModel> {
    return this._http.get<DataResponse>('/some-route').pipe(
      map((response: DataResponse) => this.handleResponse(response)),
      shareReplay(1) 
    );
}
0 голосов
/ 26 апреля 2019

Вы можете легко сгруппировать ваши запросы в один Observable, который будет обрабатываться по завершении всех ваших запросов, используя forkJoin Operator

вот пример

  public requestDataFromMultipleSources(): Observable<any[]> {
    let response1 = this.http.get(requestUrl1);
    let response2 = this.http.get(requestUrl2);
    let response3 = this.http.get(requestUrl3);
    // Observable.forkJoin (RxJS 5) changes to just forkJoin() in RxJS 6
    return forkJoin([response1, response2, response3]);
  }

В вашем примере вы должны делать запросы в компоненте отца. А затем внедрить результат в дочерние компоненты

...