How to merge data in async pipe in Angular

Viewed 2965

I have showing youtube videos by using async pipe in Angular 6. It's working fine for the first load.

This is how am using it.

Component HTML file:

<div id="youtube" class="search-results" *ngIf="(trendingVideos | async) as videos; else loadingOrError"></div>

Component TS file:

public trendingVideos: Observable<VideoClass[]>;
private loadVideos(videosPerPage?: number, regionCode?: string, videoCategoryId?: number) {
      this.trendingVideos = this.youtubeService.getTrendingVideos(videosPerPage, regionCode, videoCategoryId, false)
        .pipe(
          catchError((error: any) => {
            this.loadingError$.next(true);
            return throwError(error);
          })
        );
  }

Catch error is working fine also.

Service File:

public getTrendingVideos(videosPerPage?: number, regionCode?: string, videoCategoryId?: number, paginationMode?: boolean): Observable<VideoClass[]> {
    const params: any = {
      part: appConfig.partsToLoad,
      chart: appConfig.chart,
      videoCategoryId: videoCategoryId ? videoCategoryId : appConfig.defaultCategoryId,
      regionCode: regionCode ? regionCode : appConfig.defaultRegion,
      maxResults: videosPerPage ? videosPerPage : appConfig.maxVideosToLoad,
      key: appConfig.youtubeApiKey
    };
    return this.http.get<any>(appConfig.getYoutubeEndPoint('videos'), { params })
      .pipe(
        map(
          (data) => {
            return data.items
              .map((item) => new VideoClass(item))
              .filter((item) => item.id !== '');
          }
        ),
        catchError(this.handleError('getTrendingVideos'))
      ) as Observable<VideoClass[]>;
  }

It's working fine when I load data the first time. Now I am developing infinite scroll. So I am calling this API again. But want to merge data into previously loaded data.

It replaces the data every time with new data when my infinite scroll plugin calls this.

This is infinite scroll function:

private onScroll(){
    let videosData:Observable<VideoClass[]> = this.youtubeService.getTrendingVideos(this.videoCount, this.regionCode, this.categoryID, true)
    .pipe(
      catchError((error: any) => {
        this.loadingError$.next(true);
        return throwError(error);
      })
    );
    // Here how i merge this same video data to previously loaded trending videos data.
  }

Any Help?

2 Answers

You just have to use the rxjs map operator and return a new array inside, something like this in your case :

EDIT:

Use a behavior subject to get newly videos retrieve by api call.

Here is a live example: Stackblitz

component.ts

private videos = [];
private newVideos$ = new BehaviorSubject<VideoClass[]>([]);
public trendingVideos: Observable<VideoClass[]>;

ngOnInit(){
   this.trendingVideos = this.newVideos$.pipe(
     filter(data => data.length > 0),
     map(data => {
        this.videos = [...this.videos, ...data];
        return this.videos;
     });
}
private loadVideos(videosPerPage?: number, regionCode?: string, videoCategoryId?: number) {
  this.youtubeService.getTrendingVideos(videosPerPage, regionCode, videoCategoryId, false)
    .pipe(
      catchError((error: any) => {
        this.loadingError$.next(true);
        return throwError(error);
      })
    ).subscribe(newVideos => this.newVideos$.next(newVideos));
}

Hope it helps!

Modify Your Infinite scroll as:

private onScroll(bufferObj$) {
    return zip(bufferObj$, this.youtubeService.getTrendingVideos(this.videoCount, this.regionCode, this.categoryID, true)
    .pipe(
        catchError((error: any) => {
            this.loadingError$.next(true);
            return throwError(error);
        })
    )).pipe(map((x: any) => x[0].concat(x[1])));
}
Related