@anexia/registry-loading-interceptor

This library provides an interceptor which maps Http Requests to a Loading State. You're able to subscribe per request to the loading state.

Usage no npm install needed!

<script type="module">
  import anexiaRegistryLoadingInterceptor from 'https://cdn.skypack.dev/@anexia/registry-loading-interceptor';
</script>

README

Registry Loading Interceptor

This library provides an interceptor which maps Http Requests to a Loading State. You're able to subscribe per request to the loading state.

Usage Scenarios

  • implicit "isLoadingAny" used for a global spinner indicator to show that anything is going on in the network
  • explicit "isLoading" may be used to show exact loading indicator to a certain context

Usage

  1. Import the RegisterLoadingInterceptorModule in your root module (hint: most of the time: app.module.ts)
  2. Create your own SelectorFacade to the loading state

Definitions

RequestIdStrategy

Provides a mechanism to generate an ID from a request. Default ships with a URL to ID example. Trough this Request ID you're able to create a selector to observe the loading state of a single request.

RequestFilterStrategy

You may not want to map every request to a loading state, this is the place where you can provide a mechanism to filter/exclude request.

  • If using Angular 12, you can provide your own HttpContext based filter.

SelectorFacade

Create a service which injects RegistryLoadingInterceptorand filters RequestID based on the loadingState$.

Usage Example

App Module Configuration

Root Module (app.module.ts) registration and configuration

@NgModule({
  declarations: [AppComponent],
  imports: [
    // ... other imports
    RegistryLoadingInterceptorModule
  ],
  
})
export class AppModule { }

Selector Facade

@Injectable({providedIn: 'root'})
export class IsLoadingService {

  public constructor(
    private readonly loadingRegistry: RegistryLoadingInterceptor,
    @Inject(REQUEST_ID_GENERATOR) private readonly idGenerator: UrlFragmentIdGenerator
  ) {

  }

  public isLoading$(url: string): Observable<boolean> {
    const requestId = this.idGenerator.getIdentifier(url);
    return this.loadingRegistry.loadingState$.pipe(
      map(state => state.has(requestId) ? state.get(requestId) as boolean : false),
      distinctUntilChanged()
    );
  }

  public isAnyLoading$(): Observable<boolean> {
    return this.loadingRegistry.isAnyRequestLoading$;
  }
}

DataAccess Level: Test DataAccess Example


@Injectable({
  providedIn: 'root'
})
export class TestDataAccessService {

  private static getUsersEndpoint = '/users';

  public userListLoading$: Observable<boolean>;

  public constructor(
    private readonly httpClient: HttpClient,
    private readonly isLoadingService: IsLoadingService
    ) {
    // register request loading listener
    this.userListLoading$ = isLoadingService.isLoading$(TestDataAccessService.getUsersEndpoint);
  }

  public getUsers$(): Observable<unknown> {
    return this.httpClient.get(TestDataAccessService.getUsersEndpoint);
  }
}

Component Level

import {Component} from '@angular/core';
import {TestDataAccessService} from "./test-data-access.service";
import {IsLoadingService} from "./is-loading.service";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {

  public constructor(
    private readonly testDataAccess: TestDataAccessService,
    private readonly isLoadingService: IsLoadingService
  ) {
    // connect the facade with the view
    testDataAccess.userListLoading$.subscribe(res => console.log('explicit', res));
    // implicit loading state, will provide Loadign state for any http request
    this.isLoadingService.isAnyLoading$().subscribe(res => console.log('implicit', res));

    this.testDataAccess.getUsers$().subscribe(console.log);

  }

}

Data Sequence Diagram

Customize

To use custom Request ID Strategy or custom Request Filtering you just need to provide REQUEST_ID_GENERATOR or REQUEST_ID_GENERATOR.

 {provide: REQUEST_ID_GENERATOR, useClass: CustomIdGenerator},