typescript 40 lines · 8 steps

Tracking HTTP loading state in Angular

A signal-backed counter and an interceptor combine to expose a global isLoading flag driven by in-flight requests.

Explained by highlit
1import { Injectable, signal, computed } from '@angular/core';
2import {
3 HttpInterceptorFn,
4 HttpContextToken,
5 HttpContext,
6} from '@angular/common/http';
7import { inject } from '@angular/core';
8import { finalize } from 'rxjs';
9 
10export const SKIP_LOADING = new HttpContextToken<boolean>(() => false);
11 
12export function skipLoading(): HttpContext {
13 return new HttpContext().set(SKIP_LOADING, true);
14}
15 
16@Injectable({ providedIn: 'root' })
17export class LoadingService {
18 private readonly activeRequests = signal(0);
19 
20 readonly isLoading = computed(() => this.activeRequests() > 0);
21 
22 start(): void {
23 this.activeRequests.update((count) => count + 1);
24 }
25 
26 stop(): void {
27 this.activeRequests.update((count) => Math.max(0, count - 1));
28 }
29}
30 
31export const loadingInterceptor: HttpInterceptorFn = (req, next) => {
32 if (req.context.get(SKIP_LOADING)) {
33 return next(req);
34 }
35 
36 const loading = inject(LoadingService);
37 loading.start();
38 
39 return next(req).pipe(finalize(() => loading.stop()));
40};
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Reference counting handles overlapping requests so the loading flag stays accurate under concurrency.
  2. 2A functional interceptor is the natural place to start and stop shared state around every request.
  3. 3An HttpContextToken lets individual requests opt out of cross-cutting behavior without touching the interceptor's core logic.

Related explainers

typescript
import { useState, useEffect, useRef, useCallback } from "react";
 
interface Suggestion {
  id: string;

A debounced autocomplete hook in React

debounce custom-hooks abortcontroller
Advanced 7 steps
typescript
import { Body, Controller, Ip, Post, UnauthorizedException } from '@nestjs/common';
import { Throttle, ThrottlerGuard } from '@nestjs/throttler';
import { UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';

Rate-limiting an auth flow in NestJS

rate-limiting authentication guards
Intermediate 8 steps
java
@RestController
@RequestMapping("/api/products")
@Validated
public class ProductSearchController {

Validating query params in a Spring controller

validation pagination rest-api
Intermediate 8 steps
typescript
import { Component, computed, DestroyRef, inject, input, output, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { interval, map, takeWhile } from 'rxjs';
 

How a signal-driven countdown works in Angular

signals reactivity rxjs
Intermediate 8 steps
typescript
type TokenType = "keyword" | "string" | "comment" | "number" | "text";
 
interface Token {
  type: TokenType;

How a regex tokenizer highlights code

tokenizer regex lexing
Intermediate 10 steps
python
import secrets
 
from fastapi import Depends, FastAPI, HTTPException, Security, status
from fastapi.security import APIKeyHeader

API key authentication as a FastAPI dependency

authentication dependency-injection api-keys
Intermediate 8 steps

Share this explainer

Here's the card — post it anywhere.

Tracking HTTP loading state in Angular — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code