1

I'm creating a screen that uses a segment component to filter the results in a list of an observable using the async pipe from an http request. I'm going to create a pipe filter for this to clean it up as it's redundant, but I'm more concerned why it's happening the way is it - I'm new to observables. Everything display and works as expected, but when I toggle the segment filter at the top of the screen it sends a new request to the api each time and rebuilds the list based on the customer type. Is it because the *ngIf is based on the type property of the customer? It just seems a bit ridiculous to send a new request every time it switches. Also, if you could advise a better way to do this with a filter (or whatever method) it would be greatly appreciated.

Method in my service class:

 /**
 * Get a list of the user's customers
 */
list(): Observable<Customer[]> {
    return this.authHttp.get<Customer[]>('/api/customers')
        .map(customers => {
            return customers.json().data
        }).pipe(
            catchError(this.handleError('getCustomers', []))
        );
}

Method in my controller:

ionViewDidLoad() {
    this.customers = this.customerService.list();
}

Segment component with model binding:

<ion-segment [(ngModel)]="type">
  <ion-segment-button value="prospect">
    Prospects
  </ion-segment-button>
  <ion-segment-button value="customer">
    Customers
  </ion-segment-button>
</ion-segment>

Content switching and async pipe:

<div [ngSwitch]="type">
  <ion-list *ngSwitchCase="'prospect'">
    <ng-container *ngFor="let customer of customers | async">
      <button *ngIf="customer.type==='prospect'" ion-item (click)="customerSelected(customer)">
        {{ customer.first_name }} {{ customer.last_name }}
      </button>
    </ng-container>
  </ion-list>
  <ion-list *ngSwitchCase="'customer'">
    <ng-container *ngFor="let customer of customers | async">
      <button ion-item *ngIf="customer.type==='customer'" (click)="customerSelected(customer)">
        {{ customer.first_name }} {{ customer.last_name }}
      </button>
    </ng-container>
  </ion-list>
</div>
Allan Kiezel
  • 115
  • 2
  • 7

1 Answers1

1

Because NgSwitch is removing the dom elements. So when you toggle the switch and it adds your async pipe to the dom it calls .subscribe() on your observable which means it will re-call your API.

From Angular Docs: https://angular.io/api/common/NgSwitch#ngSwitch

NgSwitch

matches the switch expression.Adds / removes DOM sub-trees when the nest match expressions

You can set the results instead of binding directly to the observable.

ionViewDidLoad() {
    this.customerService.list().subscribe(customers => {
        this.customers = customers;
    });
}

Then drop the async pipe from your *ngFor.

<ng-container *ngFor="let customer of customers">

Or don't use ngSwitch, use [hidden] attribute.

  <ion-list [hidden]="type !== 'prospect'">
    <ng-container *ngFor="let customer of customers | async">
      <button *ngIf="customer.type==='prospect'" ion-item (click)="customerSelected(customer)">
        {{ customer.first_name }} {{ customer.last_name }}
      </button>
    </ng-container>
  </ion-list>
  <ion-list [hidden]="type !== 'customer'">
    <ng-container *ngFor="let customer of customers | async">
      <button ion-item *ngIf="customer.type==='customer'" (click)="customerSelected(customer)">
        {{ customer.first_name }} {{ customer.last_name }}
      </button>
    </ng-container>
  </ion-list>

[hidden] has some caveats though...

What is the equivalent of ngShow and ngHide in Angular?

Ryan E.
  • 977
  • 8
  • 16
  • 1
    Appreciate the great explanation! Makes a ton of sense. I guess I was looking too much into the observable side and not the obvious. :) – Allan Kiezel Aug 23 '18 at 18:43
  • Your welcome! :) Honestly there may be some super clever way to do it with observables. There are so many operators, some of which do some really cool things! In this case simpler may be better. You definitely could cache the result in your service and have the service manage calling the API if it needs to (still always returning an observable). That solution comes with it's own host of issues though, like dealing with stale data. – Ryan E. Aug 23 '18 at 18:56