Open In App

How to Use Loading in Interceptor? - Angular 17

Last Updated : 07 May, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

In Angular applications, HTTP interceptors are a powerful feature that allows you to intercept and modify HTTP requests and responses. One common use case for interceptors is to display a loading indicator to the user while an HTTP request is in progress. This improves the user experience by providing visual feedback that the application is responding to their actions. In this article, we will explore how to use loading in an interceptor in Angular V17 and provide a brief overview of what interceptors are.

Prerequisites:

What are Interceptors?

Interceptors in Angular are middleware functions that can intercept and modify HTTP requests and responses. They sit between the application's HTTP client (typically the built-in HttpClient module) and the server. Interceptors provide a way to encapsulate cross-cutting concerns related to HTTP communication, such as authentication, logging, caching, error handling, and content transformation.

Interceptors can modify outgoing HTTP requests by adding headers, transforming request bodies, or even cancelling requests altogether. Additionally, they can modify incoming HTTP responses by transforming the response data, handling errors, or adding custom logic based on the response

Steps to implement Loading in Interceptor

Step 1: Create an angular application

We can create an angular 17 application using the angular cli

ng new loader-with-interceptor
cd loader-with-interceptor

This will create a directory named interceptor and change our current directory to the interceptor.

Folder Structure

Screenshot-2024-04-29-194536
Folder Structure

Dependencies

  "dependencies": {
    "@angular/animations": "^17.3.0",
    "@angular/common": "^17.3.0",
    "@angular/compiler": "^17.3.0",
    "@angular/core": "^17.3.0",
    "@angular/forms": "^17.3.0",
    "@angular/platform-browser": "^17.3.0",
    "@angular/platform-browser-dynamic": "^17.3.0",
    "@angular/router": "^17.3.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.14.3"
  }

Step 2: Create a progress bar component

This component will be used to show some animation on the UI, so the user knows that something is happening in the background. It can be a progress bar or even an animated bulb like effect. We will use the progress bar service to conditionally render this animation.

We will use the angular cli to create progress bar component,

ng g c progress-bar

Example

HTML
<!-- progress-bar.component.html -->

@if ((progressBarService.isLoading | async) != 0) {
    <div class="progress-bar">
        <div class="progress-bar-value"></div>
    </div>
}
HTML
<!-- app.component.html -->

<app-progress-bar></app-progress-bar>
<button (click)="makeRequest()">
  Make Request
</button>
CSS
/* progress-bar.component.scss */

.progress-bar {
    height: 4px;
    background-color: rgba(5, 114, 206, 0.2);
    width: 100%;
    overflow: hidden;
}

.progress-bar-value {
    width: 100%;
    height: 100%;
    background-color: rgb(5, 114, 206);
    animation: indeterminateAnimation 1s infinite linear;
    transform-origin: 0% 50%;
}

@keyframes indeterminateAnimation {
    0% {
        transform: translateX(0) scaleX(0);
    }

    40% {
        transform: translateX(0) scaleX(0.4);
    }

    100% {
        transform: translateX(100%) scaleX(0.5);
    }
}
JavaScript
// progress-bar.component.ts

import { Component } from '@angular/core';
import { ProgressBarService } from './progress-bar.service';
import { AsyncPipe } from '@angular/common';

@Component({
    selector: 'app-progress-bar',
    standalone: true,
    imports: [AsyncPipe],
    templateUrl: './progress-bar.component.html',
    styleUrl: './progress-bar.component.scss'
})
export class ProgressBarComponent {
    constructor(
        public progressBarService: ProgressBarService
    ) { }
}
JavaScript
// progress-bar.service.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject, Subject } from 'rxjs';

@Injectable({
    providedIn: 'root',
})
export class ProgressBarService {
    public concurrentReq = 0;
    private _isLoading = new BehaviorSubject<number>(0);

    isLoading = this._isLoading.asObservable();

    show() {
        this._isLoading.next(++this.concurrentReq);
    }

    hide() {
        this._isLoading.next(--this.concurrentReq);
    }
}
JavaScript
// app.component.ts

