[DURACOM-304] Refactored scripts-select.component by using infinite scroll instead of page size 9999

This commit is contained in:
Alisa Ismailati
2024-11-20 15:23:09 +01:00
committed by FrancescoMolinaro
parent 4536d9c74d
commit 2b58377830
3 changed files with 133 additions and 43 deletions

View File

@@ -1,20 +1,46 @@
<div class="form-group" *ngIf="scripts$ | async"> <div class="d-flex w-100 flex-column gap-3">
<label for="process-script">{{'process.new.select-script' | translate}}</label> <div>
<select required id="process-script" <div ngbDropdown class="d-flex">
class="form-control" <input id="process-script"
name="script" class="form-control"
[(ngModel)]="selectedScript" required
#script="ngModel"> [ngModel]="selectedScript"
<option [ngValue]="undefined">{{'process.new.select-script.placeholder' | translate}}</option> placeholder="{{'process.new.select-script.placeholder' | translate}}"
<option *ngFor="let script of scripts$ | async" [ngValue]="script.id"> [ngModelOptions]="{standalone: true}"
{{script.name}} ngbDropdownToggle
</option> #script="ngModel">
</select> <div ngbDropdownMenu aria-labelledby="process-script" class="w-100 scrollable-menu"
role="menu"
(scroll)="onScroll($event)"
infiniteScroll
[infiniteScrollDistance]="5"
[infiniteScrollThrottle]="300"
[infiniteScrollUpDistance]="1.5"
[fromRoot]="true"
[scrollWindow]="false">
<button class="dropdown-item"
*ngFor="let script of scripts"
role="menuitem"
type="button"
title="{{ script.name }}"
(click)="onSelect(script);">
<span class="text-truncate">{{ script.name }}</span>
</button>
<ng-container *ngIf="(isLoading$ | async)">
<button class="dropdown-item disabled" role="menuitem">
<ds-loading message="{{'loading.default' | translate}}">
</ds-loading>
</button>
</ng-container>
</div>
</div>
</div>
<div>
<div *ngIf="script.invalid && (script.dirty || script.touched)" <div *ngIf="script.invalid && (script.dirty || script.touched)"
class="alert alert-danger validation-error"> class="alert alert-danger validation-error">
<div *ngIf="script.errors.required"> <div *ngIf="script.errors.required">
{{'process.new.select-script.required' | translate}} {{ 'process.new.select-script.required' | translate }}
</div> </div>
</div> </div>
</div>
</div> </div>

View File

@@ -0,0 +1,23 @@
.dropdown-item {
padding: 0.35rem 1rem;
&:active {
color: white !important;
}
}
.scrollable-menu {
height: auto;
max-height: var(--ds-dropdown-menu-max-height);
overflow-x: hidden;
}
li:not(:last-of-type) .dropdown-item {
border-bottom: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);
}
#entityControlsDropdownMenu {
outline: 0;
left: 0 !important;
box-shadow: var(--bs-btn-focus-box-shadow);
}

View File

