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. 😉