[Fixed] Overriding previous observable

Issue

I have an events app which has an all-events component which lists all the created events. When the all-events component is loaded, the events are retrieved from my database.

This is the relevant code from the all-events Typescript file:

  eventParams: EventParams;
  getAllEventsObs: Observable<PaginatedResult<BeachCleanEvent[]>>;

  ngOnInit() {
    this.eventParams = new EventParams();
    
  }

  getEvents() {
   this.getAllEventsObs = this.eventService.getAllEvents(this.eventParams);
  console.log("getting events");
  }

  pageChanged(event: any) {
    this.eventParams.pageNumber = event.page;
    this.eventService.setEventParams(this.eventParams);
    this.getEvents();
    
  }

Here is the html from the all-events component:

<div class="background-img">
</div>
<div class="container">
    <div class="events-container" *ngIf="getAllEventsObs | async as getAllEventsObs">
      <div class="event-item" *ngFor="let event of getAllEventsObs.result">
        <app-event-card [existingEvent]="event"></app-event-card>
      </div>
      <pagination *ngIf="getAllEventsObs.pagination as eventPagination"
        [boundaryLinks]="true"
        [totalItems]="eventPagination.totalItems"
        [itemsPerPage]="eventPagination.itemsPerPage"
        [(ngModel)]="eventPagination.currentPage"
        (pageChanged)="pageChanged($event)">
      </pagination>
    </div>
</div>

The getAllEvents method is just a simple http request. I won’t show this code as I know it works correctly.

This is the PaginatedResult class:

export interface Pagination {
    currentPage: number;
    itemsPerPage: number;
    totalItems: number;
    totalPages: number;
}


export class PaginatedResult<T> {  
    result: T;  
    pagination: Pagination; 
}

This is the EventParams class:

export class EventParams {
    pageNumber = 1;
    pageSize = 4;
}

When the page initially loads, it loads correctly and displays the first four events:

enter image description here

The issue I am having is that when clicking on the next page, it gets stuck and won’t load the next four events. No error is displayed on the console but the "getting events" console.log I created in the getEvents method above just keeps firing:

enter image description here

I suspect this is something to do with the way I am consuming the observable in the html code (using the async pipe). How would I go about resolving this? Is this where I should be using the switchMap RXJS operator? If so, how would I use it in this scenario?

Solution

You are on the right track… and yes, you should use switchMap 🙂

Instead of re-assigning your source observable inside getEvents(), you could simply define it to depend on your params, and just push new params when they change. switchMap will execute the service method each time the params change.

But if the EventService is keeping track of the params anyway, it’s probably the simplest to have it expose the events$ based on those params.

If you aren’t already, define the params as a BehaviorSubject with default value and add a method for consumers to modify them. Then expose a single events$ observable that represents the events based on the specific params:

service:

private params$ = new BehaviorSubject(new EventParams());

public setEventParams(params) {
   this.params$.next(params);
}

public events$ = this.params$.pipe(
    switchMap(params => this.getAllEvents(params))
);

component:

  events$ = this.eventService.events$;

  pageChanged(params) {
    // build proper params object if necessary
    this.eventService.setEventParams(params);
  }

Your component code becomes a lot simpler since it doesn’t need to be concerned with managing the observable and keeping track of params.

Leave a Reply

(*) Required, Your email will not be published