import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { ProgressBarComponent } from './progress-bar/progress-bar.component';
import { HttpClient } from '@angular/common/http';

@Component({
    selector: 'app-root',
    standalone: true,
    imports: [RouterOutlet, ProgressBarComponent],
    templateUrl: './app.component.html',
    styleUrl: './app.component.scss',
})
export class AppComponent {
    title = 'loader-with-interceptor';

    constructor(private httpClient: HttpClient) { }

    makeRequest() {
        this.httpClient
            .get('https://jsonplaceholder.typicode.com/todos/1')
            .subscribe((res) => {
                console.log(res);
            });
    }
}
JavaScript
// http.interceptor.ts

import {
    HttpRequest,
    HttpInterceptorFn,
    HttpHandlerFn,
} from '@angular/common/http';
import { inject } from '@angular/core';
import { finalize, tap } from 'rxjs/operators';
import { ProgressBarService } from './progress-bar/progress-bar.service';

export const httpInterceptor: HttpInterceptorFn = (
    req: HttpRequest<unknown>,
    next: HttpHandlerFn
) => {
    const progressBarService = inject(ProgressBarService);
    progressBarService.show();

    return next(req).pipe(finalize(() => progressBarService.hide()));
};
JavaScript
// app.config.ts

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { httpInterceptor } from './http.interceptor';

export const appConfig: ApplicationConfig = {
    providers: [
        provideRouter(routes),
        provideHttpClient(withInterceptors([httpInterceptor])),
    ]
};

To start the application run the following command.

ng serve

Output:

On clicking the Make Request button we should be able to see the below output

Animated bulb loader

That's cool right? We can also try a classic glowing bulb like animation. We can create another component AnimatedBulbComponent using the angular cli.

ng g c animated-bulb

Add a simple animated bulb in HTML and CSS. We can make their position absolute so they don't interfere with other elements. Also use the same logic as ProgressBarComponent to render them conditionally.

Example:

HTML
<!-- animated-bulb.component.html -->

@if ((progressBarService.isLoading | async) != 0) {
    <div class="bulb"></div>
}
HTML
<!-- app.component.html -->

<app-animated-bulb></app-animated-bulb>
<button (click)="makeRequest()">
  Make Request
</button>
CSS
/* animated-bulb.component.scss */

.bulb {
    width: 20px;
    height: 20px;
    background-color: yellow;
    border-radius: 20px;

    position: absolute;
    bottom: 4px;
    right: 4px;
    animation: blink 1s infinite ease-out;
}

@keyframes blink {
    0% {
        opacity: 1;
    }

    50% {
        opacity: 0.3;
    }

    100% {
        opacity: 1;
    }
}
JavaScript
// animated-bulb.component.ts

import { Component } from '@angular/core';
import { ProgressBarService } from '../progress-bar/progress-bar.service';
import { AsyncPipe } from '@angular/common';

@Component({
    selector: 'app-animated-bulb',
    standalone: true,
    imports: [AsyncPipe],
    templateUrl: './animated-bulb.component.html',
    styleUrl: './animated-bulb.component.scss'
})
export class AnimatedBulbComponent {
    constructor(
        public progressBarService: ProgressBarService
    ) { }
}
JavaScript
// app.component.ts

import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { ProgressBarComponent } from './progress-bar/progress-bar.component';
import { HttpClient } from '@angular/common/http';
import { AnimatedBulbComponent } from './animated-bulb/animated-bulb.component';

@Component({
    selector: 'app-root',
    standalone: true,
    imports: [RouterOutlet, ProgressBarComponent, AnimatedBulbComponent],
    templateUrl: './app.component.html',
    styleUrl: './app.component.scss',
})
export class AppComponent {
    title = 'loader-with-interceptor';

    constructor(private httpClient: HttpClient) { }

    makeRequest() {
        this.httpClient
            .get('https://jsonplaceholder.typicode.com/todos/1')
            .subscribe((res) => {
                console.log(res);
            });
    }
}

Output


Next Article

Similar Reads