Tips for patiently anticipating the completed response within an interceptor

Using the interceptor, I want to display a loading spinner while waiting for subscriptions to complete. This approach works well in individual components by showing and hiding the spinner accordingly:

ngOnInit() {
    this.spinnerService.show();
    this.service.methodName().subscribe(data => {
    },error => {
    },() => {
        this.spinnerService.hide();
    })
}

However, implementing this functionality in the interceptor poses a challenge. The spinner briefly appears only when the request is made, disappearing quickly even for longer requests:

 if (request.url.includes(this.apiUrl)) {
    this.spinnerService.show();
    let headers = new HttpHeaders();
    headers = headers.append('Content-Type', 'application/json');
    headers = headers.append('Authorization', `Bearer ${token}`);
    cloneRequest = request.clone({ headers: headers });
} else {
    cloneRequest = request.clone();
}
return next.handle(cloneRequest).pipe(map(response => {
            this.spinnerService.hide();
            return response;
        })).pipe(catchError(error => {
            let errorMessage = 'An unexpected error occured';
            if (error.error && error.error.message) {
                errorMessage = error.error.message;
            }
            // TODO: Error notifications are currently disabled.
            if (request.url.includes('login')) {
                this.notificationService.showError(errorMessage);
            }
            return throwError(error)
        }));

Answer №1

One issue that arises is due to multiple API requests being made, causing the spinner to hide after the first request even if subsequent requests are still pending. To address this, a counter can be implemented in the spinnerService:

@Injectable({
  providedIn: 'root'
})
export class SpinnerService {
  get shouldDisplay(): boolean {
    return !!this.counter;
  }

  private counter: number = 0; 

  show(): void {
    this.counter++;
  }

  hide(): void {
    this.counter = Math.max(0, this.counter - 1);
  }
}

An adjustment is needed in the interceptor to handle this properly. For API calls that do not require the spinner to be displayed, the counter should not be decreased. Utilizing the finalize operator from rxjs is an ideal solution for this:

const showSpinner = request.url.includes(this.apiUrl);

