Обмен данными, полученными с помощью HttpClient, между Angular компонентами - PullRequest
0 голосов
/ 21 июня 2020

Я хотел бы создать метод службы, который при первом выполнении будет извлекать набор данных из URL-адреса, кэшировать набор данных, а затем совместно использовать один и тот же экземпляр набора данных с компонентами, которые впоследствии вызывают метод службы.

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

Подход №1

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class FirstService {
  private url = 'http://somedummydomain.com/api/entries';
  private data: any;
  private observable: Observable<any>;

  constructor(private http: HttpClient) {}

  getData() : Observable<any> {
    if (this.data) {
      return of(this.data);
    } else if (this.observable) {
      return this.observable;
    } else {
      this.observable = this.http.get(this.url);
      this.observable.subscribe((data) => {
        this.data = data;
        this.observable = null;
      });
      return this.observable;
    }
  }
}

Подход №2

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root',
})
export class SecondService {
  private sharedData: BehaviorSubject<any> = new BehaviorSubject<any>(null);
  private url = 'http://somedummydomain.com/api/entries';

  constructor(private http: HttpClient) {
    this.http.get(this.url).subscribe((data) => {
      this.addData(data);
    });
  }

  private addData(data: any) {
    this.sharedData.next(data);
  }

  getData(): Observable<any> {
    return this.sharedData.asObservable();
  }
}

Версии Lib

  • angular: v9.1.9
  • rx js: v6.5.5

Спасибо!

Ответы [ 2 ]

2 голосов
/ 21 июня 2020

Для полноты, вот стиль реактивный решения вашего требования ( обмен данными между подписчиками ):

@Injectable({
  providedIn: 'root',
})
export class SecondService {
  private url = 'http://somedummydomain.com/api/entries';
  private data$: Observable<any>;

  constructor(private http: HttpClient) {
    this.data$ = this.http.get(this.url).pipe(
      shareReplay(1)
    );
  }

  getData(): Observable<any> {
    return this.data$;
  }
}

Оператор shareReplay сделает две вещи:

  1. Он будет совместно использовать Observable - это означает, что все подписчики получат одинаковые уведомления, а источник подписан только один раз. Подход 1 для вас не работает, потому что каждый подписчик снова будет запускать HTTP GET, пока не завершится первый запрос и вы не попадете в ветку this.data !== undefined.
  2. Он воспроизведет последние N уведомлений (в в этом случае только последнее уведомление) всем подписчикам, которые пришли к стороне слишком поздно, т. е. подписались после того, как HTTP-вызов уже возвратил значение. Если вам это не нужно, используйте вместо него share.

Если вам нужно data как свойство, вы можете установить его с помощью оператора tap следующим образом:

this.data$ = this.http.get(this.url).pipe(
  tap(data => this.data = data),
  shareReplay(1)
);
0 голосов
/ 21 июня 2020

Если я правильно понимаю ваш вопрос, вам нужно получить данные с URL-адреса и поделиться ими между компонентами. Почему бы вам не получить данные перед загрузкой компонента? Если получение данных перед загрузкой приложения вас устраивает, вы можете использовать инициализатор приложения

https://dzone.com/articles/how-to-use-the-app-initializer-token-to-hook-into

...