使用 httpClient 和 shareReplay 时如何触发新的 api 请求

How to trigger a new api request when using httpClient & shareReplay

我正在尝试 sharereplay() 通过使用以下配置在多个组件之间请求我的 httpClient:

apicaller.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
// import rxjs map
import { map, shareReplay } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class ApicallerService {
  peopleindex: number = 1;
  constructor(private _http: HttpClient) {}

  private readonly request = this._http
    .get('https://swapi.dev/api/people/' + this.peopleindex)
    .pipe(shareReplay());

  getData(peopleindex: number = 1) {
    this.peopleindex = peopleindex;
    return this.request;
  }
}

组件 1

应请求 api 获取 ID 2

的详细信息
import { Component, OnInit } from '@angular/core';
import { ApicallerService } from '../apicaller.service';

@Component({
  selector: 'app-component1',
  templateUrl: './component1.component.html',
  styleUrls: ['./component1.component.css'],
})
export class Component1Component implements OnInit {
  apiresponse: any;
  editRes: any;
  constructor(private _apicaller: ApicallerService) {}

  ngOnInit(): void {
    this._apicaller.getData(2).subscribe((data: any[]) => { // <-- Notice 2 in the argument
      console.log(data);
      this.apiresponse = data;
    });
  }
}

组件 2

请求默认 id

import { Component, OnInit } from '@angular/core';
import { ApicallerService } from '../apicaller.service';

@Component({
  selector: 'app-component2',
  templateUrl: './component2.component.html',
  styleUrls: ['./component2.component.css']
})
export class Component2Component implements OnInit {

  apiresponse: any = {};
  constructor(
    private _apicaller: ApicallerService
  ) { }

  ngOnInit(): void {
    // get data from api using service
    this._apicaller.getData().subscribe(
      (data) => {
        this.apiresponse = data;
      }
    );
  }

}

但是,component1

没有发出带有 peopleindex = 2 的新请求

要重现,您可以使用我的 stackblitz 设置:https://stackblitz.com/edit/angular-ivy-gdxyy6?file=src%2Fapp%2Fcomponent1%2Fcomponent1.component.ts

ShareReplay 将是有益的,如果您调用了一些元数据(主数据),它不会在组件之间发生变化(cached/sharing 相同的数据)。

要使用新值执行表达式,您需要为请求而不是字段添加 getter。

private get request() { 
    return this._http.get('https://swapi.dev/api/people/' + this.peopleindex);
}

大多数人使用的调用 httpServices 的模式是这样的。

创建一个单独的服务来调用 rest 端点(围绕 httpClient 的包装器)。

@Injectable({
  providedIn: 'root',
})
export class ApiService {
  constructor(private _http: HttpClient) {}

  get(url: string) {
    return this._http
    .get(url);
  }

  post(url: string, data) {
    return this._http
    .post(url, data);
  }

}

然后添加依赖于 apiService 的要素服务。

@Injectable({
  providedIn: 'root',
})
export class PeopleService {
  constructor(private _api: ApiService) {}

  getData(peopleindex: number = 1) {
    console.log(peopleindex);
    return this._api
    .get('https://swapi.dev/api/people/' + peopleindex);
  }
}

从您的要素组件调用这些要素服务。

export class Component1Component implements OnInit {
  apiresponse: any;
  editRes: any;

  constructor(private _peopleService: PeopleService) {}

  ngOnInit(): void {
    this._peopleService.getData(2).subscribe((data: any[]) => {
      this.apiresponse = data;
    });
  }
}

export class Component2Component implements OnInit {
      apiresponse: any = {};
      constructor(
        private _peopleService: PeopleService
      ) { }

      ngOnInit(): void {
        // get data from api using service
        this._peopleService.getData().subscribe(
          (data) => {
            console.log(data);
            this.apiresponse = data;
          }
        );        
      }
    }

这是相同的 stackblitz link

我建议为这种情况创建 API 商店。

  • 第一次它将存储 API 具有要存储的特定 ID 和 return 初始调用。

  • 第二次需要存储API

export class ApicallerService {
  apiStore = new Map();
  constructor(private _http: HttpClient) {}

  private getApiRequest(id) {
    return this._http.get('https://swapi.dev/api/people/' + id).pipe(shareReplay());
  }

  getData(peopleindex: number = 1) {
    if (!this.apiStore.has(peopleindex)) {
      this.apiStore.set(peopleindex, this.getApiRequest(peopleindex));
    }
    return this.apiStore.get(peopleindex);
  }
}