【问题标题】:Share data retrieved using HttpClient between Angular components在 Angular 组件之间共享使用 HttpClient 检索到的数据
【发布时间】:2020-06-23 20:19:39
【问题描述】:

我想创建一个服务方法,它在第一次执行时将从 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 版本

  • 角度:v9.1.9
  • rxjs: v6.5.5

谢谢!

【问题讨论】:

  • 基本上没有理由为什么要多次进行HTTP调用...您能否将console.log放在SecondService的构造函数中并验证它只调用一次?
  • 是的,谢谢。我实际上在 HTML 文件中有一个错误,导致控制台错误消息。但如果我能让第一种方法工作,我仍然会很感激,因为我稍后需要将 ReloadFromUrl 和 SaveDataToUrl 功能添加到服务中,第一种方法对我来说似乎不那么模糊,因为它明确包含“数据”属性。
  • 方法 1 中的 data 是方法 2 中的 sharedData.value。我会继续方法 2,因为它包含的逻辑更少。但是,这样做的“反应性”方式不是两者之一。但我怀疑“反应式”方法会更加“晦涩”;-)

标签: angular typescript rxjs observable angular-services


【解决方案1】:

为了完整起见,这里是 响应式 解决您的要求的方式(在订阅者之间共享数据):

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

【讨论】:

    【解决方案2】:

    如果我正确理解您的问题,您需要从 url 获取数据并在组件之间共享。为什么在加载组件之前不获取数据?如果您可以在加载应用程序之前获取数据,那么您可以使用 app-initializer

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

    【讨论】:

    • 谢谢,这似乎是一个有趣的可能性,但我需要能够偶尔从服务器重新加载整个(共享)数据集,所以这种方法对我来说是不够的。
    猜你喜欢
    • 2019-08-04
    • 2023-03-10
    • 2020-03-14
    • 2018-10-12
    • 2018-09-27
    • 2017-11-06
    • 2021-05-03
    • 2019-04-28
    相关资源
    最近更新 更多