Telerik blogs

Interceptors can help provide a great user experience in your Angular app for HTTP requests. Let’s get started!

We will play with one of the powerful Angular features—interceptors—which help us simplify how we work with HTTP requests and responses.

It helps to put in one single place the control for all API calls; today, we’re going to see when they are helpful and how to create them and maximize their power.

What Are Interceptors?

If you have worked before with a service in Angular, interceptors will sound familiar because they are an Angular service with HttpInterceptor interface implementation.

The interceptors work between our app and the server and interact with the request and response.

The power of the interceptors comes from how they simplify all requests in our app instead of making changes in every place where we make HTTP calls.

Why Use Interceptors?

Interceptors help us ensure to process all HTTP requests and responses before sending or getting the request, giving us the power to manage communication.

We have several places or scenarios to use them:

  • Logging and reporting progress
  • Adding headers to the request
  • Client-side caching

Next, we start to use the interceptors—let’s do it!

What We Are Building

We will show a list of NBA players and teams making two HTTP requests, display the loading, and hide it when the server responds.

One solution shows the loading screen in each method for HTTP calls. What happens if we need to add more requests in the future with the same behavior? For that reason, using an interceptor is the best approach.

The Services

We will create two services in the application, the nba.service.ts and loader.service.ts.

The NbaService has getPlayers and getTeams methods to communicate with the API and return data.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, Observable } from 'rxjs';

@Injectable({ provided: 'root' })
export class NbAService {

  constructor(private httpClient: HttpClient) {

  }

  getPlayers(): Observable<any> {
    return this.httpClient.get('https://www.balldontlie.io/api/v1/players').pipe(
      map((response: any) => {
        return response.data;
      })
    );
  }

  getTeams(): Observable<any> {
    return this.httpClient.get('https://www.balldontlie.io/api/v1/teams').pipe(
      map((response: any) => {
        return response.data;
      })
    );
  }

}

The loader service provides the property isLoading$ observable to get the status of the loader, and the methods show and hide to change his state.

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

@Injectable({
  providedIn: 'root'
})
export class LoaderService {
  isLoading$ = new Subject<boolean>();

  show(): void {
    this.isLoading$.next(true);
  }
  hide(): void {
    this.isLoading$.next(false);
  }
}

Perfect, we have the services for the app. The next step is to create the Interceptor.

Feel free to read more about:

Create Interceptors

Interceptors are very similar to services using the @Injectable() decorator, but instead implement the interface HttpInterceptor. We must implement the intercept method with two parameters: req to take the request and next to move to the next handler.

Create a new class with the @Injectable decorator, implement the HttpInterceptor interface and inject the loader service into the constructor.

In the intercept method, we call loader.show to set the isLoading property to true. The parameter next is an observable httpRequest, so using the finalize operator from rxjs when it completes, call the hide method.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { finalize, Observable } from 'rxjs';
import { LoaderService } from '../services/loader.service';

@Injectable()
export class LoaderInterceptor implements HttpInterceptor {
  constructor(private loader: LoaderService) { }
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.loader.show();
    return next.handle(req).pipe(
      finalize(() => {
        this.loader.hide();
      }));
  }
}

What Does the Code Do?

The HTTP request goes back from the server to the client, the Interceptor sets the loading property to true, and when it completes, it sets to false. Later we use the isLoading property in the component to hide or show the loading screen.

Register The Interceptor

Navigate to your app.module.ts and import the HttpClient module for the HTTP request in the imports area.

Add the Interceptor in the provider’s section and import the HTTP_INTERCEPTORS with the option useClass to assign the LoaderInterceptor.

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { LoaderInterceptor } from './interceptors/loader.interceptor';

@NgModule({
  declarations: [
    AppComponent

  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: LoaderInterceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Show the Loading Screen

To use the power of Interceptor and the services, we need to import it where needed and bring the services from it through the component’s constructor.

We add three properties—players and team to store the data from the API response, and the loading$ property to get the value from loaderService.

Next, we need to call the methods from the services using two buttons in the UI to call the methods loadPlayers and LoadTeams.

In the TypeScript file, the code looks like this:

import { Component } from '@angular/core';
import { LoaderService } from './services/loader.service';
import { NbAService } from './services/NBA.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  players: any[] = [];
  teams: any[] = [];

  loading$ = this.loader.isLoading$;

  constructor(private nbaApi: NbAService, private loader: LoaderService) { }

  loadPLayers() {
    this.players = [];
    this.nbaApi.getPlayers().subscribe((data) => {
      this.players = data;
    });
  }

  loadTeams() {
    this.nbaApi.getTeams().subscribe((data) => {
      this.teams = data;
    });
  }
}

Add the CSS style for the loading screen is is a div with CSS styles to take the full-width colors to look like loading.

.loading {
  position: absolute;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 30px;
  font-weight: bold;
  color: yellow;
  text-align: center;
  z-index: 1;
  background-color: rgba(43, 39, 39, 0.616);
}

In the template app.component.html iterate the player and teams object using the ngFor directive. When the user clicks on Load Player or Load Teams, the loading message appears at the top of the screen.

<div class="items">
  <ul>
    <li *ngFor="let p of players">{{ p.first_name }} {{ p.last_name }}</li>
  </ul>
    <ul>
      <li *ngFor="let t of teams">{{ t.city }} {{ t.conference }}</li>
    </ul>
</div>
<div>
  <button (click)="loadPLayers()" >Load Players</button>
  <button (click)="loadTeams()">Load Teams</button>
</div>
<!-- subscription to loading observable >
  <div class="loading" *ngIf="loading$ | async">
    <h1>Please wait...</h1>
  </div>

Learn more about ngFor and async pipe.

What Does the Code Do?

When the user clicks the buttons Get Players or Get Teams, the Interceptor detects the HTTP request and sets visible to true, and when the server returns, the data switch back to false.

Because the observable loading$ subscribes to the loadingService, it changes every time the Subject emits a new value.

The final result looks like the image:

Final result shows two buttons - load players and load teams. when each is pressed, there is a brief loading message before the list is loaded

Conclusion

We learned how to create an Interceptor and use it to control all HTTP requests in a single place. The perfect place to handle your HTTP request, the Interceptor is available in many scenarios to provide a great experience to the users.

You can find a complete code example for this article and play with the sample app at the following links:

Thanks for your time, and I hope you use Interceptor in the future.


About the Author

Dany Paredes

Dany Paredes is a Google Developer Expert on Angular and Progress Champion. He loves sharing content and writing articles about Angular, TypeScript and testing on his blog and on Twitter (@danywalls).

Related Posts

Comments

Comments are disabled in preview mode.