switchMapWithAsyncState

Frequently, we find ourselves in situations where triggering an API request upon a user's button click is essential. But how do we seamlessly transition between streams? Enter the solution: switchMapWithAsyncState. This operator is tailor-made for such scenarios, effortlessly amalgamating the strengths of both the switchMap and createAsyncState operators. By creating an AsyncState structure to encapsulate your API response, this operator becomes a versatile powerhouse, delivering the combined functionalities of switchMap and createAsyncState in one. The result is a seamless and efficient approach to managing asynchronous operations.

Example 1

Selecting a different gender will populate the user list accordingly.

import {switchMapWithAsyncState} from 'ngx-lift';
// ... other imports

@Component({
  template: `
    <clr-radio-container clrInline>
      <label>Select Gender</label>
      <clr-radio-wrapper>
        <input type="radio" clrRadio name="gender" value="male" [formControl]="genderControl" />
        <label>Male</label>
      </clr-radio-wrapper>
      <clr-radio-wrapper>
        <input type="radio" clrRadio name="gender" value="female" [formControl]="genderControl" />
        <label>Female</label>
      </clr-radio-wrapper>
    </clr-radio-container>

    <div class="mt-6" *ngIf="searchState$ | async as vm">
      <cll-spinner *ngIf="vm.loading" />

      <cll-alert *ngIf="vm.error as error" [error]="error" />

      <div *ngIf="vm.data as users" class="card-grid">
        <app-user-card *ngFor="let user of users" [user]="user" />
      </div>
    </div>
  `
})
export class FilterUsersComponent {
  genderControl = new FormControl('');

  searchState$ = this.genderControl.valueChanges.pipe(
    filter(Boolean),
    switchMapWithAsyncState((gender) => this.userService.getUsers({gender, results: 9})),
  );

  private userService = inject(UserService);
}

Example 2

Submit the form using switchMapWithAsyncState and toSignal. The submission will always emit an error because the API is designed to simulate an error.

import {switchMapWithAsyncState} from 'ngx-lift';
import {toSignal} from '@angular/core/rxjs-interop';

@Component({
  template: `
    <form>
      <clr-input-container>
        <label>Name</label>
        <input clrInput name="username" />
      </clr-input-container>

      <button type="button" class="btn btn-primary" (click)="save()" [clrLoading]="saveAction()?.loading === true">
        Save
      </button>
      @if (saveAction()?.error; as error) {
        <cll-alert [error]="error" />
      }
    </form>
  `
})
export class SubmitFormComponent {
  private http = inject(HttpClient);

  #saveAction = new Subject<void>();
  saveAction = toSignal(
    this.#saveAction.pipe(
      switchMapWithAsyncState(() => {
        return this.http.post(`https://randomuser.me/api`, {payload: 1});
      }),
    ),
  );

  save() {
    this.#saveAction.next();
  }
}