Home > Net >  Angular: multiple reciepients of data from endpoint
Angular: multiple reciepients of data from endpoint

Time:10-12

Precondition
I have the following situation:

  1. on the backend I have an endpoint, which delivers data. Data is changed very-very seldom.
  2. on the frontend I have several components, which need the data from p.1 right after the loading.

Question
How to avoid multiple requests to the backend ( = how to share the result of the first request to all the other callings of a service method).

Source code - service

@Injectable()
export class ConfigurationService {

  private ConfigurationUrl: string = 'api/configurations/';
  private httpHeaders: HttpHeaders;
  // ...
  constructor(private httpClient: HttpClient) { 
  }
  // ...
  loadCommonSettings(): Observable<CommonSettings> {
    return this.httpClient.get<CommonSettings>(this.ConfigurationUrl   "common");
  }
  // ...
}

Source code - components

export class HomeComponent {
  constructor(public dialog: MatDialog,  private configurationService: ConfigurationService, private _snackBar: MatSnackBar) { 
  }

  isNewDisabled: boolean = true;
  commonSettings: CommonSettings;

  ngOnInit() {
    this.loadCommonSettings();
  }
  //...

  loadCommonSettings() {
    this.configurationService.loadCommonSettings().subscribe(
      res => {
        this.isNewDisabled = false;
        this.commonSettings = res;
      },
      err => {
        this.openErrorDialog("Error", "Error on data loading");
      }
    );
  }
  //...
}

Problem Each calling of loadCommonSettings service function fires a new request to the service.

Supposes

  1. I thought to store data in the service as in the cache, but anyway I have 2 almost simultaneous calls of a service method.
  2. Maybe Subjects may help in this situation, but I'm completely not sure about it (because it seems, that Observables are also absolutely Ok for such calls).

CodePudding user response:

Caching is a senseful choice here as you already thought. In the following an example for your service. You can also use a BehaviorSubject instead of the ReplaySubject and initialize it with undefined.

@Injectable()
export class ConfigurationService {

  private ConfigurationUrl: string = 'api/configurations/';
  private httpHeaders: HttpHeaders;
  private commonSettings$: Subject<string>;
  
  // ...
  constructor(private httpClient: HttpClient) { 
  }
  // ...
  loadCommonSettings(): Observable<string> {
    if (!this.commonSettings$) {
      this.commonSettings$ = new ReplaySubject<string>(1);
      this.httpClient.get<string>(this.ConfigurationUrl   "common")
        .subscribe(data => this.commonSettings$.next(data));
    }
    return this.commonSettings$;
  }
  // ...

Here only the fist call to the service is starting a http-call. And all calls including the first one are only returning the subject.

CodePudding user response:

Below are the solution without using Subjects

Option 1: Using ShareReplay

loadCommonSettings(): Observable<CommonSettings> {
    return this.httpClient.get<CommonSettings>(this.ConfigurationUrl   "common").pipe(
        take(1),
        shareReplay(1)
    );
 }

Option 2 : Using local-Variable to hold result

// declare variable to hold cache
private cachedSetting: CommonSettings = null;

loadCommonSettings(): Observable<CommonSettings> {
    // if cachedSetting already available then return same
    if(cachedSetting){
        return of(this.cachedSetting);
    }
    
    return this.httpClient.get<CommonSettings>(this.ConfigurationUrl   "common").pipe(
        tap(response => this.cachedSetting = response)
    );
 }

Imports used:

import { shareReplay, take, tap } from 'rxjs/operators';
import { of } from 'rxjs';
  • Related