Skip to content

Commit ce5bbf5

Browse files
[UXP-242] implement load all for tree view
1 parent 7292f14 commit ce5bbf5

7 files changed

Lines changed: 69 additions & 34 deletions

File tree

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/hierarchy/hierarchy.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ <h4 class="modal-title border-bottom mb-3" *ngIf="vocabularyHeader">{{'vocabular
77
[enabledSearch]="false"
88
[publicModeOnly]="true"
99
[isRelationComponent]="true"
10+
[loadAllNodes]="true"
1011
></ds-vocabulary-treeview>
1112
</div>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ <h4 class="modal-title">{{'vocabulary-treeview.header' | translate}}</h4>
1111
[preloadLevel]="preloadLevel"
1212
[selectedItems]="selectedItems"
1313
[multiSelect]="multiSelect"
14+
[loadAllNodes]="true"
1415
(select)="onSelect($event)">
1516
</ds-vocabulary-treeview>
1617
</div>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<h2 *ngIf="!(loading | async) && dataSource.data.length === 0" class="h4 text-center text-muted mt-4" >
2424
<span>{{'vocabulary-treeview.search.no-result' | translate}}</span>
2525
</h2>
26-
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
26+
<cdk-tree *ngIf="!(loading | async) && dataSource.data.length !== 0" [dataSource]="dataSource" [treeControl]="treeControl">
2727
<!-- Leaf node -->
2828
<cdk-tree-node *cdkTreeNodeDef="let node" cdkTreeNodePadding class="d-flex">
2929
<span aria-hidden="true" type="button" class="btn btn-default px-2 mr-1" cdkTreeNodeToggle>

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ describe('VocabularyTreeviewComponent test suite', () => {
146146
comp.selectedItems = [currentValue];
147147
fixture.detectChanges();
148148
expect(comp.dataSource.data).toEqual([]);
149-
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), ['entryID'], 'entryID', false, false);
149+
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, testPageInfo, ['entryID'], 'entryID', false, false, false);
150150
});
151151

152152
it('should should init component properly with init value as VocabularyEntry', () => {
@@ -158,7 +158,7 @@ describe('VocabularyTreeviewComponent test suite', () => {
158158
comp.selectedItems = [currentValue];
159159
fixture.detectChanges();
160160
expect(comp.dataSource.data).toEqual([]);
161-
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), ['entryID'], 'entryID', false, false);
161+
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, testPageInfo, ['entryID'], 'entryID', false, false, false);
162162
});
163163

164164
it('should should init component properly with init value as VocabularyEntryDetail', () => {
@@ -168,7 +168,7 @@ describe('VocabularyTreeviewComponent test suite', () => {
168168
comp.selectedItems = [currentValue];
169169
fixture.detectChanges();
170170
expect(comp.dataSource.data).toEqual([]);
171-
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), ['entryID'], 'entryID', false, false);
171+
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, testPageInfo, ['entryID'], 'entryID', false, false, false);
172172
});
173173

174174
it('should should init component properly with init value as VocabularyEntry and publicModeOnly enabled', () => {
@@ -181,7 +181,7 @@ describe('VocabularyTreeviewComponent test suite', () => {
181181
comp.selectedItems = [currentValue];
182182
fixture.detectChanges();
183183
expect(comp.dataSource.data).toEqual([]);
184-
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), ['entryID'], 'entryID', true, false);
184+
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, testPageInfo, ['entryID'], 'entryID', true, false, false);
185185
});
186186

