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

What is the process for setting up a resthook trigger in Zapier?

I'm currently integrating Zapier into my Angular application, but I'm struggling with setting up a REST hook trigger in Zapier and using that URL within my app. I need to be able to call the REST hook URL every time a new customer is created and ...

Using Angular Form Builder to assign a value depending on the selected option in a dropdown menu

My approach to Angular Form Builder initialization includes a group that looks like this: contactReason: this.formBuilder.group({ description: '', source: this.sourceType() }) For the 'description' field, I hav ...

An essential aspect of utilizing ngrx is understanding how to access the previous and current state to effectively compare them when subscribing to the store

Within my component, I am subscribing to the ngrx store for changes in a specific state. I need to implement a condition where if the current state is different from the previous state, then I should perform certain actions. Is there a way to retrieve the ...

Setting the location of the global nprmrc file on a Windows operating system

Is it possible to change the default global npmrc location in Windows operating systems? ...

Developing multi-layered business solutions with Angular front-end that incorporate customized localization in .Net Core Entity MVC is essential for ensuring effective business logic implementation

Help me enhance and refine this code I faced challenges trying to understand and piece together this code (coding isn't my strong suit). Sharing knowledge is important, as encouraged by platforms like StackOverflow. Please help me improve and correct ...

having difficulty sending a post request with Angular

Submitting form data via HTTP post will look like this: saveDataFile(mutlidata,id,value): Observable<Response> { var _url = 'http://xxxx.xxx.xxx'; var saveDataURL = _url + '/' + id; var _this = this; ...

Encountering an error of TypeError while attempting to generate a new GraphQL

Currently using Apollo-Server/TypeScript with graphql-tool's makeExecutableSchema() to set up schema/directives. Encountering an error while attempting to add a basic GraphQL Directive: TypeError: Class constructor SchemaDirectiveVisitor cannot be in ...

Is the Angular 16 button being properly interpreted?

I recently added a primeicon/primeng button in my Angular 16 project using the following code snippet: <button pButton label="Clear" class="p-button-outlined" icon="pi pi-filter-slash" (click)="clear(dt)">&l ...

Determining the best method for change detection in Angular 2: Choosing between Observable, EventEmitter, and Dot Rule

Managing change detection in Angular2 can be approached in three different methods that I have observed. Utilizing Observables @Injectable() export class TodosService { todos$: Observable<Array<Todo>>; private _todosObserver: any; ...

Tips for effectively utilizing TypeORM transactions

I'm encountering an issue with transactions in TypeORM. Here's a snippet of the code causing me trouble: const someFunction = async () => { try { await this.entityManager.transaction(async (manager) => { //some opera ...

Tap into the live monitoring feature of angular-cli build

Is there a way to integrate custom functionality with angular-cli's build/watch command? ng build /w This command creates files and places them in the project's /dist folder I am looking to automatically copy the contents of the dist folder ...

Using Angular to fetch API response and convert it into a promise

I've been following the Angular tutorial available at https://angular.io/tutorial/toh-pt6. The tutorial demonstrates how to retrieve a Json response from an API call and then match it to a promise. One specific example from the tutorial is as follows ...

Implement real-time word counting in Angular as users type

I am looking to monitor word count in real-time as a user enters text into a textarea field If the word limit of 100 is exceeded, further typing should be restricted I aim to display the word count dynamically as the user types wordCounter() This functi ...

Is there a way to conduct testing on code within an Angular subscribe block without risking values being overwritten in the following subscribe blocks?

In my Angular application, I am using ngx-socket-io and have a component with several .subscribe blocks inside ngOnInit() that are triggered when data is received from the server. The issue arises when testing the component, as calling ngOnInit() in the t ...

Clicking on a single checkbox causes the entire input to become deactivated due to the way the system is

I'm encountering a puzzling issue that has me feeling like I know the solution, yet I don't. I set "State" to [checked]. The problem arises when, upon turning it into a map and clicking just one checkbox, the entire selection is clicked. To addre ...

Iterate over a collection of HTML elements to assign a specific class to one element and a different class to the remaining elements

Forgive me if this is a silly question, but I have a function named selectFace(face); The idea is that when an item is clicked, it should add one class to that item and another class to all the other items. This is what I currently have: HTML <div c ...

Issue with the declaration of custom types in Typescript

I've created a type declaration for r-dom as shown below: /// <reference types="react" /> declare module 'r-dom' { interface IRDOMFacade extends React.ReactDOM { (component: React.Component<any, any>, properties?: ...

Trouble with Angular 7: Form field not displaying touched status

I am encountering an issue while trying to input data into a form, as it is not registering the touched status. Consequently, an error always occurs when attempting to send a message back to the user. In my TypeScript file, I am utilizing FormBuilder to c ...

Troubleshooting Angular and Auth0: Understanding the issue with loginWithRedirect() always returning isAuthenticated$ as false

I have previously posted this issue on the Auth0 Community Help Forum, but I am yet to receive a response despite posting it 2 weeks ago. Here is the link to my original post: Currently, my setup includes angular/cli 15.1.1 and auth0/auth0-angular 2.0.1. ...

Using RxJS pubsub to transform an Observable<any> into a string or object

My current pubsub service implementation is as follows: export class PubSubService { subjects: Map<string, Subject<any>> = null; constructor() { this.subjects = new Map<string, Subject<any>>(); } publish(data: { key: ...