Angular 12 and RxJS: Infinite Scroll With API
Tags: angular, rxjs, infinitescroll, observable
Published: 2021-10-16 12:52:01

The article is originally published to my blog: [Angular and RxJS Infinite Scroll](https://stackblogger.com/angular-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
```
![Setup Angular Application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rldprvqfyx70o1l868gt.PNG)

## 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
```
![Generate an Angular Service](https://stackblogger.com/wp-content/uploads/2021/10/create-a-service-min.png)

## Import HttpClientModule in `app.module.ts`

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

```javascript
import { HttpClientModule } from '@angular/common/http';
 
// add in imports
imports: [
    // other modules,
    HttpClientModule
]


```
![Import HttpClientModule in App.Module.ts file](https://stackblogger.com/wp-content/uploads/2021/10/import-httpclientmodule-in-app-module-min.png)

## 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](https://jsonplaceholder.typicode.com/) API.

```javascript
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}`);
  }
}
```

## 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.

```javascript
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);
        });
      }
    });
  }
}
```

**Code Explanation:**

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

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

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

Above code block catches the scroll event on div.

```javascript
let limit = content!.scrollHeight - content!.clientHeight;
if (scrollPos === limit) {
    // other codes here
}
```

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.

```javascript
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);
});
```

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`
```css
.items {
    max-height: 300px;
    width: 460px;
    overflow: scroll;
}
```

## Template Code

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

```html
<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>
```

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lsu8pu9y3679e30ge5z6.gif)

## Conclusion

The article is originally published to my blog: [Angular and RxJS Infinite Scroll](https://stackblogger.com/angular-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:
* [5 Best Google Charts to Use in Angular (2021)](https://stackblogger.com/5-best-google-charts-for-angular/)
* [Angular Material 12 Table, Sorting, Searching & Pagination](https://stackblogger.com/angular-material-data-table-tutorial/)
* [RxJS forkJoin: Definition and Real World Uses](https://stackblogger.com/rxjs-forkjoin-definition-and-real-world-uses/)
* [5 Great SEO Ideas To Improve Angular Website Ranking](https://stackblogger.com/5-great-seo-ideas-to-improve-angular-website-ranking/)
* [Real World Examples of 5 Common Observable Operators](https://stackblogger.com/real-world-examples-of-some-common-observable-operators/)