@@ -1,14 +1,19 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Optional, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnDestroy, OnInit, Optional, Output } from '@angular/core';
import { ScriptDataService } from '../../../core/data/processes/script-data.service'; import { ScriptDataService } from '../../../core/data/processes/script-data.service';
import { Script } from '../../scripts/script.model'; import { Script } from '../../scripts/script.model';
import { Observable, Subscription } from 'rxjs'; import { BehaviorSubject, Observable, Subscription, tap } from 'rxjs';
import { distinctUntilChanged, filter, map, switchMap, take } from 'rxjs/operators'; import { distinctUntilChanged, filter, map, switchMap, take } from 'rxjs/operators';
import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../../../core/shared/operators'; import {
getRemoteDataPayload,
getFirstSucceededRemoteData,
getFirstCompletedRemoteData
} from '../../../core/shared/operators';
import { PaginatedList } from '../../../core/data/paginated-list.model'; import { PaginatedList } from '../../../core/data/paginated-list.model';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { hasNoValue, hasValue } from '../../../shared/empty.util'; import { hasNoValue, hasValue } from '../../../shared/empty.util';
import { ControlContainer, NgForm } from '@angular/forms'; import { ControlContainer, NgForm } from '@angular/forms';
import { controlContainerFactory } from '../process-form.component'; import { controlContainerFactory } from '../process-form.component';
import { FindListOptions } from "../../../core/data/find-list-options.model";
const SCRIPT_QUERY_PARAMETER = 'script'; const SCRIPT_QUERY_PARAMETER = 'script';
@@ -31,10 +36,20 @@ export class ScriptsSelectComponent implements OnInit, OnDestroy {
/** /**
* All available scripts * All available scripts
*/ */
scripts$: Observable<Script[]>; scripts: Script[] = [];
private _selectedScript: Script; private _selectedScript: Script;
private routeSub: Subscription; private routeSub: Subscription;
private _isLastPage = false;
scriptOptions: FindListOptions = {
elementsPerPage: 20,
currentPage: 1,
};
isLoading$: BehaviorSubject<boolean> = new BehaviorSubject(false);
constructor( constructor(
private scriptService: ScriptDataService, private scriptService: ScriptDataService,
private router: Router, private router: Router,
@@ -47,31 +62,46 @@ export class ScriptsSelectComponent implements OnInit, OnDestroy {
* Checks if the route contains a script ID and auto selects this scripts * Checks if the route contains a script ID and auto selects this scripts
*/ */
ngOnInit() { ngOnInit() {
this.scripts$ = this.scriptService.findAll({ elementsPerPage: 9999 }) this.loadScripts();
.pipe( }
getFirstSucceededRemoteData(),
getRemoteDataPayload(),
map((paginatedList: PaginatedList<Script>) => paginatedList.page)
);
this.routeSub = this.route.queryParams /**
.pipe( * Load the scripts and check if the route contains a script
filter((params: Params) => hasNoValue(params.id)), */
map((params: Params) => params[SCRIPT_QUERY_PARAMETER]), loadScripts() {
distinctUntilChanged(), if (this.isLoading$.value) return;
switchMap((id: string) => this.isLoading$.next(true);
this.scripts$
.pipe( this.routeSub = this.scriptService.findAll(this.scriptOptions).pipe(
take(1), getFirstCompletedRemoteData(),
map((scripts) => getRemoteDataPayload(),
scripts.find((script) => script.id === id) tap((paginatedList: PaginatedList<Script>) => {
) this._isLastPage = paginatedList?.pageInfo?.currentPage >= paginatedList?.pageInfo?.totalPages;
) }),
) map((paginatedList: PaginatedList<Script>) => paginatedList.page),
).subscribe((script: Script) => { ).subscribe((newScripts: Script[]) => {
this._selectedScript = script; this.scripts = [...this.scripts, ...newScripts];
this.select.emit(script); this.isLoading$.next(false);
});
const param = this.route.snapshot.queryParams[SCRIPT_QUERY_PARAMETER];
if (hasValue(param)) {
this._selectedScript = this.scripts.find((script) => script.id === param);
this.select.emit(this._selectedScript);
}
});
}
/**
* Load more scripts when the user scrolls to the bottom of the list
* @param event The scroll event
*/
onScroll(event: any) {
if (event.target.scrollTop + event.target.clientHeight >= event.target.scrollHeight) {
if (!this.isLoading$.value && !this._isLastPage) {
this.scriptOptions.currentPage++;
this.loadScripts();
}
}
} }
/** /**
@@ -93,6 +123,17 @@ export class ScriptsSelectComponent implements OnInit, OnDestroy {
); );
} }
selectScript(script: Script) {
this._selectedScript = script;
}
onSelect(newScript: Script) {
this.selectScript(newScript);
// this._selectedScript = newScript;
this.select.emit(newScript);
this.selectedScript = newScript.name;
}
@Input() @Input()
set script(value: Script) { set script(value: Script) {
this._selectedScript = value; this._selectedScript = value;