mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
83783: Show owning and mapped collections on item page
This commit is contained in:
@@ -208,10 +208,20 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@link RemoteData} of {@link Collection} that is the owing collection of the given item
|
* Returns {@link RemoteData} of {@link Collection} that is the owning collection of the given item
|
||||||
* @param item Item we want the owning collection of
|
* @param item Item we want the owning collection of
|
||||||
*/
|
*/
|
||||||
findOwningCollectionFor(item: Item): Observable<RemoteData<Collection>> {
|
findOwningCollectionFor(item: Item): Observable<RemoteData<Collection>> {
|
||||||
return this.findByHref(item._links.owningCollection.href);
|
return this.findByHref(item._links.owningCollection.href);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of mapped collections for the given item.
|
||||||
|
* @param item Item for which the mapped collections should be retrieved.
|
||||||
|
* @param findListOptions Pagination and search options.
|
||||||
|
*/
|
||||||
|
findMappedCollectionsFor(item: Item, findListOptions?: FindListOptions): Observable<RemoteData<PaginatedList<Collection>>> {
|
||||||
|
return this.findAllByHref(item._links.mappedCollections.href, findListOptions);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,21 @@
|
|||||||
<ds-metadata-field-wrapper *ngIf="(this.collectionsRD$ | async)?.hasSucceeded" [label]="label | translate">
|
<ds-metadata-field-wrapper [label]="label | translate">
|
||||||
<div class="collections">
|
<div class="collections">
|
||||||
<a *ngFor="let collection of (this.collectionsRD$ | async)?.payload?.page; let last=last;" [routerLink]="['/collections', collection.id]">
|
<a *ngFor="let collection of (this.collections$ | async); let last=last;" [routerLink]="['/collections', collection.id]">
|
||||||
<span>{{collection?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span>
|
<span>{{collection?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="isLoading$ | async">
|
||||||
|
{{'item.page.collections.loading' | translate}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
*ngIf="!(isLoading$ | async) && (hasMore$ | async)"
|
||||||
|
(click)="$event.preventDefault(); handleLoadMore()"
|
||||||
|
class="load-more-btn btn btn-sm btn-outline-secondary"
|
||||||
|
role="button"
|
||||||
|
href="#"
|
||||||
|
>
|
||||||
|
{{'item.page.collections.load-more' | translate}}
|
||||||
|
</a>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
|
@@ -9,46 +9,45 @@ import { Item } from '../../../core/shared/item.model';
|
|||||||
import { getMockRemoteDataBuildService } from '../../../shared/mocks/remote-data-build.service.mock';
|
import { getMockRemoteDataBuildService } from '../../../shared/mocks/remote-data-build.service.mock';
|
||||||
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||||
import { CollectionsComponent } from './collections.component';
|
import { CollectionsComponent } from './collections.component';
|
||||||
|
import { FindListOptions } from '../../../core/data/request.models';
|
||||||
|
import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model';
|
||||||
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
|
|
||||||
let collectionsComponent: CollectionsComponent;
|
const createMockCollection = (id: string) => Object.assign(new Collection(), {
|
||||||
let fixture: ComponentFixture<CollectionsComponent>;
|
id: id,
|
||||||
|
name: `collection-${id}`,
|
||||||
let collectionDataServiceStub;
|
|
||||||
|
|
||||||
const mockCollection1: Collection = Object.assign(new Collection(), {
|
|
||||||
metadata: {
|
|
||||||
'dc.description.abstract': [
|
|
||||||
{
|
|
||||||
language: 'en_US',
|
|
||||||
value: 'Short description'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
_links: {
|
|
||||||
self: { href: 'collection-selflink' }
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const succeededMockItem: Item = Object.assign(new Item(), {owningCollection: createSuccessfulRemoteDataObject$(mockCollection1)});
|
const mockItem: Item = new Item();
|
||||||
const failedMockItem: Item = Object.assign(new Item(), {owningCollection: createFailedRemoteDataObject$('error', 500)});
|
|
||||||
|
|
||||||
describe('CollectionsComponent', () => {
|
describe('CollectionsComponent', () => {
|
||||||
collectionDataServiceStub = {
|
let collectionDataService;
|
||||||
findOwningCollectionFor(item: Item) {
|
|
||||||
if (item === succeededMockItem) {
|
let mockCollection1: Collection;
|
||||||
return createSuccessfulRemoteDataObject$(mockCollection1);
|
let mockCollection2: Collection;
|
||||||
} else {
|
let mockCollection3: Collection;
|
||||||
return createFailedRemoteDataObject$('error', 500);
|
let mockCollection4: Collection;
|
||||||
}
|
|
||||||
}
|
let component: CollectionsComponent;
|
||||||
};
|
let fixture: ComponentFixture<CollectionsComponent>;
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
|
collectionDataService = jasmine.createSpyObj([
|
||||||
|
'findOwningCollectionFor',
|
||||||
|
'findMappedCollectionsFor',
|
||||||
|
]);
|
||||||
|
|
||||||
|
mockCollection1 = createMockCollection('c1');
|
||||||
|
mockCollection2 = createMockCollection('c2');
|
||||||
|
mockCollection3 = createMockCollection('c3');
|
||||||
|
mockCollection4 = createMockCollection('c4');
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot()],
|
imports: [TranslateModule.forRoot()],
|
||||||
declarations: [ CollectionsComponent ],
|
declarations: [ CollectionsComponent ],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: RemoteDataBuildService, useValue: getMockRemoteDataBuildService()},
|
{ provide: RemoteDataBuildService, useValue: getMockRemoteDataBuildService()},
|
||||||
{ provide: CollectionDataService, useValue: collectionDataServiceStub },
|
{ provide: CollectionDataService, useValue: collectionDataService },
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [ NO_ERRORS_SCHEMA ]
|
schemas: [ NO_ERRORS_SCHEMA ]
|
||||||
@@ -59,33 +58,264 @@ describe('CollectionsComponent', () => {
|
|||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
fixture = TestBed.createComponent(CollectionsComponent);
|
fixture = TestBed.createComponent(CollectionsComponent);
|
||||||
collectionsComponent = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
collectionsComponent.label = 'test.test';
|
component.item = mockItem;
|
||||||
collectionsComponent.separator = '<br/>';
|
component.label = 'test.test';
|
||||||
|
component.separator = '<br/>';
|
||||||
|
component.pageSize = 2;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('When the requested item request has succeeded', () => {
|
describe('when the item has only an owning collection', () => {
|
||||||
|
let mockPage1: PaginatedList<Collection>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
collectionsComponent.item = succeededMockItem;
|
mockPage1 = buildPaginatedList(Object.assign(new PageInfo(), {
|
||||||
|
currentPage: 1,
|
||||||
|
elementsPerPage: 2,
|
||||||
|
totalPages: 0,
|
||||||
|
totalElements: 0,
|
||||||
|
}), []);
|
||||||
|
|
||||||
|
collectionDataService.findOwningCollectionFor.and.returnValue(createSuccessfulRemoteDataObject$(mockCollection1));
|
||||||
|
collectionDataService.findMappedCollectionsFor.and.returnValue(createSuccessfulRemoteDataObject$(mockPage1));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show the collection', () => {
|
it('should display the owning collection', () => {
|
||||||
const collectionField = fixture.debugElement.query(By.css('ds-metadata-field-wrapper div.collections'));
|
const collectionFields = fixture.debugElement.queryAll(By.css('ds-metadata-field-wrapper div.collections a'));
|
||||||
expect(collectionField).not.toBeNull();
|
const loadMoreBtn = fixture.debugElement.query(By.css('ds-metadata-field-wrapper .load-more-btn'));
|
||||||
|
|
||||||
|
expect(collectionDataService.findOwningCollectionFor).toHaveBeenCalledOnceWith(mockItem);
|
||||||
|
expect(collectionDataService.findMappedCollectionsFor).toHaveBeenCalledOnceWith(mockItem, Object.assign(new FindListOptions(), {
|
||||||
|
elementsPerPage: 2,
|
||||||
|
currentPage: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(collectionFields.length).toBe(1);
|
||||||
|
expect(collectionFields[0].nativeElement.textContent).toEqual('collection-c1');
|
||||||
|
|
||||||
|
expect(component.lastPage$.getValue()).toBe(1);
|
||||||
|
expect(component.hasMore$.getValue()).toBe(false);
|
||||||
|
expect(component.isLoading$.getValue()).toBe(false);
|
||||||
|
|
||||||
|
expect(loadMoreBtn).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When the requested item request has failed', () => {
|
describe('when the item has an owning collection and one mapped collection', () => {
|
||||||
|
let mockPage1: PaginatedList<Collection>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
collectionsComponent.item = failedMockItem;
|
mockPage1 = buildPaginatedList(Object.assign(new PageInfo(), {
|
||||||
|
currentPage: 1,
|
||||||
|
elementsPerPage: 2,
|
||||||
|
totalPages: 1,
|
||||||
|
totalElements: 1,
|
||||||
|
}), [mockCollection2]);
|
||||||
|
|
||||||
|
collectionDataService.findOwningCollectionFor.and.returnValue(createSuccessfulRemoteDataObject$(mockCollection1));
|
||||||
|
collectionDataService.findMappedCollectionsFor.and.returnValue(createSuccessfulRemoteDataObject$(mockPage1));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not show the collection', () => {
|
it('should display the owning collection and the mapped collection', () => {
|
||||||
const collectionField = fixture.debugElement.query(By.css('ds-metadata-field-wrapper div.collections'));
|
const collectionFields = fixture.debugElement.queryAll(By.css('ds-metadata-field-wrapper div.collections a'));
|
||||||
expect(collectionField).toBeNull();
|
const loadMoreBtn = fixture.debugElement.query(By.css('ds-metadata-field-wrapper .load-more-btn'));
|
||||||
|
|
||||||
|
expect(collectionDataService.findOwningCollectionFor).toHaveBeenCalledOnceWith(mockItem);
|
||||||
|
expect(collectionDataService.findMappedCollectionsFor).toHaveBeenCalledOnceWith(mockItem, Object.assign(new FindListOptions(), {
|
||||||
|
elementsPerPage: 2,
|
||||||
|
currentPage: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(collectionFields.length).toBe(2);
|
||||||
|
expect(collectionFields[0].nativeElement.textContent).toEqual('collection-c1');
|
||||||
|
expect(collectionFields[1].nativeElement.textContent).toEqual('collection-c2');
|
||||||
|
|
||||||
|
expect(component.lastPage$.getValue()).toBe(1);
|
||||||
|
expect(component.hasMore$.getValue()).toBe(false);
|
||||||
|
expect(component.isLoading$.getValue()).toBe(false);
|
||||||
|
|
||||||
|
expect(loadMoreBtn).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when the item has an owning collection and multiple mapped collections', () => {
|
||||||
|
let mockPage1: PaginatedList<Collection>;
|
||||||
|
let mockPage2: PaginatedList<Collection>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockPage1 = buildPaginatedList(Object.assign(new PageInfo(), {
|
||||||
|
currentPage: 1,
|
||||||
|
elementsPerPage: 2,
|
||||||
|
totalPages: 2,
|
||||||
|
totalElements: 3,
|
||||||
|
}), [mockCollection2, mockCollection3]);
|
||||||
|
|
||||||
|
mockPage2 = buildPaginatedList(Object.assign(new PageInfo(), {
|
||||||
|
currentPage: 2,
|
||||||
|
elementsPerPage: 2,
|
||||||
|
totalPages: 2,
|
||||||
|
totalElements: 1,
|
||||||
|
}), [mockCollection4]);
|
||||||
|
|
||||||
|
collectionDataService.findOwningCollectionFor.and.returnValue(createSuccessfulRemoteDataObject$(mockCollection1));
|
||||||
|
collectionDataService.findMappedCollectionsFor.and.returnValues(
|
||||||
|
createSuccessfulRemoteDataObject$(mockPage1),
|
||||||
|
createSuccessfulRemoteDataObject$(mockPage2),
|
||||||
|
);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the owning collection, two mapped collections and a load more button', () => {
|
||||||
|
const collectionFields = fixture.debugElement.queryAll(By.css('ds-metadata-field-wrapper div.collections a'));
|
||||||
|
const loadMoreBtn = fixture.debugElement.query(By.css('ds-metadata-field-wrapper .load-more-btn'));
|
||||||
|
|
||||||
|
expect(collectionDataService.findOwningCollectionFor).toHaveBeenCalledOnceWith(mockItem);
|
||||||
|
expect(collectionDataService.findMappedCollectionsFor).toHaveBeenCalledOnceWith(mockItem, Object.assign(new FindListOptions(), {
|
||||||
|
elementsPerPage: 2,
|
||||||
|
currentPage: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(collectionFields.length).toBe(3);
|
||||||
|
expect(collectionFields[0].nativeElement.textContent).toEqual('collection-c1');
|
||||||
|
expect(collectionFields[1].nativeElement.textContent).toEqual('collection-c2');
|
||||||
|
expect(collectionFields[2].nativeElement.textContent).toEqual('collection-c3');
|
||||||
|
|
||||||
|
expect(component.lastPage$.getValue()).toBe(1);
|
||||||
|
expect(component.hasMore$.getValue()).toBe(true);
|
||||||
|
expect(component.isLoading$.getValue()).toBe(false);
|
||||||
|
|
||||||
|
expect(loadMoreBtn).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the load more button is clicked', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const loadMoreBtn = fixture.debugElement.query(By.css('ds-metadata-field-wrapper .load-more-btn'));
|
||||||
|
loadMoreBtn.nativeElement.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the owning collection and three mapped collections', () => {
|
||||||
|
const collectionFields = fixture.debugElement.queryAll(By.css('ds-metadata-field-wrapper div.collections a'));
|
||||||
|
const loadMoreBtn = fixture.debugElement.query(By.css('ds-metadata-field-wrapper .load-more-btn'));
|
||||||
|
|
||||||
|
expect(collectionDataService.findOwningCollectionFor).toHaveBeenCalledOnceWith(mockItem);
|
||||||
|
expect(collectionDataService.findMappedCollectionsFor).toHaveBeenCalledTimes(2);
|
||||||
|
expect(collectionDataService.findMappedCollectionsFor).toHaveBeenCalledWith(mockItem, Object.assign(new FindListOptions(), {
|
||||||
|
elementsPerPage: 2,
|
||||||
|
currentPage: 1,
|
||||||
|
}));
|
||||||
|
expect(collectionDataService.findMappedCollectionsFor).toHaveBeenCalledWith(mockItem, Object.assign(new FindListOptions(), {
|
||||||
|
elementsPerPage: 2,
|
||||||
|
currentPage: 2,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(collectionFields.length).toBe(4);
|
||||||
|
expect(collectionFields[0].nativeElement.textContent).toEqual('collection-c1');
|
||||||
|
expect(collectionFields[1].nativeElement.textContent).toEqual('collection-c2');
|
||||||
|
expect(collectionFields[2].nativeElement.textContent).toEqual('collection-c3');
|
||||||
|
expect(collectionFields[3].nativeElement.textContent).toEqual('collection-c4');
|
||||||
|
|
||||||
|
expect(component.lastPage$.getValue()).toBe(2);
|
||||||
|
expect(component.hasMore$.getValue()).toBe(false);
|
||||||
|
expect(component.isLoading$.getValue()).toBe(false);
|
||||||
|
|
||||||
|
expect(loadMoreBtn).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the request for the owning collection fails', () => {
|
||||||
|
let mockPage1: PaginatedList<Collection>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockPage1 = buildPaginatedList(Object.assign(new PageInfo(), {
|
||||||
|
currentPage: 1,
|
||||||
|
elementsPerPage: 2,
|
||||||
|
totalPages: 1,
|
||||||
|
totalElements: 1,
|
||||||
|
}), [mockCollection2]);
|
||||||
|
|
||||||
|
collectionDataService.findOwningCollectionFor.and.returnValue(createFailedRemoteDataObject$());
|
||||||
|
collectionDataService.findMappedCollectionsFor.and.returnValue(createSuccessfulRemoteDataObject$(mockPage1));
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the mapped collection only', () => {
|
||||||
|
const collectionFields = fixture.debugElement.queryAll(By.css('ds-metadata-field-wrapper div.collections a'));
|
||||||
|
const loadMoreBtn = fixture.debugElement.query(By.css('ds-metadata-field-wrapper .load-more-btn'));
|
||||||
|
|
||||||
|
expect(collectionDataService.findOwningCollectionFor).toHaveBeenCalledOnceWith(mockItem);
|
||||||
|
expect(collectionDataService.findMappedCollectionsFor).toHaveBeenCalledOnceWith(mockItem, Object.assign(new FindListOptions(), {
|
||||||
|
elementsPerPage: 2,
|
||||||
|
currentPage: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(collectionFields.length).toBe(1);
|
||||||
|
expect(collectionFields[0].nativeElement.textContent).toEqual('collection-c2');
|
||||||
|
|
||||||
|
expect(component.lastPage$.getValue()).toBe(1);
|
||||||
|
expect(component.hasMore$.getValue()).toBe(false);
|
||||||
|
expect(component.isLoading$.getValue()).toBe(false);
|
||||||
|
|
||||||
|
expect(loadMoreBtn).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the request for the mapped collections fails', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
collectionDataService.findOwningCollectionFor.and.returnValue(createSuccessfulRemoteDataObject$(mockCollection1));
|
||||||
|
collectionDataService.findMappedCollectionsFor.and.returnValue(createFailedRemoteDataObject$());
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the owning collection only', () => {
|
||||||
|
const collectionFields = fixture.debugElement.queryAll(By.css('ds-metadata-field-wrapper div.collections a'));
|
||||||
|
const loadMoreBtn = fixture.debugElement.query(By.css('ds-metadata-field-wrapper .load-more-btn'));
|
||||||
|
|
||||||
|
expect(collectionDataService.findOwningCollectionFor).toHaveBeenCalledOnceWith(mockItem);
|
||||||
|
expect(collectionDataService.findMappedCollectionsFor).toHaveBeenCalledOnceWith(mockItem, Object.assign(new FindListOptions(), {
|
||||||
|
elementsPerPage: 2,
|
||||||
|
currentPage: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(collectionFields.length).toBe(1);
|
||||||
|
expect(collectionFields[0].nativeElement.textContent).toEqual('collection-c1');
|
||||||
|
|
||||||
|
expect(component.lastPage$.getValue()).toBe(0);
|
||||||
|
expect(component.hasMore$.getValue()).toBe(true);
|
||||||
|
expect(component.isLoading$.getValue()).toBe(false);
|
||||||
|
|
||||||
|
expect(loadMoreBtn).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when both requests fail', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
collectionDataService.findOwningCollectionFor.and.returnValue(createFailedRemoteDataObject$());
|
||||||
|
collectionDataService.findMappedCollectionsFor.and.returnValue(createFailedRemoteDataObject$());
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display no collections', () => {
|
||||||
|
const collectionFields = fixture.debugElement.queryAll(By.css('ds-metadata-field-wrapper div.collections a'));
|
||||||
|
const loadMoreBtn = fixture.debugElement.query(By.css('ds-metadata-field-wrapper .load-more-btn'));
|
||||||
|
|
||||||
|
expect(collectionDataService.findOwningCollectionFor).toHaveBeenCalledOnceWith(mockItem);
|
||||||
|
expect(collectionDataService.findMappedCollectionsFor).toHaveBeenCalledOnceWith(mockItem, Object.assign(new FindListOptions(), {
|
||||||
|
elementsPerPage: 2,
|
||||||
|
currentPage: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(collectionFields.length).toBe(0);
|
||||||
|
|
||||||
|
expect(component.lastPage$.getValue()).toBe(0);
|
||||||
|
expect(component.hasMore$.getValue()).toBe(true);
|
||||||
|
expect(component.isLoading$.getValue()).toBe(false);
|
||||||
|
|
||||||
|
expect(loadMoreBtn).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,14 +1,19 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import {map, scan, startWith, switchMap, tap, withLatestFrom} from 'rxjs/operators';
|
||||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
import { PaginatedList, buildPaginatedList } from '../../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
|
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
|
||||||
import { hasValue } from '../../../shared/empty.util';
|
import { hasValue } from '../../../shared/empty.util';
|
||||||
|
import { FindListOptions } from '../../../core/data/request.models';
|
||||||
|
import {
|
||||||
|
getAllCompletedRemoteData,
|
||||||
|
getAllSucceededRemoteDataPayload,
|
||||||
|
getFirstSucceededRemoteDataPayload,
|
||||||
|
getPaginatedListPayload,
|
||||||
|
} from '../../../core/shared/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the parent collections section of the item
|
* This component renders the parent collections section of the item
|
||||||
@@ -27,42 +32,92 @@ export class CollectionsComponent implements OnInit {
|
|||||||
|
|
||||||
separator = '<br/>';
|
separator = '<br/>';
|
||||||
|
|
||||||
collectionsRD$: Observable<RemoteData<PaginatedList<Collection>>>;
|
/**
|
||||||
|
* Amount of mapped collections that should be fetched at once.
|
||||||
|
*/
|
||||||
|
pageSize = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last page of the mapped collections that has been fetched.
|
||||||
|
*/
|
||||||
|
lastPage$: BehaviorSubject<number> = new BehaviorSubject<number>(0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push an event to this observable to fetch the next page of mapped collections.
|
||||||
|
* Because this observable is a behavior subject, the first page will be requested
|
||||||
|
* immediately after subscription.
|
||||||
|
*/
|
||||||
|
loadMore$: BehaviorSubject<void> = new BehaviorSubject(undefined);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not a page of mapped collections is currently being loaded.
|
||||||
|
*/
|
||||||
|
isLoading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not more pages of mapped collections are available.
|
||||||
|
*/
|
||||||
|
hasMore$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All collections that have been retrieved so far. This includes the owning collection,
|
||||||
|
* as well as any number of pages of mapped collections.
|
||||||
|
*/
|
||||||
|
collections$: Observable<Collection[]>;
|
||||||
|
|
||||||
constructor(private cds: CollectionDataService) {
|
constructor(private cds: CollectionDataService) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// this.collections = this.item.parents.payload;
|
const owningCollection$: Observable<Collection> = this.cds.findOwningCollectionFor(this.item).pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
startWith(null as Collection),
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: this should use parents, but the collections
|
const mappedCollections$: Observable<Collection[]> = this.loadMore$.pipe(
|
||||||
// for an Item aren't returned by the REST API yet,
|
// update isLoading$
|
||||||
// only the owning collection
|
tap(() => this.isLoading$.next(true)),
|
||||||
this.collectionsRD$ = this.cds.findOwningCollectionFor(this.item).pipe(
|
|
||||||
map((rd: RemoteData<Collection>) => {
|
// request next batch of mapped collections
|
||||||
if (hasValue(rd.payload)) {
|
withLatestFrom(this.lastPage$),
|
||||||
return new RemoteData(
|
switchMap(([_, lastPage]: [void, number]) => {
|
||||||
rd.timeCompleted,
|
return this.cds.findMappedCollectionsFor(this.item, Object.assign(new FindListOptions(), {
|
||||||
rd.msToLive,
|
elementsPerPage: this.pageSize,
|
||||||
rd.lastUpdated,
|
currentPage: lastPage + 1,
|
||||||
rd.state,
|
}));
|
||||||
rd.errorMessage,
|
}),
|
||||||
buildPaginatedList({
|
|
||||||
elementsPerPage: 10,
|
getAllCompletedRemoteData<PaginatedList<Collection>>(),
|
||||||
totalPages: 1,
|
|
||||||
currentPage: 1,
|
// update isLoading$
|
||||||
totalElements: 1,
|
tap(() => this.isLoading$.next(false)),
|
||||||
_links: {
|
|
||||||
self: rd.payload._links.self
|
getAllSucceededRemoteDataPayload(),
|
||||||
}
|
|
||||||
} as PageInfo, [rd.payload]),
|
// update hasMore$
|
||||||
rd.statusCode
|
tap((response: PaginatedList<Collection>) => this.hasMore$.next(response.currentPage < response.totalPages)),
|
||||||
);
|
|
||||||
} else {
|
// update lastPage$
|
||||||
return rd as any;
|
tap((response: PaginatedList<Collection>) => this.lastPage$.next(response.currentPage)),
|
||||||
}
|
|
||||||
})
|
getPaginatedListPayload<Collection>(),
|
||||||
|
|
||||||
|
// add current batch to list of collections
|
||||||
|
scan((prev: Collection[], current: Collection[]) => [...prev, ...current], []),
|
||||||
|
|
||||||
|
startWith([]),
|
||||||
|
) as Observable<Collection[]>;
|
||||||
|
|
||||||
|
this.collections$ = combineLatest([owningCollection$, mappedCollections$]).pipe(
|
||||||
|
map(([owningCollection, mappedCollections]: [Collection, Collection[]]) => {
|
||||||
|
return [owningCollection, ...mappedCollections].filter(collection => hasValue(collection));
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleLoadMore() {
|
||||||
|
this.loadMore$.next();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1874,6 +1874,10 @@
|
|||||||
|
|
||||||
"item.page.collections": "Collections",
|
"item.page.collections": "Collections",
|
||||||
|
|
||||||
|
"item.page.collections.loading": "Loading...",
|
||||||
|
|
||||||
|
"item.page.collections.load-more": "Load more",
|
||||||
|
|
||||||
"item.page.date": "Date",
|
"item.page.date": "Date",
|
||||||
|
|
||||||
"item.page.edit": "Edit this item",
|
"item.page.edit": "Edit this item",
|
||||||
|
Reference in New Issue
Block a user