Angular 2 implementes a loading spinner for every HTTP request made

My objective is to implement a spinner functionality whenever an HTTP request occurs in my Angular app. Essentially, I want the user to see a loading screen during these requests within my app component. The setup for my spinner component and spinner service files follows the solution outlined in this question.
The component code snippet from my app.component is as shown below:

@Component({
    selector: 'todoApi',
    template: `
        <div class="foo">
            <spinner-component></spinner-component>
            <h1>Internship Project</h1>
            <a [routerLink]="['Dashboard']">Dashboard</a>
            <a [routerLink]="['Tasks']">List</a>
            <router-outlet></router-outlet>
        <div>
    `,
    directives: [ROUTER_DIRECTIVES,SpinnerComponent],
    providers: [
        ROUTER_PROVIDERS,
    ]
})

@RouteConfig([
    {
        path: '/dashboard',
        name: 'Dashboard',
        component: DashboardComponent,
        useAsDefault: true
    },{
        path: '/tasks',
        name: 'Tasks',
        component: TaskComponent
    },{
        path: '/detail/:id',
        name: 'TaskDetail',
        component: TaskDetailComponent
    },
])

In summary, I aim to display the spinner whenever an HTTP request is triggered within one of these routes. I understand this might be a basic query, but being new to Angular 2, I would greatly appreciate any guidance on this matter. Thanks a lot!
Edit!:
Solution implemented with reference to Günther's answer: I encapsulated my http and spinner-service functionalities into a new HttpClient component, which I then utilized instead of the standard http module. Here is the code snippet for my custom HttpClient component:

import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import { SpinnerService} from './spinner-service';

@Injectable()
export class HttpClient {
  constructor(
      private http: Http,
      public spinner: SpinnerService
    ){

  }

  createAuthorizationHeader(headers:Headers) {
    headers.append('Authorization', 'Basic ' + btoa('username:password')); 
  }

  get(url) {
    this.spinner.start();
    let headers = new Headers();
    this.createAuthorizationHeader(headers);
    return this.http.get(url, { headers: headers }).do(data=> {this.spinner.stop()});
  }

  post(url, data) {
    this.spinner.start();
    let headers = new Headers();
    this.createAuthorizationHeader(headers);
    return this.http.post(url, data, { headers: headers }).do(data=> {this.spinner.stop()});
  }
}

Answer №1

Utilize a common service for handling communication between Http (consider encapsulating Http within your own class) and the <spinner-component>. For more information, refer to https://angular.io/docs/ts/latest/cookbook/component-communication.html

In the shared service, keep track of the number of ongoing (incrementing) and completed/failed HTTP requests. Notify the <spinner-component> whenever the count transitions from 0 to >0 or from >0 to 0 in order to toggle its activation.

Answer №2

Appreciate the insight Günter Zöchbauer! Here's an example I crafted to suit my specific requirements. Rather than utilizing an HTTP wrapper for simplicity, this example demonstrates handling multiple service calls based on your counter recommendation. Hopefully, it proves helpful to others :)

  1. Start by creating the Loader service.

    import { Injectable } from '@angular/core';
    import { BehaviorSubject } from 'rxjs/BehaviorSubject';
    
    @Injectable()
    
    export class LoaderService {
        public loaderCounter: BehaviorSubject<number> = new BehaviorSubject<number>(0);
        displayLoader(value: boolean) {
          let counter = value ? this.loaderCounter.value + 1 : this.loaderCounter.value - 1;
          this.loaderCounter.next(counter);
        }
    }
    
  2. Don't forget to include the service in the providers section of your main module file (for example: AppModule).

  3. In your main component file (e.g., AppComponent), subscribe to the changes and reflect them in the loader (in my case, a separate component).

    //Imports
    import { Subscription } from 'rxjs/Subscription';
    import { LoaderService } from './core/loader.service';
    ..
    @Component({
      selector: 'my-app',
      template: `
        <div class="container-fluid content">
          <router-outlet></router-outlet>
        </div>
        <spinner [visible]="displayLoader"></spinner>
      `
    })
    
    export class AppComponent implements OnInit, OnDestroy {
        displayLoader: boolean;
        loaderSubscription: Subscription;
        constructor(private loaderService: LoaderService) {}
    
        ngOnInit() {
            this.loaderSubscription = this.loaderService.loaderCounter.subscribe((counter: number) => {
                this.displayLoader = counter != 0;
            });
        }
    
        ngOnDestroy() {
            this.loaderSubscription.unsubscribe();
        }
    }
    
  4. Implementing the loader service:

     import { LoaderService } from './core/loader.service';
        ..
        export class SampleComponent implements OnInit {
            constructor(private _service: SomeService, private loader: LoaderService) { }
    
        ngOnInit() {
            this.loader.displayLoader(true);
            this._service.getBalance().subscribe(
                response => ..do something..,
                () => .. error..,
                () => this.loader.displayLoader(false)
            );
        }
    }
    

Answer №3

For anyone who finds their way here going forward...

This particular fix ensures that the spinner won't cease spinning if there's an error with the HTTP request. Be sure to follow these steps:

