Centralized Loading Indicator
Explore how to build a centralized loading indicator for complex web applications using RxJS. Understand how to manage asynchronous loading states across UI components, reduce code duplication, and handle HTTP request status updates through a centralized service.
We'll cover the following...
Centralized loading indicator scenario
Suppose you are building an enterprise web application with a rich user interface (UI). The UI will consist of several components that consist of independent logic and make requests to the back end. Since each request may take some time, in many cases, it’s best to notify the user that an action, that might take some time, is actually happening in the background. For our scenario, I have created a high-level architecture of a web application where the users can write code, save it, modify it, and export it as a project to be loaded locally. The UI can consist of several components that are each responsible for the given task. When an action is triggered on either component, some “loading” message, based on the type of action, will be shown to the user while the respective request is made to the browser.
Centralized service
A naive solution is to place the loading logic in each UI component. However, that’s not scalable, and we will be repeating the code.
Instead, we will create a centralized service that will handle the loading logic.
This service is called ActionService, and it is the only service that will be injected into the UI components, which will perform the actions. This service will be responsible for delegating the information to the LoadingService for analyzing the action and displaying the respective message on the screen (logging to the console in our case), as well as performing the requests to the backend for each action.
Centralized loading simulation
For the simulation below, I have created an HTTPDummyService that mocks HTTP requests using Subjects.
The LoadingService is responsible for analyzing the action and displaying the respective message on the screen. It will be invoked by ActionService, whenever an action is triggered. Using RxJS, we first need to subscribe to the action stream, and for each action, we have to do two major things:
- Notify the
LoadingServicethat an action is happening, and it should display the respective message. - Perform the HTTP request and handle the response
The following code piped through the Action stream will fulfill the requirements.
tap(action => this.loadingService.handleLoadingAction(action))
switchMap(action => this.httpService.makeRequest())
However, one thing is missing: When the response is received, we need to remove the loading message. Since we are using switchMap to subscribe to the HTTPRequest, after it has produced a value, the LoadingService can be notified that it finished.
Try to run the simulation with different settings. You have noticed that the run component is not used in the simulation above, so try to use that as well while playing around.
You have certainly noticed so far that this approach has several benefits:
- There is no code replication.
- It is easy to maintain large projects with multiple UI components.
- In case you need to add additional logic before the HTTP request, you can apply that down the stream in a single line of code.