Search code examples
angularngrxangular-ngrx-data

Subscribe geting called twice for a single ngrx-data service call


I'm very new to ngrx-data and I'm try to detect why loading a few records is taking so long

Here is how I subscribe to the ngrx-data entities:

    this.subscriptions.push(this.mileageTrackingManagmentService.entities$.subscribe(data => {
      console.log('>>subscribe mileageTrackingManagmentService - ' + new Date());
      let tempDataset = data || [];
      this.calculateTotalMileageForMultipleDays(tempDataset);
      this.mileageTrackingManagment = tempDataset;
      this.filteredMileageTrackingManagement = this.mileageTrackingManagment;
      //this.setFilter();
      //this.cdRef.detectChanges();
    }));

Here is how I call the ngrx-data service to make the http request

  private loadMileageTracking() {
    if (this.domain) {
      console.log('loadMileageTracking');
      if (this.dateOption == DateOptions.Day && this.selectedResource) {
        this.mileageTrackingService.getTrackingByResourceIdAndDomainId(this.startDate, this.selectedResource.resourceId, this.domain.id);
      } else {
        console.log('getManagment');
        this.mileageTrackingManagmentService.getManagment(this.startDate, this.endDate, this.dateOption, this.domain.id)
      }
    }
  }

From what I can see in the console, subscribe reaction logic is gettig called twice with 15 seconds difference. In the network tab I can see that both request are sucessful and contain data.

I'm doing wrong the detection change to display the data, is this standard behavior ? enter image description here

Things that I have tried

  • entities$.pipe(skip(1)).subscribe...
  • entities$.pipe(take(1)).subscribe...

None of the work as expected since only the a given request is shown and any other is ignored

I'm not looking at a way to ignore returned data by the request, what I want is to know why ngrx-data or my code is doing two http request for a single call the a ngrx-data service

Here is how the service is implemeneted

export class MileageTrackingManagmentService extends EntityCollectionServiceBase<MileageTrackingManagment> {

    constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
        super('MileageTrackingManagment', serviceElementsFactory);
    }

    public getManagment(startDate: Moment, endDate: Moment, grouping: DateOptions, domainId?: string): Observable<MileageTrackingManagment[]> {
        this.clearCache();

        return this.getWithQuery(MileageCommon.buildQuery(startDate, endDate, grouping, domainId));
    }
}

I have also checked other similar questions without much luck


Solution

  • I believe this is what is happening: entities$.subscribe( yields once with entities$ current value, then once more every time entities$'s value changes.

    When the subscription takes place (at this line)

     ... (this.mileageTrackingManagmentService.entities$.subscribe(data => ...
    

    The current entities$ value is yield;

    Then the call to the service function loadMileageTracking happens.

    Then some time passes (15s per your pic) and the FE received the data;

    This causes the data to be changed on the store related to your entities, and as a consequence ...entities$.subscribe(data => ... yields again.

    if you need the subscription to not yield while Back-End is processing the service's http request, I suggest you add a property to you store to indicate if the values are loading or not.

    let me call it loading, and assume you have an observable loading$ for it in you service for the sake of the argument. Then you can do this:

    this.mileageTrackingManagmentService.loading$.pipe(filter(loading => !loading),take(1)).subscribe(() => {
    {
      this.subscriptions.push(this.mileageTrackingManagmentService.entities$.subscribe(data => {
        let tempDataset = data || [];
        this.calculateTotalMileageForMultipleDays(tempDataset);
        this.mileageTrackingManagment = tempDataset;
        this.filteredMileageTrackingManagement = this.mileageTrackingManagment;
      }));
    });
    
    

    In this example you would also need to add the handling of the loading property value to your reducer. The action sets its value to true and both the success and failure related actions set it to false.