Angular Loading Interceptor Challenge
Your task is to implement a loading interceptor in Angular. This interceptor will globally manage a loading indicator, showing it whenever an HTTP request is sent and hiding it when all requests are completed. This is a common and crucial feature for improving user experience in web applications, providing visual feedback during data fetching.
Problem Description
You need to create an Angular HTTP interceptor that toggles a loading state. This state should be managed by a service and exposed to the application.
Key Requirements:
-
Create a
LoadingService:- This service should maintain a boolean property (e.g.,
isLoading) to indicate if any HTTP requests are currently in progress. - It should provide methods to increment and decrement a counter for active requests.
- The
isLoadingproperty should betrueif the counter is greater than 0, andfalseotherwise. - Consider making
isLoadingan Observable so components can subscribe to changes.
- This service should maintain a boolean property (e.g.,
-
Create an
LoadingInterceptor:- This interceptor must implement the
HttpInterceptorinterface from@angular/common/http. - In the
interceptmethod:- When a request is initiated, increment the active request counter in the
LoadingService. - Regardless of success or failure, when a request completes (either
successorerror), decrement the active request counter in theLoadingService. - The interceptor should return the
requestitself, possibly modified, or anext.handle(request)result.
- When a request is initiated, increment the active request counter in the
- This interceptor must implement the
-
Register the Interceptor:
- Ensure the
LoadingInterceptoris correctly registered in your Angular module's providers, typically inapp.module.ts. It should be provided withHTTP_INTERCEPTORSand set to multi-use.
- Ensure the
-
Component Integration (Conceptual):
- Although you won't be building the full UI, understand how a component would use the
LoadingService. A component would injectLoadingServiceand subscribe to itsisLoadingobservable to conditionally display a loading spinner or message.
- Although you won't be building the full UI, understand how a component would use the
Expected Behavior:
- When an HTTP request is made by any Angular service, the loading indicator should become visible.
- When all active HTTP requests have finished (either successfully or with an error), the loading indicator should become hidden.
- If multiple requests are made concurrently, the loading indicator should remain visible until the last request completes.
Edge Cases to Consider:
- Multiple Concurrent Requests: The interceptor must correctly handle scenarios where many requests are active simultaneously.
- Request Failures: The loading indicator must be hidden even if a request results in an error.
- Interceptors in Sequence: If other interceptors are present, ensure your loading interceptor is placed correctly in the chain to accurately track requests.
Examples
This is a conceptual example as the "input" and "output" are more about observable state changes and component behavior rather than concrete data transformations.
Example 1: Single Request
Imagine a component calls a service that makes a single GET request to fetch user data.
- Before Request:
LoadingService.isLoadingisfalse. - Request Initiated:
LoadingInterceptorintercepts the request. It callsLoadingService.incrementRequestCount().LoadingService.isLoadingbecomestrue. A loading spinner is displayed in the UI. - Request Completes (Success):
LoadingInterceptorintercepts the response. It callsLoadingService.decrementRequestCount().LoadingService.isLoadingbecomesfalse(as the count is now 0). The loading spinner is hidden. - Request Completes (Error):
LoadingInterceptorintercepts the error. It callsLoadingService.decrementRequestCount().LoadingService.isLoadingbecomesfalse(as the count is now 0). The loading spinner is hidden.
Example 2: Multiple Concurrent Requests
Imagine a dashboard component that makes three GET requests simultaneously to fetch different data sets.
- Before Requests:
LoadingService.isLoadingisfalse. - Request 1 Initiated:
LoadingInterceptorincrements count.LoadingService.isLoadingistrue. - Request 2 Initiated:
LoadingInterceptorincrements count.LoadingService.isLoadingistrue. (Count is now 2). - Request 3 Initiated:
LoadingInterceptorincrements count.LoadingService.isLoadingistrue. (Count is now 3). - Request 1 Completes (Success):
LoadingInterceptordecrements count. (Count is now 2).LoadingService.isLoadingremainstrue. - Request 2 Completes (Error):
LoadingInterceptordecrements count. (Count is now 1).LoadingService.isLoadingremainstrue. - Request 3 Completes (Success):
LoadingInterceptordecrements count. (Count is now 0).LoadingService.isLoadingbecomesfalse. Loading spinner is hidden.
Constraints
- The
LoadingServicemust be provided as a singleton. - The
LoadingInterceptormust be registered as an HTTP interceptor for all outgoing requests. - The solution must be written in TypeScript.
- The interceptor should not block the request flow; it should simply observe and act.
Notes
- Think about using RxJS operators like
tapfor side effects within the interceptor's observable stream. - Consider using
SubjectorBehaviorSubjectin yourLoadingServiceto manage theisLoadingstate and allow for subscriptions. - The
next.handle(request)call is crucial within theinterceptmethod to ensure the request continues down the interceptor chain. - You will need to inject
LoadingServiceinto yourLoadingInterceptor.