187187
it('should call loadMore function', () => {
@@ -201,7 +201,7 @@ describe('VocabularyTreeviewComponent test suite', () => {
201201
const node = new TreeviewFlatNode(item);
202202
comp.loadChildren(node);
203203
fixture.detectChanges();
204-
expect(vocabularyTreeviewServiceStub.loadMore).toHaveBeenCalledWith(node.item, [], true);
204+
expect(vocabularyTreeviewServiceStub.loadMore).toHaveBeenCalledWith(node.item, [], true, false);
205205
});
206206

207207
it('should emit proper FormFieldMetadataValueObject when VocabularyEntryDetail has authority', () => {

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
7373
*/
7474
@Input() isRelationComponent = false;
7575

76+
/**
77+
* Whether to load all available nodes
78+
*/
79+
@Input() loadAllNodes = false;
80+
7681
/**
7782
* A map containing the current node showed by the tree
7883
*/
@@ -233,7 +238,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
233238
this.loading = this.vocabularyTreeviewService.isLoading();
234239

235240
const entryId: string = (this.selectedItems?.length > 0) ? this.getEntryId(this.selectedItems[0]) : null;
236-
this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), this.getSelectedEntryIds(), entryId, this.publicModeOnly, this.isRelationComponent);
241+
this.vocabularyTreeviewService.initialize(this.vocabularyOptions, this.initialPageInfo, this.getSelectedEntryIds(), entryId, this.publicModeOnly, this.isRelationComponent, this.loadAllNodes);
237242
}
238243

239244
/**
@@ -257,7 +262,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
257262
* @param node The TreeviewFlatNode for which to load children nodes
258263
*/
259264
loadChildren(node: TreeviewFlatNode) {
260-
this.vocabularyTreeviewService.loadMore(node.item, this.getSelectedEntryIds(), true);
265+
this.vocabularyTreeviewService.loadMore(node.item, this.getSelectedEntryIds(), true, this.loadAllNodes);
261266
}
262267

263268
/**
@@ -363,7 +368,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
363368
this.selectedItems = [];
364369
this.searchText = '';
365370
this.vocabularyTreeviewService.cleanTree();
366-
this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), this.getSelectedEntryIds(), null);
371+
this.vocabularyTreeviewService.initialize(this.vocabularyOptions, this.initialPageInfo, this.getSelectedEntryIds(), null, null, this.isRelationComponent);
367372
}
368373
}
369374
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ describe('VocabularyTreeviewService test suite', () => {
272272
scheduler.schedule(() => service.initialize(vocabularyOptions, pageInfo, [child.id], child.id, false, true));
273273
scheduler.flush();
274274

275-
expect(serviceAsAny.retrieveNodesTreeByTopParentEntry).toHaveBeenCalledWith(child.otherInformation.parent, pageInfo, [child.id]);
275+
expect(serviceAsAny.retrieveNodesTreeByTopParentEntry).toHaveBeenCalledWith(child.otherInformation.parent, pageInfo, [child.id], false);
276276
});
277277
});
278278

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

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { RemoteData } from 'src/app/core/data/remote-data';
22
import { Injectable } from '@angular/core';
33

4-
import { BehaviorSubject, combineLatest, Observable, of as observableOf } from 'rxjs';
5-
import { map, merge, mergeMap, scan } from 'rxjs/operators';
4+
import { BehaviorSubject, combineLatest, EMPTY, Observable, of as observableOf } from 'rxjs';
5+
import { expand, map, merge, mergeMap, scan } from 'rxjs/operators';
66
import findIndex from 'lodash/findIndex';
77

88
import { LOAD_MORE_NODE, LOAD_MORE_ROOT_NODE, TreeviewFlatNode, TreeviewNode } from './vocabulary-treeview-node.model';
@@ -104,8 +104,9 @@ export class VocabularyTreeviewService {
104104
* @param initValueId The entry id of the node to mark as selected, if any
105105
* @param publicModeOnly Whether the tree is used in a public view
106106
* @param isRelationComponent Whether the tree is used as relation component
107+
* @param loadAll
107108
*/
108-
initialize(options: VocabularyOptions, pageInfo: PageInfo, selectedItems: string[], initValueId?: string, publicModeOnly = false, isRelationComponent = false): void {
109+
initialize(options: VocabularyOptions, pageInfo: PageInfo, selectedItems: string[], initValueId?: string, publicModeOnly = false, isRelationComponent = false, loadAll = false): void {
109110
this.loading.next(true);
110111
this.vocabularyOptions = options;
111112
this.vocabularyName = options.name;
@@ -116,8 +117,8 @@ export class VocabularyTreeviewService {
116117
if (hasValue(hierarchy) && hierarchy.length > 0) {
117118
this.initValueHierarchy = hierarchy;
118119
isRelationComponent ?
119-
this.retrieveNodesTreeByTopParentEntry(hierarchy[0], pageInfo, selectedItems) :
120-
this.retrieveTopNodes(pageInfo, [], selectedItems, publicModeOnly ? hierarchy : null);
120+
this.retrieveNodesTreeByTopParentEntry(hierarchy[0], pageInfo, selectedItems, loadAll) :
121+
this.retrieveTopNodes(pageInfo, [], selectedItems, publicModeOnly ? hierarchy : null, loadAll);
121122
} else {
122123
this.loading.next(false);
123124
}
@@ -150,8 +151,9 @@ export class VocabularyTreeviewService {
150151
* @param item
151152
* @param selectedItems
152153
* @param onlyFirstTime
154+
* @param loadAll
153155
*/
154-
loadMore(item: VocabularyEntryDetail, selectedItems: string[], onlyFirstTime = false) {
156+
loadMore(item: VocabularyEntryDetail, selectedItems: string[], onlyFirstTime = false, loadAll = false) {
155157
if (!this.nodeMap.has(item.otherInformation.id)) {
156158
return;
157159
}
@@ -175,11 +177,18 @@ export class VocabularyTreeviewService {
175177
currentPage: list.pageInfo.currentPage + 1
176178
});
177179
parent.updatePageInfo(newPageInfo);
180+
parent.childrenChange.next(children);
181+
182+
if (loadAll) {
183+
// Need a new load more node
184+
this.loadMore(item, selectedItems);
185+
return;
186+
}
178187

179-
// Need a new load more node
180188
children.push(new TreeviewNode(LOAD_MORE_NODE, false, newPageInfo, item));
189+
} else {
190+
parent.childrenChange.next(children);
181191
}
182-
parent.childrenChange.next(children);
183192
this.dataChange.next(this.dataChange.value);
184193
});
185194

@@ -289,11 +298,23 @@ export class VocabularyTreeviewService {
289298
* Return the vocabulary entry's children
290299
* @param parentId The node id
291300
* @param pageInfo The {@link PageInfo} object
301+
* @param loadAll
302+
* @param selectedItem
292303
* @return Observable<PaginatedList<VocabularyEntryDetail>>
293304
*/
294-
private getChildrenNodesByParent(parentId: string, pageInfo: PageInfo): Observable<PaginatedList<VocabularyEntryDetail>> {
305+
private getChildrenNodesByParent(parentId: string, pageInfo: PageInfo, loadAll = false): Observable<PaginatedList<VocabularyEntryDetail>> {
295306
return this.vocabularyService.getEntryDetailChildren(parentId, this.vocabularyName, pageInfo).pipe(
296-
getFirstSucceededRemoteDataPayload()
307+
getFirstSucceededRemoteDataPayload(),
308+
).pipe(
309+
expand(res => {
310+
if (res.pageInfo.currentPage + 1 <= res.pageInfo.totalPages && loadAll) {
311+
const newPageInfo = Object.assign({}, res.pageInfo, {currentPage: res.pageInfo.currentPage + 1});
312+
return this.vocabularyService.getEntryDetailChildren(parentId, this.vocabularyName, newPageInfo).pipe(
313+
getFirstSucceededRemoteDataPayload(),
314+
);
315+
}
316+
return EMPTY;
317+
})
297318
);
298319
}
299320

@@ -331,8 +352,9 @@ export class VocabularyTreeviewService {
331352
* @param nodes The top level nodes already loaded, if any
332353
* @param selectedItems The currently selected items
333354
* @param hierarchyToLimit If given the top nodes included will be only the one related to the hierarchy
355+
* @param loadAll
334356
*/
335-
private retrieveTopNodes(pageInfo: PageInfo, nodes: TreeviewNode[], selectedItems: string[], hierarchyToLimit?: string[]): void {
357+
private retrieveTopNodes(pageInfo: PageInfo, nodes: TreeviewNode[], selectedItems: string[], hierarchyToLimit?: string[], loadAll = false): void {
336358
this.vocabularyService.searchTopEntries(this.vocabularyName, pageInfo).pipe(
337359
getFirstSucceededRemoteDataPayload()
338360
).subscribe((list: PaginatedList<VocabularyEntryDetail>) => {
@@ -344,15 +366,20 @@ export class VocabularyTreeviewService {
344366
nodes.push(...newNodes);
345367
}
346368

347-
348369
if ((list.pageInfo.currentPage + 1) <= list.pageInfo.totalPages) {
349-
// Need a new load more node
350370
const newPageInfo: PageInfo = Object.assign(new PageInfo(), list.pageInfo, {
351371
currentPage: list.pageInfo.currentPage + 1
352372
});
353-
const loadMoreNode = new TreeviewNode(LOAD_MORE_ROOT_NODE, false, newPageInfo);
354-
loadMoreNode.updatePageInfo(newPageInfo);
355-
nodes.push(loadMoreNode);
373+
374+
if (loadAll) {
375+
// loop over pages until we load all of them
376+
this.retrieveTopNodes(newPageInfo, nodes, selectedItems, hierarchyToLimit);
377+
return;
378+
} else {
379+
const loadMoreNode = new TreeviewNode(LOAD_MORE_ROOT_NODE, false, newPageInfo);
380+
loadMoreNode.updatePageInfo(newPageInfo);
381+
nodes.push(loadMoreNode);
382+
}
356383
}
357384
this.loading.next(false);
358385
// Notify the change.
@@ -365,23 +392,23 @@ export class VocabularyTreeviewService {
365392
* @param entry The root node to use to start building the tree
366393
* @param pageInfo The {@link PageInfo} object
367394
* @param selectedItems The currently selected items
395+
* @param loadAll
368396
*/
369-
private retrieveNodesTreeByTopParentEntry(entry: string, pageInfo: PageInfo, selectedItems: string[]): void {
370-
const nodes = [];
397+
private retrieveNodesTreeByTopParentEntry(entry: string, pageInfo: PageInfo, selectedItems: string[], loadAll = false): void {
371398
const rootNode$ = this.getById(entry);
372399
let tempList;
373400

374401
combineLatest([
375402
rootNode$,
376-
this.getChildrenNodesByParent(entry, pageInfo)
403+
this.getChildrenNodesByParent(entry, pageInfo, loadAll)
377404
]).pipe(
378405
mergeMap(([rootNode, list]) => {
379406
tempList = list;
380407
let newPageInfo: PageInfo;
381408

382409
const childNodes: TreeviewNode[] = list.page.map((entryDetail: VocabularyEntryDetail) => this._generateNode(entryDetail, selectedItems));
383410

384-
if ((tempList.pageInfo.currentPage + 1) <= tempList.pageInfo.totalPages) {
411+
if ((tempList.pageInfo.currentPage + 1) <= tempList.pageInfo.totalPages && !loadAll) {
385412
// Need a new load more node
386413
newPageInfo = Object.assign(new PageInfo(), tempList.pageInfo, {
387414
currentPage: tempList.pageInfo.currentPage + 1
@@ -398,11 +425,12 @@ export class VocabularyTreeviewService {
398425
);
399426
})
400427
).subscribe(hierarchy => {
401-
nodes.push(hierarchy);
402-
403-
this.loading.next(false);
428+
// Loading stops if we reach the end of the list or if we are done with the first loading and don't need to load all.
429+
if ((tempList.pageInfo.currentPage === tempList.pageInfo.totalPages && loadAll) || !loadAll) {
430+
this.loading.next(false);
431+
}
404432
// Notify the change.
405-
this.dataChange.next(nodes);
433+
this.dataChange.next([hierarchy]);
406434

407435
});
408436

0 commit comments

Comments
 (0)