if (showSpinner) {
  this.spinnerService.show();

  clonedRequest = request.clone({ headers: new HttpHeaders({
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`
  }) });
} else {
  clonedRequest = request.clone();
}

return next.handle(clonedRequest).pipe(
  catchError(error => //...),
  finalize(() => {
    if (showSpinner) {
      this.spinnerService.hide();
    }
  })
);

Creating a wrapper service for Ng4LoadingSpinnerService can be as straightforward as shown below:

@Injectable({
  providedIn: 'root'
})
export class SpinnerService {
  private counter: number = 0; 

  constructor(private loadingSpinnerService: Ng4LoadingSpinnerService) {}

  show(): void {
    if (!this.counter) {
      this.loadingSpinnerService.show();
    }

    this.counter++;
  }

  hide(): void {
    this.counter = Math.max(0, this.counter - 1);

    if (!this.counter) {
      this.loadingSpinnerService.hide();
    }
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error Encountered When Trying to Import Mocha for Typescript Unit Testing

Here's a snippet of code for testing a Typescript project using mocha chai. The test case is currently empty. import {KafkaConsumer} from '../infrastructure/delivery/kafka/kafka-consumer'; import {expect} from 'chai'; import {descr ...

Discovering the generic type from an optional parameter within a constructor

Looking to implement an optional parameter within a constructor, where the type is automatically determined based on the property's type. However, when no argument is provided, TypeScript defaults to the type "unknown" rather than inferring it as "und ...

How do I set up middleware with async/await in NestJS?

I am currently integrating bull-arena into my NestJS application. export class AppModule { configure(consumer: MiddlewareConsumer) { const queues = this.createArenaQueues(); const arena = Arena({ queues }, { disableListen: true }); consumer. ...

I am experiencing issues with my Jest unit test case for Material UI's multi select component failing

I've been working on writing a unit test case for the material UI multi select component. The code for the parent component is as follows: import {myData} from '../constant'; export const Parent = () => { const onChangeStatus= (sel ...

Dynamically generating an Angular component and populating it with data

I am currently working with Angular 7/8 and I have some code that adds a new component dynamically. In the parent component, my .ts file includes the following: PARENT COMPONENT Within the .ts file: @ViewChild(InjectDirective) injectComp: InjectDirect ...

Establishing a connection between MySQL database and an Ionic/Angular application

I have been working on an Ionic/Angular project and I'm facing difficulties in establishing a connection with my MySQL database (mariadb). Despite trying various solutions from online sources, I keep encountering numerous error messages. Any guidance ...

Exploring nested objects and arrays with Plunker - extracting their values

I have retrieved two arrays, each containing nested objects, from API endpoints. One array (preview) consists solely of numbers. For example: [{ obj1:[1, 2], obj2:[3, 4] }] To obtain strings associated with IDs, I made another call to a different en ...

When testing my POST request online, it functions properly. However, I am facing difficulties in getting it to work in next.js as I keep receiving a 405

I am currently working on establishing a connection to Zoho Creator in order to retrieve some data. After successfully testing the request on and receiving the correct response, I encountered an issue while trying to implement it within a next.js applicat ...

Icon appearing outside the button instead of inside

Here's a button I'm working with: <button class="glyphicon glyphicon-option-horizontal" (click)="myFuncion()"></button> Recently, I updated my bootstrap library from version 3 to 4.2.1 and now the icon is no longer visible. I' ...

Mastering the art of Promises and handling errors

I've been tasked with developing a WebApp using Angular, but I'm facing a challenge as the project involves Typescript and asynchronous programming, which are new to me. The prototype already exists, and it includes a handshake process that consi ...

Using the return statement to set a value from a callback function in TypeScript

As I retrieve data from an array of class People, each person has an attendance list represented by Observable<any[]>. // Defining the Person class class Person { id: number; name: string; attendance: Observable<any[]>; // Represents ...

The Angular 8 application is opting for "module" scripts over es5 when loading on IE11

My Angular 8 application is not loading on IE11. After analyzing the index.html, I discovered that it only contains scripts with the attribute type="module" and no es5: <script src="/runtime.js" type="module"></script ...

Nestjs: Step-by-step guide to removing a specific application from a Nestjs monorepo using nest/cli

Is there a method to delete a specific app within a nestjs monorepo using the nest/cli? I have searched through the documentation and developer forums but cannot seem to find a solution. ...

I want to swiftly display the API response using console.log in Angular 8

My current setup involves using a service to log the response from a News API like this: import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) exp ...

How can I access members outside of a class without a name in Typescript?

I recently developed an "anonymous" class inspired by this insightful discussion on Typescript anonymous classes. However, I'm facing a challenge in accessing the outer scope members. Here's a snippet of my code for context: class BaseCounter { ...

Tips for properly narrowing a function parameter that includes "an object key or a function"

Working on a function to retrieve a property using either a string key or a callback, I've encountered an issue with TypeScript narrowing the type parameter. Here is the function in question: function get<T, V>(value: T, fn: (value: T) => V) ...

Attempting to transfer information between components via a shared service

Currently, I am utilizing a service to transfer data between components. The code snippet for my component is as follows: constructor(private psService: ProjectShipmentService, private pdComp: ProjectDetailsComponent) { } ngOnInit() { this.psSe ...

Unable to find the locally stored directory in the device's file system using Nativescript file-system

While working on creating an audio file, everything seems to be running smoothly as the recording indicator shows no errors. However, once the app generates the directory, I am unable to locate it in the local storage. The code I am using is: var audioFo ...

Is Typescript compatible with the AWS Amplify Express API?

I've been struggling to set up my Amplify API in TypeScript and then transpile it to JavaScript. I know it sounds like a simple process, but I could really use some guidance on how to do this effectively. So far, I haven't progressed beyond the ...

What is causing the error message "may require a suitable loader" to appear when I add my TypeScript Node module to my Next.js application?

After developing a TypeScript node module and integrating it into my Next.js app, I encountered an error when attempting to run the app. Are you aware of any reason why this issue may be occurring? Please refer to the information provided below. Details a ...