Loading Indicator using HTTP Interceptors in Angular.

Loading indicators—whether bars, spinners or skeletons—are a great way to occupy the users attention while your page content needs some time to load data.

As an example, this "fake" loading indicator was implemented in update 1.18 in the web interface of my Discord bot shinpuru.

And the best thing of this implementation is that you can easily stack it on top of your current API service (assuming you are using Angular's HTTPClient).

The Loading Bar

The loading bar is actually just a div behind (z-index: 0;) all other elements in the header (z-index: 1;). When the loading bar is set to "loading", the class loading is added which starts the "fake" loading animation.

.part-container {
  display: flex;
  position: relative;
  padding: 10px;
  border-radius: 150px;
  background-color: $c-nqblack;

  > * {
    z-index: 1;
  }

  > .load-indicator {
    position: absolute;
    background-color: $c-blurple;
    top: 0;
    left: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    border-radius: 1000px;
    transition: all 1s ease;

    &.loading {
      opacity: 1;
      @keyframes kf-loading {
        0% { width: 0%; }
        100% { width: 100%; }
      }
      animation: kf-loading 10s cubic-bezier(0.05, 0.82, 0.01, 1);
    }
  }
}

Because you can not estimate the loading time of multiple requests that easily, a "fake" loading bar animation is used. The animation goes for 10 seconds with a cubic-bezier curve with a steep start and a logarithmic end (by the way, here is a great video about i.a. how bezier curves work). That creates an effect of a processing loading bar which will start very fast and slows down near the end. Most loading times are finished quickly, so the fast start makes it possible that the loading bar processes a fair bit before the load is finished. The logarithmic end is handy for long-running processes so that the loading bar still processes but "never" reaches the end before the process finished.

The Loading Bar Service

Of course, we need a service to control the state of our loading bar. When a loading process starts, the init() method is invoked which increases the internal status count by one. When a loading process finished, the finished() method is called which then decreases the status count by one. The service then simply exposes an endpoint isLoading which returns true when status is larger than zero.

@Injectable({
  providedIn: 'root',
})
export class LoadingBarService {
  private status = 0;

  public get isLoading(): boolean {
    return this.status > 0;
  }

  public init() {
    this.status++;
  }

  public finished() {
    this.status--;
  }

  public reset() {
    this.status = 0;
  }
}

Then, the header component logic references the LoadingBarService in the constructor, so the instance is injected via dependency injection.

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss'],
})
export class HeaderComponent implements OnInit {
  // ...

  constructor(
    private api: APIService,
    private router: Router,
    public loadingBar: LoadingBarService
  ) { /* ... */ }
  
  // ...
}

Following, as shown above, the div.loading-indicator binds the class loading to the loadingBar.isLoading state.

<div class="load-indicator" [class.loading]="loadingBar.isLoading"></div>

The HTTP Interceptor

But how to put this now on top of all HTTP requests? To do so, we utilize a very neat implementation of Angular called HTTPInterceptor. As the name says, it allows the interception of any HTTP Request done and executing code before the request goes out and after the response comes in. And this is how it looks like.

@Injectable()
export default class LoadingInterceptor implements HttpInterceptor {
  constructor(private loadingBar: LoadingBarService) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    this.loadingBar.init();
    return next.handle(req).pipe(finalize(() => this.loadingBar.finished()));
  }
}

As next step, the interceptor is registered in the app.module.ts as provider.

@NgModule({
  declarations: [ /* ... */ ],
  imports: [ /* ... */ ],
  providers: [
	// ...
    {
      provide: HTTP_INTERCEPTORS,
      useClass: LoadingInterceptor,
      multi: true,
    },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Now, every time a request is sent, the state count of the LoadingBarService is increased by one. After the request is responded—whether successful or failed—the state count is decreased again by one. That ensures that the loading bar is enabled as long as any request is pending, even during multiple concurrent requests.

Conclusion

I think this is a really simple approach of using a loading indicator for displaying ongoing HTTP requests in your Angular web app.

If you are interested in more articles about web development, feel free to take a look at the web development category. 😉