Interested in latest HTTP response? Use RxJS SwitchMap operator
Tags: angular, rxjs, webdev, typescript
Published: 2023-03-25 13:10:03

##Introduction

This post wants to illustrate how to use `switchMap` operator to cancel previous HTTP requests and retrieve the latest Pokemon by an id. It is an important technique to preserve scarce network resource on a device such as mobile phone that has limited data plan. It also improves performance because the application does not have to wait for previous requests to return and display until the last request comes back with the final result.

###Bootstrap AppComponent
```typescript
// main.ts

import 'zone.js/dist/zone';
import { provideHttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { PokemonComponent } from './pokemon/pokemon/pokemon.component';

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [PokemonComponent],
  template: `
    <app-pokemon></app-pokemon>
  `,
})
export class AppComponent {
  name = 'Angular';
}

bootstrapApplication(AppComponent, {
  providers: [provideHttpClient()]
});
```

In main.ts, I bootstrapped `AppComponent` as the root element of the application. It is possible because `AppComponent` is a standalone component and Component decorator defines `standalone: true` option. In the imports array, I imported `PokemonComponent` (that also a standalone component) and it is responsible to display information of a Pokemon. The second argument of `bootstrapApplication` is an array of providers and `provideHttpClient` makes `HttpClient` available for injection in components.


###Retrieve Pokemon by HTTP
```typescript
const retrievePokemonFn = () => {
  const httpClient = inject(HttpClient);
  return (id: number) => httpClient.get<Pokemon>(`https://pokeapi.co/api/v2/pokemon/${id}`)
    .pipe(
      map((pokemon) => ({
        id: pokemon.id,
        name: pokemon.name,
        height: pokemon.height,
        weight: pokemon.weight,
        back_shiny: pokemon.sprites.back_shiny,
        front_shiny: pokemon.sprites.front_shiny,
      }))
    );
}
```
`retrievePokemonFn` is a high order function that returns a function that calls Pokemon endpoint to retrieve the data by id.

###Use switchMap to make latest HTTP Request
```typescript
const btnMinusTwo$ = this.createButtonClickObservable(this.btnMinusTwo, -2);
const btnMinusOne$ = this.createButtonClickObservable(this.btnMinusOne, -1);
const btnAddOne$ = this.createButtonClickObservable(this.btnAddOne, 1);
const btnAddTwo$ = this.createButtonClickObservable(this.btnAddTwo, 2);

createButtonClickObservable(ref: ElementRef<HTMLButtonElement>, value: number) {
    return fromEvent(ref.nativeElement, 'click').pipe(map(() => value));
  }
}
```

`createButtonClickObservable` creates an Observable that emits a number when button click occurs. When button text is +1 or +2, the Observables emit 1 or 2 respectively. When button text is -1 or -2, the Observables emit -1 or -2 respectively.

```typescript
const btnPokemonId$ = merge(btnMinusTwo$, btnMinusOne$, btnAddOne$, btnAddTwo$)
  .pipe(
     scan((acc, value) => { 
          const potentialValue = acc + value;
          if (potentialValue >= 1 && potentialValue <= 100) {
            return potentialValue;
          } else if (potentialValue < 1) {
            return 1;
          }

          return 100;
     }, 1),
     startWith(1),
     shareReplay(1),
  );
```

`btnPokemonId$` is an Observable that emits the Pokemon id

```typescript
this.pokemon$ = btnPokemonId$.pipe(switchMap((id) => this.retrievePokemon(id)));
```

When `btnPokemonId$` emits a Pokemon id, I use `switchMap` to cancel previous HTTP requests and make a new request to retrieve the Pokemon. The result is then assigned to `this.pokemon$` that is an Observable of Pokemon.

###Render Pokemon in inline template
```html
<ng-container *ngIf="pokemon$ | async as pokemon">
    <div class="pokemon-container">
       <label>Id:
          <span>{{ pokemon.id }}</span>
       </label>
       <label>Name:
          <span>{{ pokemon.name }}</span>
       </label>
       <label>Height:
          <span>{{ pokemon.height }}</span>
       </label>
       <label>Weight:
          <span>{{ pokemon.weight }}</span>
       </label>
    </div>
    <div class="container">
       <img [src]="pokemon.front_shiny" />
       <img [src]="pokemon.back_shiny" />
    </div>
 </ng-container>
```

Inline template uses async pipe to resolve pokemon$ Observable and displays the details of the Pokemon.

This is it and I have added HTTP capability to call Pokemon API. Moreover, I leverage `switchMap` to end earlier requests and trigger a new one whenever new pokemon id is emitted.

{% embed
https://stackblitz.com/edit/angular-6qkuog?file=src/pokemon/pokemon/pokemon.component.ts
%}

This is the end of the blog post and I hope you like the content and continue to follow my learning experience in Angular and other technologies.


##Resources:
- Github Repo: https://github.com/railsstudent/ng-pokemon/tree/main/projects/pokemon-demo-3
- Stackblitz: https://stackblitz.com/edit/angular-6qkuog?file=src/pokemon/pokemon/pokemon.component.ts
- Youtube: https://youtu.be/Y-AUQEYj7qg
- PokeAPI: https://pokeapi.co/