Angular: Cancelling Observables when switching between Tabs

AngularEnthusiast
Geek Culture
Published in
5 min readJan 3, 2023

--

Consider you have a group of tabs in your application, and you are loading content from an API based on the tab selected.

These are the 2 questions that arise when designing a “Group of Tabs”.

  1. When I click on Tab-1 and an API call starts executing to load data for that particular tab, would it be better to gray out the screen and disable any kind of action on the application until the API call completes ? This would prevent the user from clicking on another tab or clicking anywhere in the application.

If opting for this,it is extremely essential to set a timeout for the API eg: 30 sec so that if the API takes more time to deliver the response, it will be automatically cancelled, the user is notified of the same and is now enabled to action on the application.

2. I click on Tab-1 and an API call starts executing to load data for that particular tab. I dont gray out the screen or disable any action on the application. I give the user full control to click on any other Tab as he wishes.

If the user keeps on switching between the Tabs multiple times, each time an API call is triggered for loading Tab content.

Isn’t the in-progress API call made for loading Tab-1 content of no use when Tab-2 is clicked ?

Definitely its of no use, unless you are planning to store the Tab-1 data in a service and use the cached data the next time the user clicks on Tab-1.

In this story, I am going look into Point 2) where

=>We are not going to cache the data from the API call.

=>If the user switches to Tab-2 when the API call made for Tab-1 is still in progress, then we cancel this in-progress API call and execute only the API required to load Tab-2 data.

Below is how our application looks like. We have 3 tabs for loading dummy Album, ToDos and Comments data of a user.

In order to demonstrate the cancellation of API call when switching Tabs, it is essential that I slow down the API call i.e the API call is in pending state for a slightly longer time.

To check how this has been done, let me begin with the DataService.

createDelay() calls https://api.rapidmock.com/mocks/89mEw passing a custom header to indicate that there will a 1000 ms delay before sending back a dummy response.

getAlbums(), getToDos() and getComments() will call createDelay().

For example below in getAlbums(), we are calling createDelay(). This means that https://api.rapidmock.com/mocks/89mEw will create a delay of 1000 ms before sending back some dummy response. Only after this will the API call https://jsonplaceholder.typicode.com/albums to fetch the dummy Album data will be made.

getAlbums() {
return this.createDelay().pipe(
mergeMap(() => {
return this.http.get(‘https://jsonplaceholder.typicode.com/albums');
})
);
}

This ensures that there is considerable delay before the dummy Album data is retrieved.

Moving to the AppComponent Class.

  1. As already mentioned, we have 3 tabs. We have assigned a number to each tab Albums Tab-1, ToDos Tab-2 and Comments Tab-3.

The property selectedTab holds this number to identify which tab is currently active.

selectedTab: number = 1;

By default, selectedTab holds the number 1, which indicates that the Albums tab is selected when the application loads.

2. Lets now talk about the BehaviorSubject which does most of our job.

private tabSwitchSub = new BehaviorSubject<number>(1);

The subject tabSwitchSub is passed an initial value of 1. Here 1 indicates the tab number.

On application load, since the Tab-1 i.e the Albums Tab is active by default, we require the Album data to be loaded. To achieve this in the ngOnInit() lifecycle hook, we have written the below logic.

ngOnInit() {

this.final$ = this.tabSwitchSub.asObservable().pipe(

switchMap((tabIndex: number) => {
this.selectedTab = tabIndex;
console.log(`Switched to Tab ${tabIndex}`);
return this.getTabDataSource(tabIndex);
})
);
}

We are first accessing the observable corresponding to the tabSwitchSub using asObservable(). Next we are piping the switchMap operator to the observable.

Within the switchMap operator, we are setting the selectedTab property to the tab number passed to the tabSwitchSub subject. On application load, the tab number will be 1. On Tab switch the number can be 1, 2 or 3,

this.selectedTab = tabIndex;

After this we are calling getTabDataSource() to determine which method in the DataService needs to be called to fetch the Tab data. Again based on the Tab number i.e 1,2 or 3 we are making this decision.

getTabDataSource(tabIndex: number) {
return tabIndex === 1
? this.service.getAlbums().pipe(this.handleError().bind(this))
: tabIndex === 2
? this.service.getToDos().pipe(this.handleError().bind(this))
: this.service.getComments().pipe(this.handleError().bind(this));
}

The observable returned by the method in the DataService will be in turn returned by getTabDataSource() to the switchMap operator. The switchMap operator will subscribe to this inner observable to execute the API call.

Please note that the outer observable i.e observable corresponding to tabSwitchSub has been assigned to a property final$. We are subscribing to this outer observable inside the template using async pipe. We shall see how this is done shortly.

3. Finally, lets see how the subject tabSwitchSub works when we switch from 1 tab to another.

For this we need to see how the AppComponent Template looks like. The template contains the 3 Tabs and we have also subscribed to the outer observable final$ using async pipe to display the Tab data.

On clicking on any tab, we are calling changeTab() passing the Tab number as argument.

changeTab(tabIndex: number) {
this.tabSwitchSub.next(tabIndex);
}

In this method ,we are passing the Tab number to the tabSwitchSub subject to again execute the logic inside the ngOnInit() which we have already discussed.

Now lets see how the switchMap operator helps in cancelling any in-progress API calls when a tab switch happens.

Currently Tab-1 is active and the Album data is loaded. I switch to Tab-2 i.e ToDos tab. But before the API call could complete, I switched to Tab-3 i.e Comments tab.

As you can see above the API call to create the delay before fetching the ToDo data has got cancelled.

Also another API call to create the delay before fetching the Comments Data has executed. Once this has completed, the API to fetch the Comments data too has executed.

You can find the entire working example below.

--

--

AngularEnthusiast
Geek Culture

Loves Angular and Node. I wish to target issues that front end developers struggle with the most.