[Fixed] Angular – scroll to the bottom of the page causes one function call

Issue

I would like make on my site a solution like on well known sites like Instagram,
when user is scrolling the page and reach the bottom of the page another request is send and another photos are loaded from API.

Code I have:

  @HostListener('window:scroll', ['$event'])
  getScrollHeight(): void {
    if (document.body.scrollTop + window.innerHeight >= document.body.scrollHeight) {
      console.log('yes');
      this.pageNumber += 1;
      this.fetchData(this.pageNumber); //1
    }
  }

Since in if statement there is a sign >= this return true multiple times and the method this.fetchData() is called also multiple times but I need only one
when I change >= into === sometimes it is not true (it vary how fast I scroll)

any solution?

working repo: https://stackblitz.com/edit/angular-bottom-of-the-page

Solution

There are a few different ways you could approach this problem. Most likely, given the code you’ve shared, the most straightforward approach would be to utilize a property tracking the ‘loading’ status. Your stackblitz example doesn’t show the fetchData method that’s in your question. I stubbed one out below to assume a 1s latency in the call.

My assumption is that your fetchData call, loads more results and then makes the content of your page larger, pushing the window.innerHeight and window.scrollY down so that this event will fire again when the bottom of the page is reached a second time.

Is isLoading property is set to true when you go to fetch more data and then to false when the new data is received. This will allow for the event to happen multiple times, but should wait until the first event finishes before firing another.

    @Component({
      selector: "my-app",
      templateUrl: "./app.component.html",
      styleUrls: ["./app.component.css"]
    })
    export class AppComponent {
      name = "Angular " + VERSION.major;
    
      pageNumber = 1;
    
      isLoading = false;
      @HostListener("window:scroll", ["$event"])
      getScrollHeight(): void {
        if (window.innerHeight + window.scrollY >= document.body.offsetHeight && !this.isLoading) {
          console.log("bottom of the page");
          this.pageNumber += 1;
          this.isLoading = true;
          this.fetchData()
              .pipe(
                tap(() => this.isLoading = false)
              )
              .subscribe();
        }
      }
    
      fetchData() {
        return of('foo')
                .pipe(delay(1000));
      }
    }

Another approach would be to utilize the Intersection Observer API.

Leave a Reply

(*) Required, Your email will not be published