src/components/sq-infinity-scroll/sq-infinity-scroll.component.ts
Represents the SqInfinityComponent, a component for infinite scrolling.
Example :<sq-infinity-scroll [length]="totalItems" [hasMore]="hasMoreData" [loading]="isLoading" (scrolledEmitter)="loadMoreItems()">
<!-- Content Here -->
</sq-infinity-scroll>
AfterViewInit
AfterContentChecked
OnDestroy
| selector | sq-infinity-scroll |
| standalone | true |
| imports |
SqLoaderComponent
|
| styleUrls | ./sq-infinity-scroll.component.scss |
| templateUrl | ./sq-infinity-scroll.component.html |
Properties |
|
Methods |
Inputs |
Outputs |
constructor(documentImported: Document, getWindow: GetWindow)
|
||||||||||||
|
Creates an instance of SqInfinityComponent.
Parameters :
|
| elementToScrollId | |
Type : string
|
|
|
The ID of the element to scroll (if using a custom scrolling element). |
|
| endMessage | |
Type : string
|
|
|
The message to display when reaching the end of the list. |
|
| hasMore | |
Type : boolean | string
|
|
Default value : true
|
|
|
Indicates whether there are more items to load. |
|
| length | |
Type : number
|
|
Default value : 0
|
|
|
The total number of items in the list. |
|
| loaderColor | |
Type : string
|
|
|
The color of the loader. |
|
| loading | |
Type : boolean
|
|
|
Indicates whether data is currently being loaded. |
|
| scrolledEmitter | |
Type : EventEmitter<void>
|
|
|
Event emitter for when the user scrolls to trigger loading more items. |
|
| ngAfterContentChecked |
ngAfterContentChecked()
|
|
Performs actions after content has been checked.
Returns :
void
|
| ngAfterViewInit |
ngAfterViewInit()
|
|
Performs actions after the view has been initialized.
Returns :
void
|
| ngOnDestroy |
ngOnDestroy()
|
|
Performs actions before the component is destroyed.
Returns :
void
|
| document |
Type : Document
|
|
Reference to the Document object for interacting with the DOM. |
| Public documentImported |
Type : Document
|
Decorators :
@Inject(DOCUMENT)
|
|
Reference to the Document object for interacting with the DOM.
|
| Optional elementToScroll |
Type : HTMLElement | null |
|
|
Element that have the scroll listener |
| Public getWindow |
Type : GetWindow
|
|
Reference to the GetWindow service for safely accessing the window object.
|
| onScroll |
Default value : () => {...}
|
|
Handles the scroll event and triggers loading more items if applicable. |
| Optional scrollElement |
Type : ElementRef
|
Decorators :
@ViewChild('scroll', {static: true})
|
|
Reference to the scroll element. |
| tresholdScroll |
Type : number
|
Default value : 25
|
|
Threshold for scrolling. |
import { DOCUMENT } from '@angular/common';
import {
AfterContentChecked,
AfterViewInit,
Component,
ElementRef,
EventEmitter,
Inject,
Input,
OnDestroy,
Output,
ViewChild,
} from '@angular/core';
import { GetWindow } from '../../helpers/window.helper';
import { SqLoaderComponent } from '../sq-loader/sq-loader.component';
/**
* Represents the SqInfinityComponent, a component for infinite scrolling.
*
* @implements {AfterViewInit}
* @implements {AfterContentChecked}
* @implements {OnDestroy}
*
* @example
* <sq-infinity-scroll [length]="totalItems" [hasMore]="hasMoreData" [loading]="isLoading" (scrolledEmitter)="loadMoreItems()">
* <!-- Content Here -->
* </sq-infinity-scroll>
*/
@Component({
selector: 'sq-infinity-scroll',
templateUrl: './sq-infinity-scroll.component.html',
styleUrls: ['./sq-infinity-scroll.component.scss'],
standalone: true,
imports: [SqLoaderComponent],
})
export class SqInfinityComponent implements AfterViewInit, AfterContentChecked, OnDestroy {
/**
* Reference to the scroll element.
*/
@ViewChild('scroll', { static: true }) scrollElement?: ElementRef;
/**
* The total number of items in the list.
*/
@Input() length = 0;
/**
* The message to display when reaching the end of the list.
*/
@Input() endMessage?: string;
/**
* Indicates whether there are more items to load.
*/
@Input() hasMore?: boolean | string = true;
/**
* Indicates whether data is currently being loaded.
*/
@Input() loading?: boolean;
/**
* The color of the loader.
*/
@Input() loaderColor?: string;
/**
* The ID of the element to scroll (if using a custom scrolling element).
*/
@Input() elementToScrollId?: string;
/**
* Event emitter for when the user scrolls to trigger loading more items.
*/
@Output() scrolledEmitter: EventEmitter<void> = new EventEmitter();
/**
* Element that have the scroll listener
*/
elementToScroll?: HTMLElement | null | (Window & typeof globalThis);
/**
* Reference to the Document object for interacting with the DOM.
*/
document: Document;
/**
* Threshold for scrolling.
*/
tresholdScroll = 25;
/**
* Creates an instance of SqInfinityComponent.
* @constructor
* @param {Document} documentImported Reference to the Document object for interacting with the DOM.
* @param {GetWindow} getWindow Reference to the GetWindow service for safely accessing the window object.
*/
constructor(
@Inject(DOCUMENT) public documentImported: Document,
public getWindow: GetWindow
) {
this.document = this.documentImported || document;
}
/**
* Performs actions after the view has been initialized.
*/
ngAfterViewInit(): void {
const { elementToScrollId } = this;
if (elementToScrollId) {
this.elementToScroll = this.document.getElementById(elementToScrollId);
}
if (!elementToScrollId || !this.elementToScroll) {
this.elementToScroll = this.getWindow.window();
}
this.elementToScroll?.addEventListener('scroll', this.onScroll, false);
}
/**
* Performs actions after content has been checked.
*/
ngAfterContentChecked(): void {
if (
this.elementToScrollId &&
this.elementToScroll &&
this.elementToScroll instanceof HTMLElement &&
typeof this.elementToScroll.getAttribute === 'undefined'
) {
const element = this.document.getElementById(this.elementToScrollId);
if (element) {
this.elementToScroll.removeEventListener('scroll', this.onScroll, false);
element.addEventListener('scroll', this.onScroll, false);
this.elementToScroll = element;
}
}
}
/**
* Performs actions before the component is destroyed.
*/
ngOnDestroy(): void {
this.elementToScroll?.removeEventListener('scroll', this.onScroll, false);
}
/**
* Handles the scroll event and triggers loading more items if applicable.
*/
onScroll = () => {
if (!this.loading && this.length > 0 && this.hasMore) {
if (this.elementToScrollId && this.elementToScroll instanceof HTMLElement) {
const allScroll = this.elementToScroll?.scrollTop + this.elementToScroll?.clientHeight;
if (allScroll + this.tresholdScroll >= this.elementToScroll?.scrollHeight) {
this.elementToScroll?.removeEventListener('scroll', this.onScroll, false);
this.scrolledEmitter.emit();
this.elementToScroll?.addEventListener('scroll', this.onScroll, false);
}
} else if (this.elementToScroll instanceof Window) {
const elementHeight = this.elementToScroll?.innerHeight;
const elementY = this.elementToScroll?.scrollY;
if (
elementHeight + elementY + this.tresholdScroll >=
this.scrollElement?.nativeElement.offsetHeight + this.scrollElement?.nativeElement.offsetTop
) {
this.elementToScroll?.removeEventListener('scroll', this.onScroll, false);
this.scrolledEmitter.emit();
this.elementToScroll?.addEventListener('scroll', this.onScroll, false);
}
}
}
};
}
<div #scroll class="scroll">
<ng-content></ng-content>
@if (loading) {
<div class="infinity-loader">
<sq-loader color="{{ loaderColor }}"></sq-loader>
</div>
}
@if (!hasMore && endMessage) {
<div class="wrapper-message">
<p>
{{ endMessage || '' }}
</p>
</div>
}
</div>
./sq-infinity-scroll.component.scss
.scroll {
width: 100%;
height: auto;
position: relative;
.wrapper-message {
p {
text-align: center;
font-size: 0.86rem;
margin: 1.1rem auto;
}
}
}
.infinity-loader {
position: absolute;
bottom: 0;
right: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--white-html);
}