...
return this.http.post(url, data, { headers: headers })
  .do(data=> {this.spinner.stop()},
  err=> {this.spinner.stop());
...

Answer №4

Another option is to incorporate Pace.js

It would be quite simple to implement

<head>
  <script src="/pace/pace.js"></script>
  <link href="/pace/themes/pace-theme-barber-shop.css" rel="stylesheet" />
</head>

Detailed instructions can be found at:

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

How can you add a Git submodule without specifying a URL?

I am looking to integrate my Angular folder as a submodule into my primary directory. The Angular folder has already been initialized as a git repository. It is currently a local folder on my Windows machine with no URL. After successfully initializing gi ...

The horizontal bar chart on Stacker fails to display accurate data when a name is duplicated

When using a stacker horizontal bar chart to display employee names and bills assigned to them, there is an issue where if two employees have the same name, only the last one will be displayed on the chart. export var multi = [ { "name": "sam", ...

Transferring information to a navigated module using Angular2

I am currently facing a scenario where I have a component being loaded via routing, and my goal is to pass data from the parent component into this child component. How exactly can I achieve this task effectively? Parent Component Class export class Home ...

Is there a way to ensure a consistent return value when using exhaustive switch-case statements?

I'm facing an issue with the following code snippet: function (flavors: IceCreamFlavor): 'like'|'dislike' { switch (flavors) { case IceCreamFlavor.vanilla: return 'dislike'; case IceCreamFl ...

Ways of invoking a component method from a service in Angular 2

I have a concept for creating a unique service that is capable of interacting with one specific component. In my application, all other components should have the ability to call upon this service and then have it interact with the designated component in ...

How to Insert a New User into the Database Using Angularfire2

During the login process, I implemented the following method: login() { this.auth.login(); this.authService.login().subscribe(() => { if (this.authService.isLoggedIn) { console.log("ADDING THE USER.."); // Insert a new user into the use ...

Images in Angular 2 not appearing until system reboot

When working with angular2 and nodejs to upload an image, I encounter an issue where after node uploads the file to the assets folder, an error occurs when attempting to display it in angular: GET http://localhost:4200/assets/img/3.jpg 404 (Not Found) In ...

Angular's AsyncValidatorFn is triggered by the onblur event and does not work with keypress events

I'm currently working with the latest version of Angular and I'm attempting to implement a custom validation for checking a code through a RestAPI. The example below is functional, but it doesn't trigger on keypress events; it only activates ...

Managing the activation and deactivation of a form based on value modifications

I have a formgroup with interconnected formcontrols where each control is enabled only if the previous one is filled. Additionally, if a control is cleared, all subsequent controls should also be cleared and disabled. To illustrate this use case, I create ...

Troubleshooting: Issue with Angular 2 FormArray

Hey there! I'm currently working on my Angular 2 Recipe app where I want to display multiple ingredient details. I am using a FormArray but encountered an error while debugging with the browser developer tools. The error displayed on the Console tab i ...

Angular page startup triggers NPM, leading to a sudden crash

Our ASP.Net + Angular web pages running on the IIS server (built with .Net Core 2.1 and Angular5) have suddenly stopped functioning. An error message "AggregateException: One or more errors occurred. (One or more errors occurred. (The NPM script 'sta ...

Are there any methods to incorporate Facebook and Google login into an Ionic progressive web app (PWA)?

After successfully developing an app in Ionic 3 for Android and iOS, I encountered a problem when adding the browser platform. The Facebook and Google login features were not functioning as expected. Despite the assurance from Ionic documentation that the ...

After compiling the code, a mysterious TypeScript error pops up out of nowhere, despite no errors being

Currently, I am delving into the world of TypeScript and below you can find the code that I have been working on: const addNumbers = (a: number, b: number) => { return a + b } Before compiling the file using the command -> tsc index.ts, the ...

Using dictionary keys as valid property values

I have put together a dictionary like so: const iconTypesDictionary: { [key: string]: IconPrefix } = { solid: 'fas', regular: 'far', light: 'fal', } My goal is to be able to utilize the keys of this dictionary as potent ...

What could be causing jQuery to overlook this button?

Having some trouble with my angular, bootstrap, and jQuery setup. I can't get jQuery to select a button and trigger an alert when clicked: $('#some_button').click(function(e) { alert('testing'); }); <button id="some_but ...

Using JavaScript to place a particular tag at a designated position

I have a string that looks like this: var txtstr='<p>Text 1</p><p>&nbsp;</p><p>Text &nbsp;2</p><p>&nbsp;</p><p>Text 3&nbsp;</p>'; I have an <img src=..../> tag and ...

Tips for utilizing import alongside require in Javascript/Typescript

In my file named index.ts, I have the following code snippet: const start = () => {...} Now, in another file called app.ts, the code is as follows: const dotenv = require('dotenv'); dotenv.config(); const express = require('express' ...

displaying post data in the URL address bar

During the development of my portal using angular 5, everything was running smoothly in the testing environment. However, due to production requirements, I made some changes to access modifiers from private to public. This modification has caused issues in ...

Trouble with embedding video in the background using Next.js and Tailwind CSS

This is the code snippet for app/page.tsx: export default function Home() { return ( <> <main className='min-h-screen'> <video muted loop autoPlay className="fixed -top ...

We are in the process of migrating Angular from version 7 to 16, however, we are facing an issue where one of the libraries is still stuck on version 5, causing a Module not found error related to 'rxjs-com

We recently upgraded an Angular project from version 7 to 16 and we are currently using "rxjs": "~7.8.0". Interestingly, there is no mention of rxjs-compat in the package.json file either. However, during the building process of the application, we encoun ...