Skip to content

Commit a201d2f

Browse files
author
Zahraa Chreim
committed
132284: Add paginated vocabulary search with ‘Show previous/next results’ controls in hierarchical browse
1 parent 417f835 commit a201d2f

4 files changed

Lines changed: 97 additions & 3 deletions

File tree

src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,20 @@ <h2 class="h4 text-center text-muted mt-4" >
139139
</cdk-tree-node>
140140
</cdk-tree>
141141
</div>
142+
<!-- Show previous/next results pagination -->
143+
@if ((showPreviousPage$ | async) || (showNextPage$ | async)) {
144+
<div class="mb-3 d-flex gap-2">
145+
@if (showPreviousPage$ | async) {
146+
<button class="btn btn-outline-secondary"
147+
(click)="loadPreviousPage()">
148+
{{ 'browse.taxonomy.show_previous_results' | translate }}
149+
</button>
150+
}
151+
@if (showNextPage$ | async) {
152+
<button class="btn btn-outline-secondary"
153+
(click)="loadNextPage()">
154+
{{ 'browse.taxonomy.show_next_results' | translate }}
155+
</button>
156+
}
157+
</div>
158+
}

src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
2020
import { TranslateModule } from '@ngx-translate/core';
2121
import {
2222
Observable,
23+
of,
2324
Subscription,
2425
} from 'rxjs';
2526

@@ -159,6 +160,14 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
159160

160161
readonly AlertType = AlertType;
161162

163+
public showNextPage$ = this.vocabularyTreeviewService.showNextPageSubject
164+
? this.vocabularyTreeviewService.showNextPageSubject.asObservable()
165+
: of(false);
166+
167+
public showPreviousPage$ = this.vocabularyTreeviewService.showPreviousPageSubject
168+
? this.vocabularyTreeviewService.showPreviousPageSubject.asObservable()
169+
: of(false);
170+
162171
/**
163172
* Initialize instance variables
164173
*
@@ -314,6 +323,30 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
314323
}
315324
}
316325

326+
/**
327+
* Loads the next page of vocabulary search results.
328+
* Increments the current page in the service and re-triggers the query with the same search term and selection.
329+
*/
330+
loadNextPage(): void {
331+
const svc = this.vocabularyTreeviewService;
332+
333+
if (svc.currentPage < svc.totalPages) {
334+
svc.searchByQueryAndPage(svc.queryInProgress, [], svc.currentPage + 1);
335+
}
336+
}
337+
338+
/**
339+
* Loads the previous page of vocabulary search results.
340+
* Decrements the current page in the service and re-triggers the query with the same search term and selection.
341+
*/
342+
loadPreviousPage(): void {
343+
const svc = this.vocabularyTreeviewService;
344+
345+
if (svc.currentPage > 1) {
346+
svc.searchByQueryAndPage(svc.queryInProgress, [], svc.currentPage - 1);
347+
}
348+
}
349+
317350
/**
318351
* Check if search box contains any text
319352
*/

src/app/shared/form/vocabulary-treeview/vocabulary-treeview.service.ts

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ import {
1010
merge,
1111
mergeMap,
1212
scan,
13+
tap,
1314
} from 'rxjs/operators';
1415

1516
import { PaginatedList } from '../../../core/data/paginated-list.model';
17+
import { RemoteData } from '../../../core/data/remote-data';
1618
import {
19+
getFirstSucceededRemoteData,
1720
getFirstSucceededRemoteDataPayload,
1821
getFirstSucceededRemoteListPayload,
1922
} from '../../../core/shared/operators';
@@ -90,6 +93,12 @@ export class VocabularyTreeviewService {
9093
*/
9194
private hideSearchingWhenUnsubscribed$ = new Observable(() => () => this.loading.next(false));
9295

96+
public currentPage = 1;
97+
public totalPages = 1;
98+
public queryInProgress = '';
99+
public showNextPageSubject = new BehaviorSubject<boolean>(false);
100+
public showPreviousPageSubject = new BehaviorSubject<boolean>(false);
101+
93102
/**
94103
* Initialize instance variables
95104
*
@@ -197,20 +206,51 @@ export class VocabularyTreeviewService {
197206
}
198207

199208
/**
200-
* Perform a search operation by query
209+
* Initiates a vocabulary search using the provided query term and selection, starting from the first page.
210+
*
211+
* @param query - The text input to search for within the vocabulary.
212+
* @param selectedItems - Currently selected vocabulary item IDs to retain in the result.
201213
*/
202214
searchByQuery(query: string, selectedItems: string[]) {
215+
this.searchByQueryAndPage(query, selectedItems, 1);
216+
}
217+
218+
/**
219+
* Executes a paginated vocabulary search with the given query, selection, and page number.
220+
* Updates pagination state, loading indicators, and triggers the vocabulary tree rebuild.
221+
*
222+
* @param query - The search term to filter vocabulary entries.
223+
* @param selectedItems - IDs of items currently selected in the tree.
224+
* @param page - The page number to fetch (1-based index).
225+
*/
226+
searchByQueryAndPage(query: string, selectedItems: string[], page: number = 1) {
203227
this.loading.next(true);
228+
this.queryInProgress = query;
229+
this.currentPage = page;
230+
204231
if (isEmpty(this.storedNodes)) {
205232
this.storedNodes = this.dataChange.value;
206233
this.storedNodeMap = this.nodeMap;
207234
}
208235
this.nodeMap = new Map<string, TreeviewNode>();
209236
this.dataChange.next([]);
210237

211-
this.vocabularyService.getVocabularyEntriesByValue(query, false, this.vocabularyOptions, new PageInfo()).pipe(
238+
const pageInfo = new PageInfo({
239+
elementsPerPage: 20,
240+
currentPage: page,
241+
totalElements: 0,
242+
totalPages: 0,
243+
});
244+
245+
this.vocabularyService.getVocabularyEntriesByValue(query, false, this.vocabularyOptions, pageInfo).pipe(
246+
getFirstSucceededRemoteData(),
247+
tap((rd: RemoteData<PaginatedList<VocabularyEntry>>) => {
248+
this.totalPages = rd.payload.pageInfo.totalPages;
249+
this.showPreviousPageSubject.next(rd.payload.pageInfo.currentPage > 1);
250+
this.showNextPageSubject.next(rd.payload.pageInfo.currentPage < this.totalPages);
251+
}),
212252
getFirstSucceededRemoteListPayload(),
213-
mergeMap((result: VocabularyEntry[]) => (result.length > 0) ? result : of(null)),
253+
mergeMap((result: VocabularyEntry[]) => result.length > 0 ? result : of(null)),
214254
mergeMap((entry: VocabularyEntry) =>
215255
this.vocabularyService.findEntryDetailById(entry.otherInformation.id, this.vocabularyName).pipe(
216256
getFirstSucceededRemoteDataPayload(),

src/assets/i18n/en.json5

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,10 @@
11271127

11281128
"browse.title": "Browsing by {{ field }}{{ startsWith }} {{ value }}",
11291129

1130+
"browse.taxonomy.show_next_results": "Show next results",
1131+
1132+
"browse.taxonomy.show_previous_results": "Show previous results",
1133+
11301134
"browse.title.page": "Browsing by {{ field }} {{ value }}",
11311135

11321136
"search.browse.item-back": "Back to Results",

0 commit comments

Comments
 (0)