From 2b744664db978ab115d44030a8c2accfa2d5a06c Mon Sep 17 00:00:00 2001 From: Leandro Candeias Date: Thu, 4 Sep 2025 18:06:28 +0100 Subject: [PATCH 1/3] Add Infinite Scroll to extensible-table --- .../extensible-table.component.html | 5 +++++ .../extensible-table.component.ts | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html index bfedbe8a44..e614dbc74c 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html @@ -7,6 +7,11 @@ (activate)="tableActivate.emit($event)" (select)="onSelect($event)" [selected]="selected" + (scroll)="onScroll($event)" + [scrollbarV]="infiniteScroll" + [style.height]="infiniteScroll ? '300px' : 'auto'" + [loadingIndicator]="infiniteScroll && isLoading" + [footerHeight]="infiniteScroll ? false : 50" > @if(selectable) { diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts index fdb19e553d..285a63872d 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts @@ -118,6 +118,10 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn @Input() selected: any[] = []; @Output() selectionChange = new EventEmitter(); + @Input() infiniteScroll = false; + @Input() isLoading = false; + @Output() loadMore = new EventEmitter(); + hasAtLeastOnePermittedAction: boolean; readonly propList: EntityPropList; @@ -247,10 +251,23 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn this.selectionChange.emit(selected); } + onScroll(scrollEvent) { + if ( + this.infiniteScroll && + !this.isLoading && + scrollEvent?.target?.offsetHeight + scrollEvent?.target?.scrollTop >= + scrollEvent?.target?.scrollHeight - 1 + ) { + this.loadMore.emit(); + } + } + ngAfterViewInit(): void { - this.list?.requestStatus$?.pipe(filter(status => status === 'loading')).subscribe(() => { + if (!this.infiniteScroll) { + this.list?.requestStatus$?.pipe(filter(status => status === 'loading')).subscribe(() => { this.data = []; this.cdr.markForCheck(); }); + } } } From f1342bb50b5c9b79ef0646095527de87808c027f Mon Sep 17 00:00:00 2001 From: Leandro Candeias Date: Thu, 4 Sep 2025 19:42:53 +0100 Subject: [PATCH 2/3] Add TableHeight Input to extensible-table --- .../extensible-table/extensible-table.component.html | 2 +- .../extensible-table/extensible-table.component.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html index e614dbc74c..4628c54197 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html @@ -9,7 +9,7 @@ [selected]="selected" (scroll)="onScroll($event)" [scrollbarV]="infiniteScroll" - [style.height]="infiniteScroll ? '300px' : 'auto'" + [style.height]="getTableHeight()" [loadingIndicator]="infiniteScroll && isLoading" [footerHeight]="infiniteScroll ? false : 50" > diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts index 285a63872d..88b34706ca 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts @@ -121,6 +121,7 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn @Input() infiniteScroll = false; @Input() isLoading = false; @Output() loadMore = new EventEmitter(); + @Input() tableHeight: number; hasAtLeastOnePermittedAction: boolean; @@ -262,6 +263,12 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn } } + getTableHeight() { + if (!this.infiniteScroll) return 'auto'; + + return this.tableHeight ? `${this.tableHeight}px` : 'auto'; + } + ngAfterViewInit(): void { if (!this.infiniteScroll) { this.list?.requestStatus$?.pipe(filter(status => status === 'loading')).subscribe(() => { From 2996b47823de29c9e2cc5837fd707c9035c171bc Mon Sep 17 00:00:00 2001 From: sumeyye Date: Tue, 9 Sep 2025 15:49:27 +0300 Subject: [PATCH 3/3] update: enhancement for infinite scroll support --- .../extensible-table.component.ts | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts index b96d7dfe5b..98132bc5f6 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts @@ -10,16 +10,16 @@ import { Input, LOCALE_ID, OnChanges, + OnDestroy, Output, signal, SimpleChanges, TemplateRef, TrackByFunction, - ViewChild, } from '@angular/core'; import { AsyncPipe, NgComponentOutlet, NgTemplateOutlet } from '@angular/common'; -import { Observable, filter, map } from 'rxjs'; +import { Observable, filter, map, Subject, debounceTime, distinctUntilChanged } from 'rxjs'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { NgxDatatableModule, SelectionType } from '@swimlane/ngx-datatable'; @@ -75,7 +75,7 @@ const DEFAULT_ACTIONS_COLUMN_WIDTH = 150; templateUrl: './extensible-table.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ExtensibleTableComponent implements OnChanges, AfterViewInit { +export class ExtensibleTableComponent implements OnChanges, AfterViewInit, OnDestroy { readonly #injector = inject(Injector); readonly getInjected = this.#injector.get.bind(this.#injector); protected readonly cdr = inject(ChangeDetectorRef); @@ -113,13 +113,14 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn this._selectionType = typeof value === 'string' ? SelectionType[value] : value; } _selectionType: SelectionType = SelectionType.multiClick; - - + @Input() selected: any[] = []; @Output() selectionChange = new EventEmitter(); + // Infinite scroll configuration @Input() infiniteScroll = false; @Input() isLoading = false; + @Input() scrollThreshold = 10; @Output() loadMore = new EventEmitter(); @Input() tableHeight: number; @@ -134,6 +135,12 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn // Signal for actions column width private readonly _actionsColumnWidth = signal(DEFAULT_ACTIONS_COLUMN_WIDTH); + // Infinite scroll: debounced load more subject + private readonly loadMoreSubject = new Subject(); + private readonly loadMoreSubscription = this.loadMoreSubject + .pipe(debounceTime(100), distinctUntilChanged()) + .subscribe(() => this.triggerLoadMore()); + readonly columnWidths = computed(() => { const actionsColumn = this._actionsColumnWidth(); const widths = [actionsColumn]; @@ -221,7 +228,6 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn return record; }); - } isVisibleActions(rowData: any): boolean { @@ -252,17 +258,34 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn this.selectionChange.emit(selected); } - onScroll(scrollEvent) { - if ( - this.infiniteScroll && - !this.isLoading && - scrollEvent?.target?.offsetHeight + scrollEvent?.target?.scrollTop >= - scrollEvent?.target?.scrollHeight - 1 - ) { - this.loadMore.emit(); + onScroll(scrollEvent: Event): void { + if (!this.shouldHandleScroll()) { + return; + } + + const target = scrollEvent.target as HTMLElement; + if (!target) { + return; + } + + if (this.isNearScrollBottom(target)) { + this.loadMoreSubject.next(); } } + private shouldHandleScroll(): boolean { + return this.infiniteScroll && !this.isLoading; + } + + private isNearScrollBottom(element: HTMLElement): boolean { + const { offsetHeight, scrollTop, scrollHeight } = element; + return offsetHeight + scrollTop >= scrollHeight - this.scrollThreshold; + } + + private triggerLoadMore(): void { + this.loadMore.emit(); + } + getTableHeight() { if (!this.infiniteScroll) return 'auto'; @@ -277,4 +300,8 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn }); } } + + ngOnDestroy(): void { + this.loadMoreSubscription.unsubscribe(); + } }