@@ -24,16 +24,18 @@ import {
2424 TranslateModule ,
2525 TranslateService ,
2626} from '@ngx-translate/core' ;
27+ import { InfiniteScrollModule } from 'ngx-infinite-scroll' ;
2728import {
2829 BehaviorSubject ,
30+ combineLatest as observableCombineLatest ,
2931 Observable ,
3032 of ,
3133 Subscription ,
3234} from 'rxjs' ;
3335import {
3436 debounceTime ,
35- distinctUntilChanged ,
3637 map ,
38+ startWith ,
3739 switchMap ,
3840 take ,
3941 tap ,
@@ -50,6 +52,7 @@ import {
5052 metadataFieldsToString ,
5153} from '../../../core/shared/operators' ;
5254import { hasValue } from '../../../shared/empty.util' ;
55+ import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component' ;
5356import { NotificationsService } from '../../../shared/notifications/notifications.service' ;
5457import { ClickOutsideDirective } from '../../../shared/utils/click-outside.directive' ;
5558import { followLink } from '../../../shared/utils/follow-link-config.model' ;
@@ -59,7 +62,7 @@ import { followLink } from '../../../shared/utils/follow-link-config.model';
5962 styleUrls : [ './metadata-field-selector.component.scss' ] ,
6063 templateUrl : './metadata-field-selector.component.html' ,
6164 standalone : true ,
62- imports : [ FormsModule , NgClass , ReactiveFormsModule , ClickOutsideDirective , NgIf , NgFor , AsyncPipe , TranslateModule ] ,
65+ imports : [ FormsModule , NgClass , ReactiveFormsModule , ClickOutsideDirective , NgIf , NgFor , AsyncPipe , TranslateModule , ThemedLoadingComponent , InfiniteScrollModule ] ,
6366} )
6467/**
6568 * Component displaying a searchable input for metadata-fields
@@ -96,7 +99,7 @@ export class MetadataFieldSelectorComponent implements OnInit, OnDestroy, AfterV
9699 * List of available metadata field options to choose from, dependent on the current query the user entered
97100 * Shows up in a dropdown below the input
98101 */
99- mdFieldOptions$ : Observable < string [ ] > ;
102+ mdFieldOptions$ : BehaviorSubject < string [ ] > = new BehaviorSubject < string [ ] > ( [ ] ) ;
100103
101104 /**
102105 * FormControl for the input
@@ -131,6 +134,30 @@ export class MetadataFieldSelectorComponent implements OnInit, OnDestroy, AfterV
131134 */
132135 subs : Subscription [ ] = [ ] ;
133136
137+
138+ /**
139+ * The current page to load
140+ * Dynamically goes up as the user scrolls down until it reaches the last page possible
141+ */
142+ currentPage$ = new BehaviorSubject ( 1 ) ;
143+
144+ /**
145+ * Whether or not the list contains a next page to load
146+ * This allows us to avoid next pages from trying to load when there are none
147+ */
148+ hasNextPage = false ;
149+
150+ /**
151+ * Whether or not new results are currently loading
152+ */
153+ loading = false ;
154+
155+ /**
156+ * Default page option for this feature
157+ */
158+ pageOptions = { elementsPerPage : 20 , sort : new SortOptions ( 'fieldName' , SortDirection . ASC ) } ;
159+
160+
134161 constructor ( protected registryService : RegistryService ,
135162 protected notificationsService : NotificationsService ,
136163 protected translate : TranslateService ) {
@@ -141,32 +168,33 @@ export class MetadataFieldSelectorComponent implements OnInit, OnDestroy, AfterV
141168 * Update the mdFieldOptions$ depending on the query$ fired by querying the server
142169 */
143170 ngOnInit ( ) : void {
171+ this . subs . push ( this . input . valueChanges . pipe (
172+ debounceTime ( this . debounceTime ) ,
173+ startWith ( '' ) ,
174+ ) . subscribe ( ( valueChange ) => {
175+ this . currentPage$ . next ( 1 ) ;
176+ if ( ! this . selectedValueLoading ) {
177+ this . query$ . next ( valueChange ) ;
178+ }
179+ this . mdField = valueChange ;
180+ this . mdFieldChange . emit ( this . mdField ) ;
181+ } ) ) ;
144182 this . subs . push (
145- this . input . valueChanges . pipe (
146- debounceTime ( this . debounceTime ) ,
147- ) . subscribe ( ( valueChange ) => {
148- if ( ! this . selectedValueLoading ) {
149- this . query$ . next ( valueChange ) ;
150- }
151- this . selectedValueLoading = false ;
152- this . mdField = valueChange ;
153- this . mdFieldChange . emit ( this . mdField ) ;
154- } ) ,
155- ) ;
156- this . mdFieldOptions$ = this . query$ . pipe (
157- distinctUntilChanged ( ) ,
158- switchMap ( ( query : string ) => {
159- this . showInvalid = false ;
160- if ( query !== null ) {
161- return this . registryService . queryMetadataFields ( query , { elementsPerPage : 10 , sort : new SortOptions ( 'fieldName' , SortDirection . ASC ) } , true , false , followLink ( 'schema' ) ) . pipe (
162- getAllSucceededRemoteData ( ) ,
163- metadataFieldsToString ( ) ,
164- ) ;
165- } else {
166- return [ [ ] ] ;
167- }
168- } ) ,
169- ) ;
183+ observableCombineLatest (
184+ this . query$ ,
185+ this . currentPage$ ,
186+ )
187+ . pipe (
188+ switchMap ( ( [ query , page ] : [ string , number ] ) => {
189+ this . loading = true ;
190+ if ( page === 1 ) {
191+ this . mdFieldOptions$ . next ( [ ] ) ;
192+ }
193+ return this . search ( query as string , page as number ) ;
194+ } ) ,
195+ ) . subscribe ( ( rd ) => {
196+ if ( ! this . selectedValueLoading ) { this . updateList ( rd ) ; }
197+ } ) ) ;
170198 }
171199
172200 /**
@@ -210,6 +238,41 @@ export class MetadataFieldSelectorComponent implements OnInit, OnDestroy, AfterV
210238 this . input . setValue ( mdFieldOption ) ;
211239 }
212240
241+
242+ /**
243+ * When the user reaches the bottom of the page (or almost) and there's a next page available, increase the current page
244+ */
245+ onScrollDown ( ) {
246+ if ( this . hasNextPage && ! this . loading ) {
247+ this . currentPage$ . next ( this . currentPage$ . value + 1 ) ;
248+ }
249+ }
250+
251+ /**
252+ * @Description It update the mdFieldOptions$ according the query result page
253+ * */
254+ updateList ( list : string [ ] ) {
255+ this . loading = false ;
256+ this . hasNextPage = list . length > 0 ;
257+ const currentEntries = this . mdFieldOptions$ . getValue ( ) ;
258+ this . mdFieldOptions$ . next ( [ ...currentEntries , ...list ] ) ;
259+ this . selectedValueLoading = false ;
260+ }
261+ /**
262+ * Perform a search for the current query and page
263+ * @param query Query to search objects for
264+ * @param page Page to retrieve
265+ * @param useCache Whether or not to use the cache
266+ */
267+ search ( query : string , page : number , useCache : boolean = true ) {
268+ return this . registryService . queryMetadataFields ( query , {
269+ elementsPerPage : this . pageOptions . elementsPerPage , sort : this . pageOptions . sort ,
270+ currentPage : page } , useCache , false , followLink ( 'schema' ) )
271+ . pipe (
272+ getAllSucceededRemoteData ( ) ,
273+ metadataFieldsToString ( ) ,
274+ ) ;
275+ }
213276 /**
214277 * Unsubscribe from any open subscriptions
215278 */
0 commit comments