DEV Community

Jameer Khan
Jameer Khan

Posted on • Updated on • Originally published at stackblogger.com

Angular 12 and RxJS: Infinite Scroll With API

The article is originally published to my blog: Angular and RxJS Infinite Scroll

Infinite Scroll is the ability to scroll through a large infinite list without compromising the performance of application. It improves user experience of the application. In this article I will cover how to implement infinite scroll with API in Angular 12 with the help of RxJS Operators.

How does Infinite Scroll Works

A question comes in mind that how it works internally. It works like there is a big list say 10k items are there. Displaying all those items in once is not possible as it will break the application. And you can not put numbering pagination as that will look bad on the place or you have dropdown (dropdowns can not number paginations).

In that case you need infinite scroll. It loads only 10 (size is provided in configuration) records at a time and every time user scrolls to the end of list, application automatically calls the API to fetch next 10 records. So as soon as you reach end of the list, it fetches you the next 10 records. It gives you next results till all the results are finished in database.

Note: I am using Angular 12 and latest versions of RxJS Operators for this example. However the code should work on below versions too. But if you face any issue implementing it in below version, let me know in comments I will check them.

Let's implement infinite scroll using RxJS

Setup an Angular application

You can skip this step if you have an existing angular application.

Create a application with below command:

ng n angular-rxjs-infite-scroll
Enter fullscreen mode Exit fullscreen mode

Setup Angular Application

Create a Service file

You can skip this step if you have already a service file.

Run the command to create a service file: app.service.ts

ng g s app
Enter fullscreen mode Exit fullscreen mode

Generate an Angular Service

Import HttpClientModule in app.module.ts

Import the HttpClientModule package the application module. It is required to consume the Http APIs.

import { HttpClientModule } from '@angular/common/http';

// add in imports
imports: [
    // other modules,
    HttpClientModule
]


Enter fullscreen mode Exit fullscreen mode

Import HttpClientModule in App.Module.ts file

Consume API

The next step is to create a method getData in app.service.ts file to fetch the API results. I am using JSON PlaceHolder Fake API.

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AppService {

  constructor(private httpClient: HttpClient) { }

  public getData(pageNumber: number, pageSize: number) {
    return this.httpClient.get(`https://jsonplaceholder.typicode.com/photos?_start=${pageNumber}&_limit=${pageSize}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Create Infinite Scroll Logic

Create logics to scroll items infinitely. For this purpose I have used following RxJS Observables / Operators:

  • BehaviorSubject– to store the fetched items
  • forkJoin– to call multiple observables simultaneously
  • fromEvent– to generate scroll event on div
  • map– to get only scrollTop data
  • take– to take the last items from existing fetched data

Here is the complete code.

import { Component, OnInit } from '@angular/core';
import { BehaviorSubject, forkJoin, fromEvent, Observable } from "rxjs";
import { map, take } from "rxjs/operators";
import { AppService } from './app.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  obsArray: BehaviorSubject<any[]> = new BehaviorSubject<any[]>([]);
  items$: Observable<any> = this.obsArray.asObservable();
  currentPage: number = 0;
  pageSize: number = 10;

  constructor(private appService: AppService) { }

  ngOnInit() {
    this.getData();
  }

  private getData() {
    this.appService.getData(this.currentPage, this.pageSize).subscribe((data: any) => {
      this.obsArray.next(data);
    });

    const content = document.querySelector('.items');
    const scroll$ = fromEvent(content!, 'scroll').pipe(map(() => { return content!.scrollTop; }));

    scroll$.subscribe((scrollPos) => {
      let limit = content!.scrollHeight - content!.clientHeight;
      if (scrollPos === limit) {
        this.currentPage += this.pageSize;
        forkJoin([this.items$.pipe(take(1)), this.appService.getData(this.currentPage, this.pageSize)]).subscribe((data: Array<Array<any>>) => {
          const newArr = [...data[0], ...data[1]];
          this.obsArray.next(newArr);
        });
      }
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Code Explanation:

this.appService.getData(this.currentPage, this.pageSize).subscribe((data: any) => {
    this.obsArray.next(data);
});
Enter fullscreen mode Exit fullscreen mode

Above code will get you the first 10 items to bind data on page load.

const content = document.querySelector('.items');
const scroll$ = fromEvent(content!, 'scroll').pipe(map(() => { return content!.scrollTop; }));
Enter fullscreen mode Exit fullscreen mode

Above code block catches the scroll event on div.

let limit = content!.scrollHeight - content!.clientHeight;
if (scrollPos === limit) {
    // other codes here
}
Enter fullscreen mode Exit fullscreen mode

Above code block checks whether you have scrolled through the end of items. If yes, do whatever code you want to do in the if block.

this.currentPage += this.pageSize;
forkJoin([this.items$.pipe(take(1)), this.appService.getData(this.currentPage, this.pageSize)]).subscribe((data: Array<Array<any>>) => {
    const newArr = [...data[0], ...data[1]];
    this.obsArray.next(newArr);
});
Enter fullscreen mode Exit fullscreen mode

This code block will increase the current page records and fetch the next page data. Once the data is received, merged the received data with existing array and provide to BehaviorSubject observable. That will refresh the bind data on page and show you the new data (while keeping the existing data in list).

Do Some Styling

Add some css to restrict height of the div and display scroll bar.

app.component.scss

.items {
    max-height: 300px;
    width: 460px;
    overflow: scroll;
}
Enter fullscreen mode Exit fullscreen mode

Template Code

Add following html to your app.component.html file.

<div class="items">
    <div *ngFor="let item of items$ | async">
        <b>ID: </b> {{item.id}}<br>
        <b>Title: </b>{{item.title}}<br>
        <img src="{{item.thumbnailUrl}}" alt="">
        <br>
        <br>
        <br>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Output

Its time to run the app and check output. Check the browser. You should see the infinite scroll in your div.

Server Side Infinite Scroll in Angular using RxJS

Conclusion

The article is originally published to my blog: Angular and RxJS Infinite Scroll

In this article we covered how to implement a server side infinite scroll in Angular using RxJS Operators.

Must Read Articles on Angular:

Discussion (0)