Perfect Scrollbar Performance Improvement In Angular

Here the idea is not to override the existing behavior of the perfect-scrollbar, definitely it should be an optional choice. I mean, it should work in such a way that; if we set the property to "true" then lazy loading should enable and if we set it to false then perfect-scrollbar should work as as a default one.

 
Before proceeding to this article, please go through my previous article "http://www.c-sharpcorner.com/article/usage-of-perfect-scrollbar-in-angular/" where I have explained perfect-scrollbar basics.

Here, I am going to show the steps to improve the performance of a perfect-scrollbar using lazy loading concept.  Here, lazy loading means loading the data on an on-demand basis when the scrollbar reaches the end of the panel.

The idea is not to override the existing behavior of the perfect-scrollbar; definitely, it should be an optional choice. I mean it should work in such a way that if we set the property to "true", the lazy loading should enable and if we set it to false, the perfect-scrollbar should work as a default one.

To do that, we need the below inputs,

  • isPagingMode
    This decides if the lazy loading is required or not. If we set it to "true", then lazy loading will enable and if we set it to "false", the lazy loading will be disabled.

  • PAGE_LOAD_SIZE
    This decides what is the maximum number of records to load to a perfect-scrollbar panel.

  • offset
    This decides the current pointer of a perfect-scrollbar. Initially, the offset value sets it as 0. For instance, if we set the PAGE_LOAD_SIZE value as 25; and when we scroll down to a perfect-scrollbar and it reaches to 25th record, then the offset value will be incremented by 25 (considered as end of the perfect-scrollbar) and if the perfect-scrollbar reaches to beginning of a perfect-scrollbar (considered as start point of the perfect-scrollbar), then it will be decremented by 25 and it will continue till the start point reaches to 0.

  • MINIMUM_RESULT_SIZE
This tells the minimum size of the record in a perfect-scrollbar. If the size of the record exceeds to a MINIMUM_RESULT_SIZE, then only the lazy loading will be enabled, otherwise, the lazy loading will not be enabled even after we enable "isPagingMode".

  • ps-y-reach-end
This is an event that fires when Y- axis scrolls in either direction. For more details on perfect-scrollbar event, please go through the "https://www.npmjs.com/package/perfect-scrollbar" link.
  • onYReachEnd
    This is a method which is mapped to "ps-y-reach-end" event which fires when the scroll reaches on every time. When it reaches the scroll count to PAGE_LOAD_SIZE and accordingly, it increments or decrements the offset value.

Now, it's time to show you the sample code snippet and its executed output:

user-profile.component.ts

  1. import {  
  2.     Component, Input, Output, EventEmitter, Type, OnChanges, OnInit, ViewChild, DoCheck, OnDestroy  
  3. } from '@angular/core';  
  4. import * as _ from 'underscore';  
  5. import { IListItem } from './configdir.model';  
  6. import {PerfectScrollbarComponent} from "ngx-perfect-scrollbar";  
  7. import {PAGE_LOAD_SIZE, PageChangeModel} from './page-change.model';  
  8. import {Subject} from 'rxjs/Subject';  
  9.   
  10. @Component({  
  11.     selector: 'user-profile',  
  12.     templateUrl: './user-profile.component.html',  
  13.     styleUrls: ['./user-profile.component.scss'],  
  14. })  
  15.   
  16. export class ConfigdirComponent<T extends IListItem> implements OnChanges, OnInit, DoCheck, OnDestroy {  
  17.     @ViewChild(PerfectScrollbarComponent) public perfectScrollbar: PerfectScrollbarComponent;  
  18.   
  19.     @Input() isPagingMode: boolean;  
  20.     @Output() eventReachEnd = new EventEmitter<PageChangeModel>();  
  21.     @Output() eventSearchUser = new EventEmitter<PageChangeModel>();  
  22.   
  23.     selectedItem: T = null;  
  24.     items: Array<T> = [];  
  25.     private restoreSelectedItem: T = null;  
  26.     consentFilter: IListItem = { id: 0, text: '' };  
  27.   
  28.     pageChangeModel: PageChangeModel;  
  29.     private eventReachEndSubject = new Subject<PageChangeModel>();  
  30.     private eventSearchUserSubject = new Subject<PageChangeModel>();  
  31.   
  32.     constructor() {  
  33.     }  
  34.   
  35.     initializePageChangeModel() {  
  36.         if (!this.pageChangeModel) {  
  37.             this.pageChangeModel =  new PageChangeModel();  
  38.         }  
  39.         this.pageChangeModel.offset = 0;  
  40.         this.pageChangeModel.pageSize = PAGE_LOAD_SIZE;  
  41.         this.pageChangeModel.searchText = null;  
  42.         this.pageChangeModel.isEnablePageLoad = true;  
  43.     }  
  44.   
  45.     initializePageInterval() {  
  46.         if (!this.pageChangeModel) {  
  47.             this.pageChangeModel =  new PageChangeModel();  
  48.         }  
  49.         this.pageChangeModel.offset = 0;  
  50.         this.pageChangeModel.pageSize = PAGE_LOAD_SIZE;  
  51.     }  
  52.   
  53.     fireInitialPageLoadEvent() {  
  54.         this.initializePageChangeModel();  
  55.         this.eventSearchUser.emit(this.pageChangeModel);  
  56.     }  
  57.   
  58.     ngOnInit(): void {  
  59.         if (this.isPagingMode) {  
  60.             this.initializePageChangeModel();  
  61.   
  62.             this.eventReachEndSubject.debounceTime(600).subscribe((vlu) => {  
  63.              this.pageChangeModel.offset = this.pageChangeModel.offset + this.pageChangeModel.pageSize;  
  64.                 this.eventReachEnd.emit(this.pageChangeModel);  
  65.             });  
  66.   
  67.             this.eventSearchUserSubject.debounceTime(500).subscribe((vlu) => {  
  68.                 this.initializePageInterval();  
  69.                 this.eventSearchUser.emit(this.pageChangeModel);  
  70.             });  
  71.         }  
  72.     }  
  73.   
  74.     ngDoCheck(): void {  
  75.         setTimeout(() => {  
  76.             if (this.items && this.items.length > 0) {  
  77.                 this.perfectScrollbar.ngDoCheck();  
  78.             }  
  79.         });  
  80.     }  
  81.   
  82.     ngOnChanges(): void {  
  83.         if (this.isPagingMode) {  
  84.             this.items.splice(0, this.items.length);  
  85.         }  
  86.         this.items = this.listItems;  
  87.         this.items = this.sortItems();  
  88.     }  
  89.       
  90.     private ScrollToItem(): void {  
  91.         setTimeout(() => {  
  92.             let documentItem = document.getElementsByClassName('my-scroll-container');  
  93.             let targetItem = document.getElementsByClassName('selected');  
  94.             documentItem[0].scrollTop = targetItem.length > 0 ? targetItem['0'].offsetTop : 0;  
  95.         });  
  96.     }      
  97.     }  
  98.   
  99.     onYReachEnd(value) {  
  100.         if (this.isPagingMode) {  
  101.             if (this.items && this.items.length > 0) {  
  102.                 if (this.pageChangeModel.isEnablePageLoad) {  
  103.                     this.eventReachEndSubject.next(value);  
  104.                 }  
  105.             }  
  106.         }  
  107.     }  
  108.   
  109.     ngOnDestroy() {  
  110.         if (this.isPagingMode) {  
  111.             this.eventReachEndSubject.next();  
  112.             this.eventReachEndSubject.complete();  
  113.             this.eventSearchUserSubject.next();  
  114.             this.eventSearchUserSubject.complete();  
  115.         }  
  116.     }  

user-profile.component.html

  1. <perfect-scrollbar class="my-scroll-container" (ps-y-reach-end)="onYReachEnd($event)">  
  2.                     <li class="list-group-item showtrash-icon" [ngClass]="{selected: isItemSelected(item)}"  *ngFor="let item of isPagingMode ? items : items | filterBy: consentFilter"  
  3.                       (click)="selectItem(item)">  
  4.                     <div class="config-selector-list-item-container">  
  5.                       <div class="flex-grow" title="{{item.text}}">  
  6.                         {{item.text}}  
  7.                       </div>  
  8.                       <div class="fa fa-check fa-2x check-icon" *ngIf="this.isItemSelected(item)">  
  9.                       </div>  
  10.                       <div class="fa fa-trash-o fa-x trash-icon"  (click)="this.deleteItem(item)"></div>  
  11.                       <div>  
  12.                  </div>  
  13.            </div>  
  14.        </li>  
  15. </perfect-scrollbar>  

user-profile.component.scss

  1. .my-scroll-container {  
  2.   width: 32.8rem;  
  3.   height: 20rem;  
  4. }  

page-change.model.ts

  1. export const PAGE_LOAD_SIZE = 25;  
  2. export const MINIMUM_RESULT_SIZE = 5;  
  3.   
  4. export class PageChangeModel {  
  5.     offset: number;  
  6.     pageSize: number;  
  7.     searchText: string;  
  8.     isEnablePageLoad: boolean;  

Output

Angular

Angular

I would appreciate your valuable comments.