Implementing Infinite Scroll in Angular.js

Infinite scroll is a sophisticated task in Angular.js. Infinite scroll is used by  big shots like Google, Apple, Amazon, Disney etc. You can install infinite scroll using npm.

 npm install ngx-infinite-scroll --save

Infinite scroll is used mostly in Single Page Applications to display data by continuously loading data as the user scrolls down the page. First step to implement infinite scroll is to create the component. Add an Observable element at the end of the list of your displayed items.


 @Component({

  selector: 'infinite-scroll',

  template: `<ng-content></ng-content><div #anchor></div>`,

})

export class InfiniteScrollComponent implements OnInit, OnDestroy {

  @Input() options = {};

  @Output() scrolled = new EventEmitter();

  @ViewChild('anchor') anchor: ElementRef<HTMLElement>;

  private observer: IntersectionObserver;

  constructor(private host: ElementRef) { }

  get element() {

    return this.host.nativeElement;

  }

}


To make it as flexible as possible let the consumers pass the template they want using ng-content. We add an anchor element under the template. This will be the target element we watch. 


@Component({

  selector: 'infinite-scroll',

  template: `<ng-content></ng-content><div #anchor></div>`

})

export class InfiniteScrollComponent implements OnInit, OnDestroy {

  ...

  ngOnInit() {

    const options = {

      root: null,

      ...this.options

    };

   this.observer = new IntersectionObserver(([entry]) => {

      entry.isIntersecting && this.scrolled.emit();

    }, options);

    this.observer.observe(this.anchor.nativeElement);

 }

}


Create a new IntersectionObserver. It will receive entries as arguments. This function can be called whenever the observed element enters the viewport. By checking isIntersection property we can see element visibility status. If the status is true the target element has become visible as the threshold that we passed. The default threshold  value is 0. Pass the observe() method to the anchor element. The root property indicates the element which is used as a viewport for checking target visibility. When you need a scrollable container and it to act as a root element. 

 

@Component({

  selector: 'infinite-scroll',

  template: `<ng-content></ng-content><div #anchor></div>`

})

export class InfiniteScrollComponent implements OnInit, OnDestroy {

  ...

  ngOnInit() {

    const options = {

      root: this.isHostScrollable() ? this.host.nativeElement : null,

      ...this.options

    };

    this.observer = new IntersectionObserver(([entry]) => {

      entry.isIntersecting && this.scrolled.emit();

    }, options);


    this.observer.observe(this.anchor.nativeElement);

  }

  private isHostScrollable() {

    const style = window.getComputedStyle(this.element);


    return style.getPropertyValue('overflow') === 'auto' ||

      style.getPropertyValue('overflow-y') === 'scroll';

  }

}

Disconnect the observer when the component is destroyed.

ngOnDestroy() {

  this.observer.disconnect();

}


See the example below to see how to use it.


@Component({

  selector: 'app-tweets',

  template: `

     <infinite-scroll (scrolled)="onScroll()">

       <app-tweet *ngFor="let tweet of tweets$ | async" [tweet]="tweet"></app-tweet>

       <div *ngIf="isLoading$ | async">

         Fetching tweets...

       </div>

     </infinite-scroll>

})

export class TweetsComponent implements OnInit {

  ...

}


Example for a scrollable container.


@Component({

  selector: 'app-tweets',

  template: `

     <infinite-scroll (scrolled)="onScroll()" style="height: 400px; overflow-y: auto">

       ...

     </infinite-scroll>

})

export class TweetsComponent implements OnInit {

  ...

}