pageSize}"
diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.spec.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.spec.ts
index 717c5873d2..807d9f8357 100644
--- a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.spec.ts
+++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.spec.ts
@@ -16,6 +16,11 @@ import { ResponsiveColumnSizes } from '../../../../../shared/responsive-table-si
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils';
import { createPaginatedList } from '../../../../../shared/testing/utils.test';
import { RequestService } from '../../../../../core/data/request.service';
+import { PaginationService } from '../../../../../core/pagination/pagination.service';
+import { PaginationComponentOptions } from '../../../../../shared/pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../../../../../core/cache/models/sort-options.model';
+import { FindListOptions } from '../../../../../core/data/request.models';
+import { PaginationServiceStub } from '../../../../../shared/testing/pagination-service.stub';
describe('PaginatedDragAndDropBitstreamListComponent', () => {
let comp: PaginatedDragAndDropBitstreamListComponent;
@@ -24,6 +29,7 @@ describe('PaginatedDragAndDropBitstreamListComponent', () => {
let bundleService: BundleDataService;
let objectValuesPipe: ObjectValuesPipe;
let requestService: RequestService;
+ let paginationService;
const columnSizes = new ResponsiveTableSizes([
new ResponsiveColumnSizes(2, 2, 3, 4, 4),
@@ -109,6 +115,8 @@ describe('PaginatedDragAndDropBitstreamListComponent', () => {
hasByHref$: observableOf(true)
});
+ paginationService = new PaginationServiceStub();
+
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
declarations: [PaginatedDragAndDropBitstreamListComponent, VarDirective],
@@ -116,7 +124,8 @@ describe('PaginatedDragAndDropBitstreamListComponent', () => {
{ provide: ObjectUpdatesService, useValue: objectUpdatesService },
{ provide: BundleDataService, useValue: bundleService },
{ provide: ObjectValuesPipe, useValue: objectValuesPipe },
- { provide: RequestService, useValue: requestService }
+ { provide: RequestService, useValue: requestService },
+ { provide: PaginationService, useValue: paginationService }
], schemas: [
NO_ERRORS_SCHEMA
]
diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts
index bb77bfc0c4..f3f00abf92 100644
--- a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts
+++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts
@@ -10,6 +10,8 @@ import { ResponsiveTableSizes } from '../../../../../shared/responsive-table-siz
import { followLink } from '../../../../../shared/utils/follow-link-config.model';
import { ObjectValuesPipe } from '../../../../../shared/utils/object-values-pipe';
import { RequestService } from '../../../../../core/data/request.service';
+import { PaginationService } from '../../../../../core/pagination/pagination.service';
+import { PaginationComponentOptions } from '../../../../../shared/pagination/pagination-component-options.model';
@Component({
selector: 'ds-paginated-drag-and-drop-bitstream-list',
@@ -37,8 +39,9 @@ export class PaginatedDragAndDropBitstreamListComponent extends AbstractPaginate
protected elRef: ElementRef,
protected objectValuesPipe: ObjectValuesPipe,
protected bundleService: BundleDataService,
+ protected paginationService: PaginationService,
protected requestService: RequestService) {
- super(objectUpdatesService, elRef, objectValuesPipe);
+ super(objectUpdatesService, elRef, objectValuesPipe, paginationService);
}
ngOnInit() {
@@ -50,8 +53,8 @@ export class PaginatedDragAndDropBitstreamListComponent extends AbstractPaginate
*/
initializeObjectsRD(): void {
this.objectsRD$ = this.currentPage$.pipe(
- switchMap((page: number) => {
- const paginatedOptions = new PaginatedSearchOptions({pagination: Object.assign({}, this.options, { currentPage: page })});
+ switchMap((page: PaginationComponentOptions) => {
+ const paginatedOptions = new PaginatedSearchOptions({pagination: Object.assign({}, page)});
return this.bundleService.getBitstreamsEndpoint(this.bundle.id, paginatedOptions).pipe(
switchMap((href) => this.requestService.hasByHref$(href)),
switchMap(() => this.bundleService.getBitstreams(
diff --git a/src/app/+item-page/full/field-components/file-section/full-file-section.component.html b/src/app/+item-page/full/field-components/file-section/full-file-section.component.html
index 00218b66d1..d593d60ce6 100644
--- a/src/app/+item-page/full/field-components/file-section/full-file-section.component.html
+++ b/src/app/+item-page/full/field-components/file-section/full-file-section.component.html
@@ -8,8 +8,7 @@
[paginationOptions]="originalOptions"
[pageInfoState]="originals"
[collectionSize]="originals?.totalElements"
- [disableRouteParameterUpdate]="true"
- (pageChange)="switchOriginalPage($event)">
+ [retainScrollPosition]="true">
@@ -51,8 +50,7 @@
[paginationOptions]="licenseOptions"
[pageInfoState]="licenses"
[collectionSize]="licenses?.totalElements"
- [disableRouteParameterUpdate]="true"
- (pageChange)="switchLicensePage($event)">
+ [retainScrollPosition]="true">
diff --git a/src/app/+item-page/full/field-components/file-section/full-file-section.component.spec.ts b/src/app/+item-page/full/field-components/file-section/full-file-section.component.spec.ts
index 1773a0fe74..9b225632df 100644
--- a/src/app/+item-page/full/field-components/file-section/full-file-section.component.spec.ts
+++ b/src/app/+item-page/full/field-components/file-section/full-file-section.component.spec.ts
@@ -16,6 +16,11 @@ import { MockBitstreamFormat1 } from '../../../../shared/mocks/item.mock';
import { By } from '@angular/platform-browser';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
+import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../../../../core/cache/models/sort-options.model';
+import { FindListOptions } from '../../../../core/data/request.models';
+import { PaginationService } from '../../../../core/pagination/pagination.service';
+import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub';
describe('FullFileSectionComponent', () => {
let comp: FullFileSectionComponent;
@@ -52,6 +57,8 @@ describe('FullFileSectionComponent', () => {
findAllByItemAndBundleName: createSuccessfulRemoteDataObject$(createPaginatedList([mockBitstream, mockBitstream, mockBitstream]))
});
+ const paginationService = new PaginationServiceStub();
+
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
@@ -64,7 +71,8 @@ describe('FullFileSectionComponent', () => {
declarations: [FullFileSectionComponent, VarDirective, FileSizePipe, MetadataFieldWrapperComponent],
providers: [
{ provide: BitstreamDataService, useValue: bitstreamDataService },
- { provide: NotificationsService, useValue: new NotificationsServiceStub() }
+ { provide: NotificationsService, useValue: new NotificationsServiceStub() },
+ { provide: PaginationService, useValue: paginationService }
],
schemas: [NO_ERRORS_SCHEMA]
@@ -82,39 +90,5 @@ describe('FullFileSectionComponent', () => {
const fileSection = fixture.debugElement.queryAll(By.css('.file-section'));
expect(fileSection.length).toEqual(6);
});
-
- describe('when we press the pageChange button for original bundle', () => {
- beforeEach(() => {
- comp.switchOriginalPage(2);
- fixture.detectChanges();
- });
-
- it('should give the value to the currentpage', () => {
- expect(comp.originalOptions.currentPage).toBe(2);
- });
- it('should call the next function on the originalCurrentPage', (done) => {
- comp.originalCurrentPage$.subscribe((event) => {
- expect(event).toEqual(2);
- done();
- });
- });
- });
-
- describe('when we press the pageChange button for license bundle', () => {
- beforeEach(() => {
- comp.switchLicensePage(2);
- fixture.detectChanges();
- });
-
- it('should give the value to the currentpage', () => {
- expect(comp.licenseOptions.currentPage).toBe(2);
- });
- it('should call the next function on the licenseCurrentPage', (done) => {
- comp.licenseCurrentPage$.subscribe((event) => {
- expect(event).toEqual(2);
- done();
- });
- });
- });
});
});
diff --git a/src/app/+item-page/full/field-components/file-section/full-file-section.component.ts b/src/app/+item-page/full/field-components/file-section/full-file-section.component.ts
index ca3d5e65c7..214484120e 100644
--- a/src/app/+item-page/full/field-components/file-section/full-file-section.component.ts
+++ b/src/app/+item-page/full/field-components/file-section/full-file-section.component.ts
@@ -1,5 +1,5 @@
import { Component, Input, OnInit } from '@angular/core';
-import { BehaviorSubject, Observable } from 'rxjs';
+import { Observable } from 'rxjs';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { Bitstream } from '../../../../core/shared/bitstream.model';
@@ -13,6 +13,7 @@ import { switchMap, tap } from 'rxjs/operators';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import { hasValue, isEmpty } from '../../../../shared/empty.util';
+import { PaginationService } from '../../../../core/pagination/pagination.service';
/**
* This component renders the file section of the item
@@ -35,23 +36,22 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
pageSize = 5;
originalOptions = Object.assign(new PaginationComponentOptions(), {
- id: 'original-bitstreams-options',
+ id: 'obo',
currentPage: 1,
pageSize: this.pageSize
});
- originalCurrentPage$ = new BehaviorSubject
(1);
licenseOptions = Object.assign(new PaginationComponentOptions(), {
- id: 'license-bitstreams-options',
+ id: 'lbo',
currentPage: 1,
pageSize: this.pageSize
});
- licenseCurrentPage$ = new BehaviorSubject(1);
constructor(
bitstreamDataService: BitstreamDataService,
protected notificationsService: NotificationsService,
- protected translateService: TranslateService
+ protected translateService: TranslateService,
+ protected paginationService: PaginationService
) {
super(bitstreamDataService, notificationsService, translateService);
}
@@ -61,11 +61,11 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
}
initialize(): void {
- this.originals$ = this.originalCurrentPage$.pipe(
- switchMap((pageNumber: number) => this.bitstreamDataService.findAllByItemAndBundleName(
+ this.originals$ = this.paginationService.getCurrentPagination(this.originalOptions.id, this.originalOptions).pipe(
+ switchMap((options: PaginationComponentOptions) => this.bitstreamDataService.findAllByItemAndBundleName(
this.item,
'ORIGINAL',
- {elementsPerPage: this.pageSize, currentPage: pageNumber},
+ {elementsPerPage: options.pageSize, currentPage: options.currentPage},
true,
true,
followLink('format')
@@ -78,11 +78,11 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
)
);
- this.licenses$ = this.licenseCurrentPage$.pipe(
- switchMap((pageNumber: number) => this.bitstreamDataService.findAllByItemAndBundleName(
+ this.licenses$ = this.paginationService.getCurrentPagination(this.licenseOptions.id, this.licenseOptions).pipe(
+ switchMap((options: PaginationComponentOptions) => this.bitstreamDataService.findAllByItemAndBundleName(
this.item,
'LICENSE',
- {elementsPerPage: this.pageSize, currentPage: pageNumber},
+ {elementsPerPage: options.pageSize, currentPage: options.currentPage},
true,
true,
followLink('format')
@@ -97,25 +97,13 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
}
- /**
- * Update the current page for the original bundle bitstreams
- * @param page
- */
- switchOriginalPage(page: number) {
- this.originalOptions.currentPage = page;
- this.originalCurrentPage$.next(page);
- }
-
- /**
- * Update the current page for the license bundle bitstreams
- * @param page
- */
- switchLicensePage(page: number) {
- this.licenseOptions.currentPage = page;
- this.licenseCurrentPage$.next(page);
- }
-
hasValuesInBundle(bundle: PaginatedList) {
return hasValue(bundle) && !isEmpty(bundle.page);
}
+
+ ngOnDestroy(): void {
+ this.paginationService.clearPagination(this.originalOptions.id);
+ this.paginationService.clearPagination(this.licenseOptions.id);
+ }
+
}
diff --git a/src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts b/src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts
index 4154a09f15..87a2f8a9dd 100644
--- a/src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts
+++ b/src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts
@@ -9,6 +9,8 @@ import { ActivatedRouteStub } from '../shared/testing/active-router.stub';
import { RoleServiceMock } from '../shared/mocks/role-service.mock';
import { cold, hot } from 'jasmine-marbles';
import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type';
+import { PaginationServiceStub } from '../shared/testing/pagination-service.stub';
+import { PaginationService } from '../core/pagination/pagination.service';
describe('MyDSpaceConfigurationService', () => {
let service: MyDSpaceConfigurationService;
@@ -34,12 +36,13 @@ describe('MyDSpaceConfigurationService', () => {
getRouteDataValue: observableOf({})
});
+ const paginationService = new PaginationServiceStub();
const activatedRoute: any = new ActivatedRouteStub();
const roleService: any = new RoleServiceMock();
beforeEach(() => {
- service = new MyDSpaceConfigurationService(roleService, spy, activatedRoute);
+ service = new MyDSpaceConfigurationService(roleService, spy, paginationService as any, activatedRoute);
});
describe('when the scope is called', () => {
@@ -102,25 +105,19 @@ describe('MyDSpaceConfigurationService', () => {
describe('when getCurrentSort is called', () => {
beforeEach(() => {
- service.getCurrentSort({} as any);
+ service.getCurrentSort('page-id', defaults.sort);
});
- it('should call getQueryParameterValue on the routeService with parameter name \'sortDirection\'', () => {
- expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('sortDirection');
- });
- it('should call getQueryParameterValue on the routeService with parameter name \'sortField\'', () => {
- expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('sortField');
+ it('should call getCurrentSort on the paginationService with the provided id and sort options', () => {
+ expect((service as any).paginationService.getCurrentSort).toHaveBeenCalledWith('page-id', defaults.sort);
});
});
describe('when getCurrentPagination is called', () => {
beforeEach(() => {
- service.getCurrentPagination({ currentPage: 1, pageSize: 10 } as any);
+ service.getCurrentPagination('page-id', defaults.pagination);
});
- it('should call getQueryParameterValue on the routeService with parameter name \'page\'', () => {
- expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('page');
- });
- it('should call getQueryParameterValue on the routeService with parameter name \'pageSize\'', () => {
- expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('pageSize');
+ it('should call getCurrentPagination on the paginationService with the provided id and sort options', () => {
+ expect((service as any).paginationService.getCurrentPagination).toHaveBeenCalledWith('page-id', defaults.pagination);
});
});
@@ -152,7 +149,7 @@ describe('MyDSpaceConfigurationService', () => {
describe('when subscribeToPaginatedSearchOptions is called', () => {
beforeEach(() => {
- (service as any).subscribeToPaginatedSearchOptions(defaults);
+ (service as any).subscribeToPaginatedSearchOptions('id', defaults);
});
it('should call all getters it needs', () => {
expect(service.getCurrentPagination).toHaveBeenCalled();
diff --git a/src/app/+my-dspace-page/my-dspace-configuration.service.ts b/src/app/+my-dspace-page/my-dspace-configuration.service.ts
index 58946c9c16..82f76eb776 100644
--- a/src/app/+my-dspace-page/my-dspace-configuration.service.ts
+++ b/src/app/+my-dspace-page/my-dspace-configuration.service.ts
@@ -11,6 +11,7 @@ import { SearchConfigurationService } from '../core/shared/search/search-configu
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
import { RouteService } from '../core/services/route.service';
+import { PaginationService } from '../core/pagination/pagination.service';
/**
* Service that performs all actions that have to do with the current mydspace configuration
@@ -55,13 +56,15 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService {
*
* @param {roleService} roleService
* @param {RouteService} routeService
+ * @param {PaginationService} paginationService
* @param {ActivatedRoute} route
*/
constructor(protected roleService: RoleService,
protected routeService: RouteService,
+ protected paginationService: PaginationService,
protected route: ActivatedRoute) {
- super(routeService, route);
+ super(routeService, paginationService, route);
// override parent class initialization
this._defaults = null;
diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.html b/src/app/access-control/epeople-registry/epeople-registry.component.html
index 1a2e8e1e13..7ef02a76cf 100644
--- a/src/app/access-control/epeople-registry/epeople-registry.component.html
+++ b/src/app/access-control/epeople-registry/epeople-registry.component.html
@@ -52,8 +52,7 @@
[pageInfoState]="pageInfoState$"
[collectionSize]="(pageInfoState$ | async)?.totalElements"
[hideGear]="true"
- [hidePagerWhenSinglePage]="true"
- (pageChange)="onPageChange($event)">
+ [hidePagerWhenSinglePage]="true">
diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts b/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts
index 2fb955bb02..bcf7e8f1d9 100644
--- a/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts
+++ b/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts
@@ -25,6 +25,8 @@ import { NotificationsServiceStub } from '../../shared/testing/notifications-ser
import { RouterStub } from '../../shared/testing/router.stub';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { RequestService } from '../../core/data/request.service';
+import { PaginationService } from '../../core/pagination/pagination.service';
+import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
describe('EPeopleRegistryComponent', () => {
let component: EPeopleRegistryComponent;
@@ -37,6 +39,8 @@ describe('EPeopleRegistryComponent', () => {
let authorizationService: AuthorizationDataService;
let modalService;
+ let paginationService;
+
beforeEach(waitForAsync(() => {
mockEPeople = [EPersonMock, EPersonMock2];
ePersonDataServiceStub = {
@@ -115,6 +119,8 @@ describe('EPeopleRegistryComponent', () => {
});
builderService = getMockFormBuilderService();
translateService = getMockTranslateService();
+
+ paginationService = new PaginationServiceStub();
TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
TranslateModule.forRoot({
@@ -131,7 +137,8 @@ describe('EPeopleRegistryComponent', () => {
{ provide: AuthorizationDataService, useValue: authorizationService },
{ provide: FormBuilderService, useValue: builderService },
{ provide: Router, useValue: new RouterStub() },
- { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) }
+ { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) },
+ { provide: PaginationService, useValue: paginationService }
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.ts b/src/app/access-control/epeople-registry/epeople-registry.component.ts
index f76c56853c..b99304d037 100644
--- a/src/app/access-control/epeople-registry/epeople-registry.component.ts
+++ b/src/app/access-control/epeople-registry/epeople-registry.component.ts
@@ -4,7 +4,7 @@ import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';
-import { PaginatedList, buildPaginatedList } from '../../core/data/paginated-list.model';
+import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model';
import { RemoteData } from '../../core/data/remote-data';
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { EPerson } from '../../core/eperson/models/eperson.model';
@@ -14,15 +14,13 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c
import { EpersonDtoModel } from '../../core/eperson/models/eperson-dto.model';
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
-import {
- getFirstCompletedRemoteData,
- getAllSucceededRemoteData
-} from '../../core/shared/operators';
+import { getAllSucceededRemoteData, getFirstCompletedRemoteData } from '../../core/shared/operators';
import { ConfirmationModalComponent } from '../../shared/confirmation-modal/confirmation-modal.component';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { RequestService } from '../../core/data/request.service';
import { PageInfo } from '../../core/shared/page-info.model';
import { NoContent } from '../../core/shared/NoContent.model';
+import { PaginationService } from '../../core/pagination/pagination.service';
@Component({
selector: 'ds-epeople-registry',
@@ -60,7 +58,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
* Pagination config used to display the list of epeople
*/
config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
- id: 'epeople-list-pagination',
+ id: 'elp',
pageSize: 5,
currentPage: 1
});
@@ -78,9 +76,9 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
currentSearchScope: string;
/**
- * The subscription for the search method
+ * FindListOptions
*/
- searchSub: Subscription;
+ findListOptionsSub: Subscription;
/**
* List of subscriptions
@@ -94,6 +92,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
private formBuilder: FormBuilder,
private router: Router,
private modalService: NgbModal,
+ private paginationService: PaginationService,
public requestService: RequestService) {
this.currentSearchQuery = '';
this.currentSearchScope = 'metadata';
@@ -113,7 +112,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
initialisePage() {
this.searching$.next(true);
this.isEPersonFormShown = false;
- this.search({ scope: this.currentSearchScope, query: this.currentSearchQuery });
+ this.search({scope: this.currentSearchScope, query: this.currentSearchQuery});
this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => {
if (eperson != null && eperson.id) {
this.isEPersonFormShown = true;
@@ -139,56 +138,51 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
return [epeople];
}
})).subscribe((value: PaginatedList) => {
- this.searching$.next(false);
- this.ePeopleDto$.next(value);
- this.pageInfoState$.next(value.pageInfo);
+ this.searching$.next(false);this.ePeopleDto$.next(value);
+ this.pageInfoState$.next(value.pageInfo);
}));
}
- /**
- * Event triggered when the user changes page
- * @param event
- */
- onPageChange(event) {
- if (this.config.currentPage !== event) {
- this.config.currentPage = event;
- this.search({ scope: this.currentSearchScope, query: this.currentSearchQuery });
- }
- }
-
/**
* Search in the EPeople by metadata (default) or email
* @param data Contains scope and query param
*/
search(data: any) {
this.searching$.next(true);
- const query: string = data.query;
- const scope: string = data.scope;
- if (query != null && this.currentSearchQuery !== query) {
- this.router.navigateByUrl(this.epersonService.getEPeoplePageRouterLink());
- this.currentSearchQuery = query;
- this.config.currentPage = 1;
+ if (hasValue(this.findListOptionsSub)) {
+ this.findListOptionsSub.unsubscribe();
}
- if (scope != null && this.currentSearchScope !== scope) {
- this.router.navigateByUrl(this.epersonService.getEPeoplePageRouterLink());
- this.currentSearchScope = scope;
- this.config.currentPage = 1;
- }
- if (hasValue(this.searchSub)) {
- this.searchSub.unsubscribe();
- this.subs = this.subs.filter((sub: Subscription) => sub !== this.searchSub);
- }
- this.searchSub = this.epersonService.searchByScope(this.currentSearchScope, this.currentSearchQuery, {
- currentPage: this.config.currentPage,
- elementsPerPage: this.config.pageSize
- }).pipe(
+ this.findListOptionsSub = this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
+ switchMap((findListOptions) => {
+ const query: string = data.query;
+ const scope: string = data.scope;
+ if (query != null && this.currentSearchQuery !== query) {
+ this.router.navigate([this.epersonService.getEPeoplePageRouterLink()], {
+ queryParamsHandling: 'merge'
+ });
+ this.currentSearchQuery = query;
+ this.paginationService.resetPage(this.config.id);
+ }
+ if (scope != null && this.currentSearchScope !== scope) {
+ this.router.navigate([this.epersonService.getEPeoplePageRouterLink()], {
+ queryParamsHandling: 'merge'
+ });
+ this.currentSearchScope = scope;
+ this.paginationService.resetPage(this.config.id);
+
+ }
+ return this.epersonService.searchByScope(this.currentSearchScope, this.currentSearchQuery, {
+ currentPage: findListOptions.currentPage,
+ elementsPerPage: findListOptions.pageSize
+ });
+ }
+ ),
getAllSucceededRemoteData(),
).subscribe((peopleRD) => {
this.ePeople$.next(peopleRD.payload);
this.pageInfoState$.next(peopleRD.payload.pageInfo);
}
);
- this.subs.push(this.searchSub);
}
/**
@@ -243,7 +237,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
if (hasValue(ePerson.id)) {
this.epersonService.deleteEPerson(ePerson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData) => {
if (restResponse.hasSucceeded) {
- this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { name: ePerson.name }));
+ this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', {name: ePerson.name}));
this.reset();
} else {
this.notificationsService.error('Error occured when trying to delete EPerson with id: ' + ePerson.id + ' with code: ' + restResponse.statusCode + ' and message: ' + restResponse.errorMessage);
@@ -260,8 +254,10 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
*/
ngOnDestroy(): void {
this.cleanupSubscribes();
+ this.paginationService.clearPagination(this.config.id);
}
+
cleanupSubscribes() {
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
}
@@ -283,7 +279,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
this.searchForm.patchValue({
query: '',
});
- this.search({ query: '' });
+ this.search({query: ''});
}
/**
diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts
index 454dad0018..832f4f6ce5 100644
--- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts
+++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts
@@ -26,6 +26,8 @@ import { AuthorizationDataService } from '../../../core/data/feature-authorizati
import { GroupDataService } from '../../../core/eperson/group-data.service';
import { createPaginatedList } from '../../../shared/testing/utils.test';
import { RequestService } from '../../../core/data/request.service';
+import { PaginationService } from '../../../core/pagination/pagination.service';
+import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
describe('EPersonFormComponent', () => {
let component: EPersonFormComponent;
@@ -38,6 +40,10 @@ describe('EPersonFormComponent', () => {
let authorizationService: AuthorizationDataService;
let groupsDataService: GroupDataService;
+ let paginationService;
+
+
+
beforeEach(waitForAsync(() => {
mockEPeople = [EPersonMock, EPersonMock2];
ePersonDataServiceStub = {
@@ -104,6 +110,8 @@ describe('EPersonFormComponent', () => {
findAllByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])),
getGroupRegistryRouterLink: ''
});
+
+ paginationService = new PaginationServiceStub();
TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
TranslateModule.forRoot({
@@ -121,6 +129,7 @@ describe('EPersonFormComponent', () => {
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: AuthService, useValue: authService },
{ provide: AuthorizationDataService, useValue: authorizationService },
+ { provide: PaginationService, useValue: paginationService },
{ provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) }
],
schemas: [NO_ERRORS_SCHEMA]
diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts
index 285cbbefa6..11c117ef55 100644
--- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts
+++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts
@@ -7,7 +7,7 @@ import {
DynamicInputModel
} from '@ng-dynamic-forms/core';
import { TranslateService } from '@ngx-translate/core';
-import { combineLatest, Observable, of, Subscription } from 'rxjs';
+import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
import { switchMap, take } from 'rxjs/operators';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { RemoteData } from '../../../core/data/remote-data';
@@ -16,9 +16,9 @@ import { GroupDataService } from '../../../core/eperson/group-data.service';
import { EPerson } from '../../../core/eperson/models/eperson.model';
import { Group } from '../../../core/eperson/models/group.model';
import {
- getRemoteDataPayload,
+ getFirstCompletedRemoteData,
getFirstSucceededRemoteData,
- getFirstCompletedRemoteData
+ getRemoteDataPayload
} from '../../../core/shared/operators';
import { hasValue } from '../../../shared/empty.util';
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
@@ -31,6 +31,7 @@ import { ConfirmationModalComponent } from '../../../shared/confirmation-modal/c
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { RequestService } from '../../../core/data/request.service';
import { NoContent } from '../../../core/shared/NoContent.model';
+import { PaginationService } from '../../../core/pagination/pagination.service';
@Component({
selector: 'ds-eperson-form',
@@ -118,7 +119,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
* Observable whether or not the admin is allowed to reset the EPerson's password
* TODO: Initialize the observable once the REST API supports this (currently hardcoded to return false)
*/
- canReset$: Observable = of(false);
+ canReset$: Observable = observableOf(false);
/**
* Observable whether or not the admin is allowed to delete the EPerson
@@ -144,7 +145,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
* Pagination config used to display the list of groups
*/
config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
- id: 'groups-ePersonMemberOf-list-pagination',
+ id: 'gem',
pageSize: 5,
currentPage: 1
});
@@ -167,6 +168,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
private authService: AuthService,
private authorizationService: AuthorizationDataService,
private modalService: NgbModal,
+ private paginationService: PaginationService,
public requestService: RequestService) {
this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => {
this.epersonInitial = eperson;
@@ -184,13 +186,13 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
* This method will initialise the page
*/
initialisePage() {
- combineLatest(
- this.translateService.get(`${this.messagePrefix}.firstName`),
- this.translateService.get(`${this.messagePrefix}.lastName`),
- this.translateService.get(`${this.messagePrefix}.email`),
- this.translateService.get(`${this.messagePrefix}.canLogIn`),
- this.translateService.get(`${this.messagePrefix}.requireCertificate`),
- this.translateService.get(`${this.messagePrefix}.emailHint`),
+ observableCombineLatest(
+ this.translateService.get(`${this.messagePrefix}.firstName`),
+ this.translateService.get(`${this.messagePrefix}.lastName`),
+ this.translateService.get(`${this.messagePrefix}.email`),
+ this.translateService.get(`${this.messagePrefix}.canLogIn`),
+ this.translateService.get(`${this.messagePrefix}.requireCertificate`),
+ this.translateService.get(`${this.messagePrefix}.emailHint`),
).subscribe(([firstName, lastName, email, canLogIn, requireCertificate, emailHint]) => {
this.firstName = new DynamicInputModel({
id: 'firstName',
@@ -222,19 +224,19 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
hint: emailHint
});
this.canLogIn = new DynamicCheckboxModel(
- {
- id: 'canLogIn',
- label: canLogIn,
- name: 'canLogIn',
- value: (this.epersonInitial != null ? this.epersonInitial.canLogIn : true)
- });
+ {
+ id: 'canLogIn',
+ label: canLogIn,
+ name: 'canLogIn',
+ value: (this.epersonInitial != null ? this.epersonInitial.canLogIn : true)
+ });
this.requireCertificate = new DynamicCheckboxModel(
- {
- id: 'requireCertificate',
- label: requireCertificate,
- name: 'requireCertificate',
- value: (this.epersonInitial != null ? this.epersonInitial.requireCertificate : false)
- });
+ {
+ id: 'requireCertificate',
+ label: requireCertificate,
+ name: 'requireCertificate',
+ value: (this.epersonInitial != null ? this.epersonInitial.requireCertificate : false)
+ });
this.formModel = [
this.firstName,
this.lastName,
@@ -258,11 +260,29 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
requireCertificate: eperson != null ? eperson.requireCertificate : false
});
}));
- this.canImpersonate$ = this.epersonService.getActiveEPerson().pipe(
- switchMap((eperson) => this.authorizationService.isAuthorized(FeatureID.LoginOnBehalfOf, hasValue(eperson) ? eperson.self : undefined))
+
+ const activeEPerson$ = this.epersonService.getActiveEPerson();
+
+ this.groups = activeEPerson$.pipe(
+ switchMap((eperson) => {
+ return observableCombineLatest([observableOf(eperson), this.paginationService.getFindListOptions(this.config.id, {
+ currentPage: 1,
+ elementsPerPage: this.config.pageSize
+ })]);
+ }),
+ switchMap(([eperson, findListOptions]) => {
+ if (eperson != null) {
+ return this.groupsDataService.findAllByHref(eperson._links.groups.href, findListOptions);
+ }
+ return observableOf(undefined);
+ })
);
- this.canDelete$ = this.epersonService.getActiveEPerson().pipe(
- switchMap((eperson) => this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined))
+
+ this.canImpersonate$ = activeEPerson$.pipe(
+ switchMap((eperson) => this.authorizationService.isAuthorized(FeatureID.LoginOnBehalfOf, hasValue(eperson) ? eperson.self : undefined))
+ );
+ this.canDelete$ = activeEPerson$.pipe(
+ switchMap((eperson) => this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined))
);
});
}
@@ -322,10 +342,10 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
getFirstCompletedRemoteData()
).subscribe((rd: RemoteData) => {
if (rd.hasSucceeded) {
- this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', { name: ePersonToCreate.name }));
+ this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', {name: ePersonToCreate.name}));
this.submitForm.emit(ePersonToCreate);
} else {
- this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure', { name: ePersonToCreate.name }));
+ this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure', {name: ePersonToCreate.name}));
this.cancelForm.emit();
}
});
@@ -361,10 +381,10 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
const response = this.epersonService.updateEPerson(editedEperson);
response.pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData) => {
if (rd.hasSucceeded) {
- this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.edited.success', { name: editedEperson.name }));
+ this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.edited.success', {name: editedEperson.name}));
this.submitForm.emit(editedEperson);
} else {
- this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.edited.failure', { name: editedEperson.name }));
+ this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.edited.failure', {name: editedEperson.name}));
this.cancelForm.emit();
}
});
@@ -469,8 +489,10 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
ngOnDestroy(): void {
this.onCancel();
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
+ this.paginationService.clearPagination(this.config.id);
}
+
/**
* This method will ensure that the page gets reset and that the cache is cleared
*/
diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.html b/src/app/access-control/group-registry/group-form/members-list/members-list.component.html
index f54936148c..51282b49c0 100644
--- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.html
+++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.html
@@ -32,8 +32,7 @@
[pageInfoState]="(ePeopleSearchDtos | async)"
[collectionSize]="(ePeopleSearchDtos | async)?.totalElements"
[hideGear]="true"
- [hidePagerWhenSinglePage]="true"
- (pageChange)="onPageChangeSearch($event)">
+ [hidePagerWhenSinglePage]="true">
@@ -86,8 +85,7 @@
[pageInfoState]="(ePeopleMembersOfGroupDtos | async)"
[collectionSize]="(ePeopleMembersOfGroupDtos | async)?.totalElements"
[hideGear]="true"
- [hidePagerWhenSinglePage]="true"
- (pageChange)="onPageChange($event)">
+ [hidePagerWhenSinglePage]="true">
diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts
index 20419fb49d..0b19b17100 100644
--- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts
+++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts
@@ -26,6 +26,8 @@ import { getMockFormBuilderService } from '../../../../shared/mocks/form-builder
import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
import { RouterMock } from '../../../../shared/mocks/router.mock';
+import { PaginationService } from '../../../../core/pagination/pagination.service';
+import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub';
describe('MembersListComponent', () => {
let component: MembersListComponent;
@@ -39,6 +41,7 @@ describe('MembersListComponent', () => {
let allGroups;
let epersonMembers;
let subgroupMembers;
+ let paginationService;
beforeEach(waitForAsync(() => {
activeGroup = GroupMock;
@@ -113,6 +116,8 @@ describe('MembersListComponent', () => {
};
builderService = getMockFormBuilderService();
translateService = getMockTranslateService();
+
+ paginationService = new PaginationServiceStub();
TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
TranslateModule.forRoot({
@@ -129,6 +134,7 @@ describe('MembersListComponent', () => {
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: FormBuilderService, useValue: builderService },
{ provide: Router, useValue: new RouterMock() },
+ { provide: PaginationService, useValue: paginationService },
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts
index 6513881fbf..54d144da51 100644
--- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts
+++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts
@@ -18,13 +18,13 @@ import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { EPerson } from '../../../../core/eperson/models/eperson.model';
import { Group } from '../../../../core/eperson/models/group.model';
import {
- getRemoteDataPayload,
getFirstSucceededRemoteData,
- getFirstCompletedRemoteData, getAllCompletedRemoteData
+ getFirstCompletedRemoteData, getAllCompletedRemoteData, getRemoteDataPayload
} from '../../../../core/shared/operators';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
import {EpersonDtoModel} from '../../../../core/eperson/models/eperson-dto.model';
+import { PaginationService } from '../../../../core/pagination/pagination.service';
/**
* Keys to keep track of specific subscriptions
@@ -60,7 +60,7 @@ export class MembersListComponent implements OnInit, OnDestroy {
* Pagination config used to display the list of EPeople that are result of EPeople search
*/
configSearch: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
- id: 'search-members-list-pagination',
+ id: 'sml',
pageSize: 5,
currentPage: 1
});
@@ -68,7 +68,7 @@ export class MembersListComponent implements OnInit, OnDestroy {
* Pagination config used to display the list of EPerson Membes of active group being edited
*/
config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
- id: 'members-list-pagination',
+ id: 'ml',
pageSize: 5,
currentPage: 1
});
@@ -91,11 +91,15 @@ export class MembersListComponent implements OnInit, OnDestroy {
// current active group being edited
groupBeingEdited: Group;
+ paginationSub: Subscription;
+
+
constructor(private groupDataService: GroupDataService,
public ePersonDataService: EPersonDataService,
private translateService: TranslateService,
private notificationsService: NotificationsService,
private formBuilder: FormBuilder,
+ private paginationService: PaginationService,
private router: Router) {
this.currentSearchQuery = '';
this.currentSearchScope = 'metadata';
@@ -114,23 +118,6 @@ export class MembersListComponent implements OnInit, OnDestroy {
}));
}
- /**
- * Event triggered when the user changes page on search result
- * @param event
- */
- onPageChangeSearch(event) {
- this.configSearch.currentPage = event;
- this.search({ scope: this.currentSearchScope, query: this.currentSearchQuery });
- }
-
- /**
- * Event triggered when the user changes page on EPerson embers of active group
- * @param event
- */
- onPageChange(event) {
- this.retrieveMembers(event);
- }
-
/**
* Retrieve the EPersons that are members of the group
*
@@ -139,10 +126,15 @@ export class MembersListComponent implements OnInit, OnDestroy {
*/
private retrieveMembers(page: number) {
this.unsubFrom(SubKey.MembersDTO);
- this.subs.set(SubKey.MembersDTO, this.ePersonDataService.findAllByHref(this.groupBeingEdited._links.epersons.href, {
- currentPage: page,
- elementsPerPage: this.config.pageSize
- }).pipe(
+ this.subs.set(SubKey.MembersDTO,
+ this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
+ switchMap((currentPagination) => {
+ return this.ePersonDataService.findAllByHref(this.groupBeingEdited._links.epersons.href, {
+ currentPage: currentPagination.currentPage,
+ elementsPerPage: currentPagination.pageSize
+ }
+ );
+ }),
getAllCompletedRemoteData(),
map((rd: RemoteData) => {
if (rd.hasFailed) {
@@ -245,26 +237,34 @@ export class MembersListComponent implements OnInit, OnDestroy {
* @param data Contains scope and query param
*/
search(data: any) {
- const query: string = data.query;
- const scope: string = data.scope;
- if (query != null && this.currentSearchQuery !== query && this.groupBeingEdited) {
- this.router.navigateByUrl(this.groupDataService.getGroupEditPageRouterLink(this.groupBeingEdited));
- this.currentSearchQuery = query;
- this.configSearch.currentPage = 1;
- }
- if (scope != null && this.currentSearchScope !== scope && this.groupBeingEdited) {
- this.router.navigateByUrl(this.groupDataService.getGroupEditPageRouterLink(this.groupBeingEdited));
- this.currentSearchScope = scope;
- this.configSearch.currentPage = 1;
- }
- this.searchDone = true;
-
this.unsubFrom(SubKey.SearchResultsDTO);
this.subs.set(SubKey.SearchResultsDTO,
- this.ePersonDataService.searchByScope(this.currentSearchScope, this.currentSearchQuery, {
- currentPage: this.configSearch.currentPage,
- elementsPerPage: this.configSearch.pageSize
- }, false).pipe(
+ this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe(
+ switchMap((paginationOptions) => {
+
+ const query: string = data.query;
+ const scope: string = data.scope;
+ if (query != null && this.currentSearchQuery !== query && this.groupBeingEdited) {
+ this.router.navigate([], {
+ queryParamsHandling: 'merge'
+ });
+ this.currentSearchQuery = query;
+ this.paginationService.resetPage(this.configSearch.id);
+ }
+ if (scope != null && this.currentSearchScope !== scope && this.groupBeingEdited) {
+ this.router.navigate([], {
+ queryParamsHandling: 'merge'
+ });
+ this.currentSearchScope = scope;
+ this.paginationService.resetPage(this.configSearch.id);
+ }
+ this.searchDone = true;
+
+ return this.ePersonDataService.searchByScope(this.currentSearchScope, this.currentSearchQuery, {
+ currentPage: paginationOptions.currentPage,
+ elementsPerPage: paginationOptions.pageSize
+ });
+ }),
getAllCompletedRemoteData(),
map((rd: RemoteData) => {
if (rd.hasFailed) {
@@ -300,6 +300,8 @@ export class MembersListComponent implements OnInit, OnDestroy {
for (const key of this.subs.keys()) {
this.unsubFrom(key);
}
+ this.paginationService.clearPagination(this.config.id);
+ this.paginationService.clearPagination(this.configSearch.id);
}
/**
diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html
index 293d9e469a..5b87045e78 100644
--- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html
+++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html
@@ -29,8 +29,7 @@
[pageInfoState]="(searchResults$ | async)?.payload"
[collectionSize]="(searchResults$ | async)?.payload?.totalElements"
[hideGear]="true"
- [hidePagerWhenSinglePage]="true"
- (pageChange)="onPageChangeSearch($event)">
+ [hidePagerWhenSinglePage]="true">
@@ -83,8 +82,7 @@
[pageInfoState]="(subGroups$ | async)?.payload"
[collectionSize]="(subGroups$ | async)?.payload?.totalElements"
[hideGear]="true"
- [hidePagerWhenSinglePage]="true"
- (pageChange)="onPageChange($event)">
+ [hidePagerWhenSinglePage]="true">
diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts
index e839d77a6a..bee5126e09 100644
--- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts
+++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts
@@ -35,6 +35,8 @@ import { getMockTranslateService } from '../../../../shared/mocks/translate.serv
import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
import { map } from 'rxjs/operators';
+import { PaginationService } from '../../../../core/pagination/pagination.service';
+import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub';
describe('SubgroupsListComponent', () => {
let component: SubgroupsListComponent;
@@ -47,6 +49,7 @@ describe('SubgroupsListComponent', () => {
let subgroups;
let allGroups;
let routerStub;
+ let paginationService;
beforeEach(waitForAsync(() => {
activeGroup = GroupMock;
@@ -100,6 +103,8 @@ describe('SubgroupsListComponent', () => {
routerStub = new RouterMock();
builderService = getMockFormBuilderService();
translateService = getMockTranslateService();
+
+ paginationService = new PaginationServiceStub();
TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
TranslateModule.forRoot({
@@ -115,6 +120,7 @@ describe('SubgroupsListComponent', () => {
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: FormBuilderService, useValue: builderService },
{ provide: Router, useValue: routerStub },
+ { provide: PaginationService, useValue: paginationService },
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts
index d9f03963d6..6d8285f10b 100644
--- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts
+++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts
@@ -2,20 +2,21 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
-import { Observable, of as observableOf, Subscription, BehaviorSubject } from 'rxjs';
-import { map, mergeMap, take } from 'rxjs/operators';
+import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs';
+import { map, mergeMap, switchMap, take } from 'rxjs/operators';
import { PaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { Group } from '../../../../core/eperson/models/group.model';
import {
- getRemoteDataPayload,
+ getFirstCompletedRemoteData,
getFirstSucceededRemoteData,
- getFirstCompletedRemoteData
+ getRemoteDataPayload
} from '../../../../core/shared/operators';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
import { NoContent } from '../../../../core/shared/NoContent.model';
+import { PaginationService } from '../../../../core/pagination/pagination.service';
/**
* Keys to keep track of specific subscriptions
@@ -56,7 +57,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
* Pagination config used to display the list of groups that are result of groups search
*/
configSearch: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
- id: 'search-subgroups-list-pagination',
+ id: 'ssgl',
pageSize: 5,
currentPage: 1
});
@@ -64,7 +65,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
* Pagination config used to display the list of subgroups of currently active group being edited
*/
config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
- id: 'subgroups-list-pagination',
+ id: 'sgl',
pageSize: 5,
currentPage: 1
});
@@ -85,6 +86,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
private translateService: TranslateService,
private notificationsService: NotificationsService,
private formBuilder: FormBuilder,
+ private paginationService: PaginationService,
private router: Router) {
this.currentSearchQuery = '';
}
@@ -96,42 +98,27 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
this.subs.set(SubKey.ActiveGroup, this.groupDataService.getActiveGroup().subscribe((activeGroup: Group) => {
if (activeGroup != null) {
this.groupBeingEdited = activeGroup;
- this.retrieveSubGroups(this.config.currentPage);
+ this.retrieveSubGroups();
}
}));
}
- /**
- * Event triggered when the user changes page on search result
- * @param event
- */
- onPageChangeSearch(event) {
- this.configSearch.currentPage = event;
- this.search({ query: this.currentSearchQuery });
- }
-
- /**
- * Event triggered when the user changes page on subgroups of active group
- * @param event
- */
- onPageChange(event) {
- this.retrieveSubGroups(event);
- }
-
/**
* Retrieve the Subgroups that are members of the group
*
* @param page the number of the page to retrieve
* @private
*/
- private retrieveSubGroups(page: number) {
+ private retrieveSubGroups() {
this.unsubFrom(SubKey.Members);
this.subs.set(
SubKey.Members,
- this.groupDataService.findAllByHref(this.groupBeingEdited._links.subgroups.href, {
- currentPage: page,
- elementsPerPage: this.config.pageSize
- }
+ this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
+ switchMap((config) => this.groupDataService.findAllByHref(this.groupBeingEdited._links.subgroups.href, {
+ currentPage: config.currentPage,
+ elementsPerPage: config.pageSize
+ }
+ ))
).subscribe((rd: RemoteData>) => {
this.subGroups$.next(rd);
}));
@@ -226,10 +213,12 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
this.searchDone = true;
this.unsubFrom(SubKey.SearchResults);
- this.subs.set(SubKey.SearchResults, this.groupDataService.searchGroups(this.currentSearchQuery, {
- currentPage: this.configSearch.currentPage,
- elementsPerPage: this.configSearch.pageSize
- }).subscribe((rd: RemoteData>) => {
+ this.subs.set(SubKey.SearchResults, this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe(
+ switchMap((config) => this.groupDataService.searchGroups(this.currentSearchQuery, {
+ currentPage: config.currentPage,
+ elementsPerPage: config.pageSize
+ }))
+ ).subscribe((rd: RemoteData>) => {
this.searchResults$.next(rd);
}));
}
@@ -255,6 +244,8 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
for (const key of this.subs.keys()) {
this.unsubFrom(key);
}
+ this.paginationService.clearPagination(this.config.id);
+ this.paginationService.clearPagination(this.configSearch.id);
}
/**
diff --git a/src/app/access-control/group-registry/groups-registry.component.html b/src/app/access-control/group-registry/groups-registry.component.html
index 219c236ef5..e5e25ae944 100644
--- a/src/app/access-control/group-registry/groups-registry.component.html
+++ b/src/app/access-control/group-registry/groups-registry.component.html
@@ -40,8 +40,7 @@
[pageInfoState]="pageInfoState$"
[collectionSize]="(pageInfoState$ | async)?.totalElements"
[hideGear]="true"
- [hidePagerWhenSinglePage]="true"
- (pageChange)="onPageChange($event)">
+ [hidePagerWhenSinglePage]="true">
diff --git a/src/app/access-control/group-registry/groups-registry.component.spec.ts b/src/app/access-control/group-registry/groups-registry.component.spec.ts
index b5b5a1b209..10064800e1 100644
--- a/src/app/access-control/group-registry/groups-registry.component.spec.ts
+++ b/src/app/access-control/group-registry/groups-registry.component.spec.ts
@@ -28,6 +28,8 @@ import { TranslateLoaderMock } from '../../shared/testing/translate-loader.mock'
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { routeServiceStub } from '../../shared/testing/route-service.stub';
import { RouterMock } from '../../shared/mocks/router.mock';
+import { PaginationService } from '../../core/pagination/pagination.service';
+import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
describe('GroupRegistryComponent', () => {
let component: GroupsRegistryComponent;
@@ -39,6 +41,7 @@ describe('GroupRegistryComponent', () => {
let mockGroups;
let mockEPeople;
+ let paginationService;
beforeEach(waitForAsync(() => {
mockGroups = [GroupMock, GroupMock2];
@@ -131,6 +134,7 @@ describe('GroupRegistryComponent', () => {
authorizationService = jasmine.createSpyObj('authorizationService', {
isAuthorized: observableOf(true)
});
+ paginationService = new PaginationServiceStub();
TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
TranslateModule.forRoot({
@@ -149,6 +153,7 @@ describe('GroupRegistryComponent', () => {
{ provide: RouteService, useValue: routeServiceStub },
{ provide: Router, useValue: new RouterMock() },
{ provide: AuthorizationDataService, useValue: authorizationService },
+ { provide: PaginationService, useValue: paginationService },
{ provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) }
],
schemas: [NO_ERRORS_SCHEMA]
diff --git a/src/app/access-control/group-registry/groups-registry.component.ts b/src/app/access-control/group-registry/groups-registry.component.ts
index 19dcc3c552..b28ac043d9 100644
--- a/src/app/access-control/group-registry/groups-registry.component.ts
+++ b/src/app/access-control/group-registry/groups-registry.component.ts
@@ -5,15 +5,15 @@ import { TranslateService } from '@ngx-translate/core';
import {
BehaviorSubject,
combineLatest as observableCombineLatest,
- Subscription,
Observable,
- of as observableOf
+ of as observableOf,
+ Subscription
} from 'rxjs';
import { catchError, map, switchMap, take } from 'rxjs/operators';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
-import { PaginatedList, buildPaginatedList } from '../../core/data/paginated-list.model';
+import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model';
import { RemoteData } from '../../core/data/remote-data';
import { RequestService } from '../../core/data/request.service';
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
@@ -24,15 +24,17 @@ import { Group } from '../../core/eperson/models/group.model';
import { RouteService } from '../../core/services/route.service';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import {
- getAllSucceededRemoteDataPayload,
+ getAllSucceededRemoteData,
getFirstCompletedRemoteData,
- getFirstSucceededRemoteData
+ getFirstSucceededRemoteData,
+ getRemoteDataPayload
} from '../../core/shared/operators';
import { PageInfo } from '../../core/shared/page-info.model';
import { hasValue } from '../../shared/empty.util';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { NoContent } from '../../core/shared/NoContent.model';
+import { PaginationService } from '../../core/pagination/pagination.service';
@Component({
selector: 'ds-groups-registry',
@@ -50,7 +52,7 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
* Pagination config used to display the list of groups
*/
config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
- id: 'groups-list-pagination',
+ id: 'gl',
pageSize: 5,
currentPage: 1
});
@@ -83,6 +85,8 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
*/
searchSub: Subscription;
+ paginationSub: Subscription;
+
/**
* List of subscriptions
*/
@@ -97,6 +101,7 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
protected routeService: RouteService,
private router: Router,
private authorizationService: AuthorizationDataService,
+ private paginationService: PaginationService,
public requestService: RequestService) {
this.currentSearchQuery = '';
this.searchForm = this.formBuilder.group(({
@@ -108,37 +113,30 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
this.search({ query: this.currentSearchQuery });
}
- /**
- * Event triggered when the user changes page
- * @param event
- */
- onPageChange(event) {
- this.config.currentPage = event;
- this.search({ query: this.currentSearchQuery });
- }
-
/**
* Search in the groups (searches by group name and by uuid exact match)
* @param data Contains query param
*/
search(data: any) {
this.searching$.next(true);
- const query: string = data.query;
- if (query != null && this.currentSearchQuery !== query) {
- this.router.navigateByUrl(this.groupService.getGroupRegistryRouterLink());
- this.currentSearchQuery = query;
- this.config.currentPage = 1;
- }
if (hasValue(this.searchSub)) {
this.searchSub.unsubscribe();
this.subs = this.subs.filter((sub: Subscription) => sub !== this.searchSub);
}
-
- this.searchSub = this.groupService.searchGroups(this.currentSearchQuery.trim(), {
- currentPage: this.config.currentPage,
- elementsPerPage: this.config.pageSize
- }).pipe(
- getAllSucceededRemoteDataPayload(),
+ this.searchSub = this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
+ switchMap((paginationOptions) => {
+ const query: string = data.query;
+ if (query != null && this.currentSearchQuery !== query) {
+ this.currentSearchQuery = query;
+ this.paginationService.updateRouteWithUrl(this.config.id, [], {page: 1});
+ }
+ return this.groupService.searchGroups(this.currentSearchQuery.trim(), {
+ currentPage: paginationOptions.currentPage,
+ elementsPerPage: paginationOptions.pageSize
+ });
+ }),
+ getAllSucceededRemoteData(),
+ getRemoteDataPayload(),
switchMap((groups: PaginatedList) => {
if (groups.page.length === 0) {
return observableOf(buildPaginatedList(groups.pageInfo, []));
@@ -166,13 +164,15 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
})).pipe(map((dtos: GroupDtoModel[]) => {
return buildPaginatedList(groups.pageInfo, dtos);
}));
- })).subscribe((value: PaginatedList) => {
+ })
+ ).subscribe((value: PaginatedList) => {
this.groupsDto$.next(value);
this.pageInfoState$.next(value.pageInfo);
this.searching$.next(false);
});
+
this.subs.push(this.searchSub);
- }
+ }
/**
* Delete Group
@@ -248,9 +248,16 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
*/
ngOnDestroy(): void {
this.cleanupSubscribes();
+ this.paginationService.clearPagination(this.config.id);
}
+
cleanupSubscribes() {
+ if (hasValue(this.paginationSub)) {
+ this.paginationSub.unsubscribe();
+ }
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
+ this.paginationService.clearPagination(this.config.id);
}
+
}
diff --git a/src/app/core/pagination/pagination.service.spec.ts b/src/app/core/pagination/pagination.service.spec.ts
new file mode 100644
index 0000000000..18f94cc84c
--- /dev/null
+++ b/src/app/core/pagination/pagination.service.spec.ts
@@ -0,0 +1,158 @@
+import { PaginationService } from './pagination.service';
+import { RouterStub } from '../../shared/testing/router.stub';
+import { of as observableOf } from 'rxjs';
+import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../cache/models/sort-options.model';
+import { FindListOptions } from '../data/request.models';
+
+
+describe('PaginationService', () => {
+ let service: PaginationService;
+ let router;
+ let routeService;
+
+ const defaultPagination = new PaginationComponentOptions();
+ const defaultSort = new SortOptions('id', SortDirection.DESC);
+ const defaultFindListOptions = new FindListOptions();
+
+ beforeEach(() => {
+ router = new RouterStub();
+ routeService = {
+ getQueryParameterValue: (param) => {
+ let value;
+ if (param.endsWith('.page')) {
+ value = 5;
+ }
+ if (param.endsWith('.rpp')) {
+ value = 10;
+ }
+ if (param.endsWith('.sd')) {
+ value = 'ASC';
+ }
+ if (param.endsWith('.sf')) {
+ value = 'score';
+ }
+ return observableOf(value);
+ }
+ };
+
+ service = new PaginationService(routeService, router);
+ });
+
+
+ describe('getCurrentPagination', () => {
+ it('should retrieve the current pagination info from the routerService', () => {
+ service.getCurrentPagination('test-id', defaultPagination).subscribe((currentPagination) => {
+ expect(currentPagination).toEqual(Object.assign(new PaginationComponentOptions(), {
+ currentPage: 5,
+ pageSize: 10
+ }));
+ });
+ });
+ });
+ describe('getCurrentSort', () => {
+ it('should retrieve the current sort info from the routerService', () => {
+ service.getCurrentSort('test-id', defaultSort).subscribe((currentSort) => {
+ expect(currentSort).toEqual(Object.assign(new SortOptions('score', SortDirection.ASC )));
+ });
+ });
+ });
+ describe('getFindListOptions', () => {
+ it('should retrieve the current findListOptions info from the routerService', () => {
+ service.getFindListOptions('test-id', defaultFindListOptions).subscribe((findListOptions) => {
+ expect(findListOptions).toEqual(Object.assign(new FindListOptions(),
+ {
+ sort: new SortOptions('score', SortDirection.ASC ),
+ currentPage: 5,
+ elementsPerPage: 10
+ }));
+ });
+ });
+ });
+ describe('resetPage', () => {
+ it('should call the updateRoute method with the id and page 1', () => {
+ spyOn(service, 'updateRoute');
+ service.resetPage('test');
+
+ expect(service.updateRoute).toHaveBeenCalledWith('test', {page: 1});
+ });
+ });
+
+ describe('updateRoute', () => {
+ it('should update the route with the provided page params', () => {
+ service.updateRoute('test', {page: 2, pageSize: 5, sortField: 'title', sortDirection: SortDirection.DESC});
+
+ const navigateParams = {};
+ navigateParams[`test.page`] = `2`;
+ navigateParams[`test.rpp`] = `5`;
+ navigateParams[`test.sf`] = `title`;
+ navigateParams[`test.sd`] = `DESC`;
+
+ expect(router.navigate).toHaveBeenCalledWith([], {queryParams: navigateParams, queryParamsHandling: 'merge'});
+ });
+ it('should update the route with the provided page params while keeping the existing non provided ones', () => {
+ service.updateRoute('test', {page: 2});
+
+ const navigateParams = {};
+ navigateParams[`test.page`] = `2`;
+ navigateParams[`test.rpp`] = `10`;
+ navigateParams[`test.sf`] = `score`;
+ navigateParams[`test.sd`] = `ASC`;
+
+ expect(router.navigate).toHaveBeenCalledWith([], {queryParams: navigateParams, queryParamsHandling: 'merge'});
+ });
+ });
+ describe('updateRouteWithUrl', () => {
+ it('should update the route with the provided page params and url', () => {
+ service.updateRouteWithUrl('test', ['someUrl'], {page: 2, pageSize: 5, sortField: 'title', sortDirection: SortDirection.DESC});
+
+ const navigateParams = {};
+ navigateParams[`test.page`] = `2`;
+ navigateParams[`test.rpp`] = `5`;
+ navigateParams[`test.sf`] = `title`;
+ navigateParams[`test.sd`] = `DESC`;
+
+ expect(router.navigate).toHaveBeenCalledWith(['someUrl'], {queryParams: navigateParams, queryParamsHandling: 'merge'});
+ });
+ it('should update the route with the provided page params and url while keeping the existing non provided ones', () => {
+ service.updateRouteWithUrl('test',['someUrl'], {page: 2});
+
+ const navigateParams = {};
+ navigateParams[`test.page`] = `2`;
+ navigateParams[`test.rpp`] = `10`;
+ navigateParams[`test.sf`] = `score`;
+ navigateParams[`test.sd`] = `ASC`;
+
+ expect(router.navigate).toHaveBeenCalledWith(['someUrl'], {queryParams: navigateParams, queryParamsHandling: 'merge'});
+ });
+
+ });
+ describe('clearPagination', () => {
+ it('should clear the pagination next time the updateRoute/updateRouteWithUrl method is called', () => {
+ service.clearPagination('test');
+
+ const resetParams = {};
+ resetParams[`test.page`] = null;
+ resetParams[`test.rpp`] = null;
+ resetParams[`test.sf`] = null;
+ resetParams[`test.sd`] = null;
+
+
+ const navigateParams = {};
+ navigateParams[`another-id.page`] = `5`;
+ navigateParams[`another-id.rpp`] = `10`;
+ navigateParams[`another-id.sf`] = `score`;
+ navigateParams[`another-id.sd`] = `ASC`;
+
+ service.updateRoute('another-id', {});
+
+ expect(router.navigate).toHaveBeenCalledWith([], {queryParams: Object.assign({}, resetParams, navigateParams), queryParamsHandling: 'merge'});
+ });
+ });
+ describe('getPageParam', () => {
+ it('should return the name of the page param', () => {
+ const pageParam = service.getPageParam('test');
+ expect(pageParam).toEqual('test.page');
+ });
+ });
+});
diff --git a/src/app/core/pagination/pagination.service.ts b/src/app/core/pagination/pagination.service.ts
new file mode 100644
index 0000000000..dae6991834
--- /dev/null
+++ b/src/app/core/pagination/pagination.service.ts
@@ -0,0 +1,242 @@
+import { Injectable } from '@angular/core';
+import { Router } from '@angular/router';
+import { RouteService } from '../services/route.service';
+import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
+import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
+import { filter, map, take } from 'rxjs/operators';
+import { SortDirection, SortOptions } from '../cache/models/sort-options.model';
+import { FindListOptions } from '../data/request.models';
+import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
+import { difference } from '../../shared/object.util';
+import { isNumeric } from 'rxjs/internal-compatibility';
+
+
+@Injectable({
+ providedIn: 'root',
+})
+/**
+ * Service to manage the pagination of different components
+ * The pagination information will be stored in the route based on a paginationID.
+ * The following params are used for the different kind of pagination information:
+ * - For the page: {paginationID}.p
+ * - For the page size: {paginationID}.rpp
+ * - For the sort direction: {paginationID}.sd
+ * - For the sort field: {paginationID}.sf
+ */
+export class PaginationService {
+
+ private defaultSortOptions = new SortOptions('id', SortDirection.ASC);
+
+ private clearParams = {};
+
+ constructor(protected routeService: RouteService,
+ protected router: Router
+ ) {
+ }
+
+ /**
+ * Method to retrieve the current pagination settings for an ID based on the router params and default options
+ * @param paginationId - The id to check the pagination for
+ * @param defaultPagination - The default pagination values to be used when no route info is present
+ * @returns {Observable} Retrieves the current pagination settings based on the router params
+ */
+ getCurrentPagination(paginationId: string, defaultPagination: PaginationComponentOptions): Observable {
+ const page$ = this.routeService.getQueryParameterValue(`${paginationId}.page`);
+ const size$ = this.routeService.getQueryParameterValue(`${paginationId}.rpp`);
+ return observableCombineLatest([page$, size$]).pipe(
+ map(([page, size]) => {
+ return Object.assign(new PaginationComponentOptions(), defaultPagination, {
+ currentPage: this.convertToNumeric(page, defaultPagination.currentPage),
+ pageSize: this.getBestMatchPageSize(size, defaultPagination)
+ });
+ })
+ );
+ }
+
+ /**
+ * Method to retrieve the current sort options for an ID based on the router params and default options
+ * @param paginationId - The id to check the sort options for
+ * @param defaultSort - The default sort options to be used when no route info is present
+ * @param ignoreDefault - Indicate whether the default should be ignored
+ * @returns {Observable} Retrieves the current sort options based on the router params
+ */
+ getCurrentSort(paginationId: string, defaultSort: SortOptions, ignoreDefault?: boolean): Observable {
+ if (!ignoreDefault && (isEmpty(defaultSort) || !hasValue(defaultSort))) {
+ defaultSort = this.defaultSortOptions;
+ }
+ const sortDirection$ = this.routeService.getQueryParameterValue(`${paginationId}.sd`);
+ const sortField$ = this.routeService.getQueryParameterValue(`${paginationId}.sf`);
+ return observableCombineLatest([sortDirection$, sortField$]).pipe(map(([sortDirection, sortField]) => {
+ const field = sortField || defaultSort?.field;
+ const direction = SortDirection[sortDirection] || defaultSort?.direction;
+ return new SortOptions(field, direction);
+ })
+ );
+ }
+
+ /**
+ * Method to retrieve the current find list options for an ID based on the router params and default options
+ * @param paginationId - The id to check the find list options for
+ * @param defaultFindList - The default find list options to be used when no route info is present
+ * @param ignoreDefault - Indicate whether the default should be ignored
+ * @returns {Observable} Retrieves the current find list options based on the router params
+ */
+ getFindListOptions(paginationId: string, defaultFindList: FindListOptions, ignoreDefault?: boolean): Observable {
+ const paginationComponentOptions = new PaginationComponentOptions();
+ paginationComponentOptions.currentPage = defaultFindList.currentPage;
+ paginationComponentOptions.pageSize = defaultFindList.elementsPerPage;
+ const currentPagination$ = this.getCurrentPagination(paginationId, paginationComponentOptions);
+ const currentSortOptions$ = this.getCurrentSort(paginationId, defaultFindList.sort, ignoreDefault);
+
+ return observableCombineLatest([currentPagination$, currentSortOptions$]).pipe(
+ filter(([currentPagination, currentSortOptions]) => hasValue(currentPagination) && hasValue(currentSortOptions)),
+ map(([currentPagination, currentSortOptions]) => {
+ return Object.assign(new FindListOptions(), defaultFindList, {
+ sort: currentSortOptions,
+ currentPage: currentPagination.currentPage,
+ elementsPerPage: currentPagination.pageSize
+ });
+ }));
+ }
+
+ /**
+ * Reset the current page for the provided pagination ID to 1.
+ * @param paginationId - The pagination id for which to reset the page
+ */
+ resetPage(paginationId: string) {
+ this.updateRoute(paginationId, {page: 1});
+ }
+
+
+ /**
+ * Update the route with the provided information
+ * @param paginationId - The pagination ID for which to update the route with info
+ * @param params - The page related params to update in the route
+ * @param extraParams - Addition params unrelated to the pagination that need to be added to the route
+ * @param retainScrollPosition - Scroll to the pagination component after updating the route instead of the top of the page
+ */
+ updateRoute(paginationId: string, params: {
+ page?: number
+ pageSize?: number
+ sortField?: string
+ sortDirection?: SortDirection
+ }, extraParams?, retainScrollPosition?: boolean) {
+
+ this.updateRouteWithUrl(paginationId, [], params, extraParams, retainScrollPosition);
+ }
+
+ /**
+ * Update the route with the provided information
+ * @param paginationId - The pagination ID for which to update the route with info
+ * @param url - The url to navigate to
+ * @param params - The page related params to update in the route
+ * @param extraParams - Addition params unrelated to the pagination that need to be added to the route
+ * @param retainScrollPosition - Scroll to the pagination component after updating the route instead of the top of the page
+ */
+ updateRouteWithUrl(paginationId: string, url: string[], params: {
+ page?: number
+ pageSize?: number
+ sortField?: string
+ sortDirection?: SortDirection
+ }, extraParams?, retainScrollPosition?: boolean) {
+ this.getCurrentRouting(paginationId).subscribe((currentFindListOptions) => {
+ const currentParametersWithIdName = this.getParametersWithIdName(paginationId, currentFindListOptions);
+ const parametersWithIdName = this.getParametersWithIdName(paginationId, params);
+ if (isNotEmpty(difference(parametersWithIdName, currentParametersWithIdName)) || isNotEmpty(extraParams) || isNotEmpty(this.clearParams)) {
+ const queryParams = Object.assign({}, this.clearParams, currentParametersWithIdName,
+ parametersWithIdName, extraParams);
+ if (retainScrollPosition) {
+ this.router.navigate(url, {
+ queryParams: queryParams,
+ queryParamsHandling: 'merge',
+ fragment: `p-${paginationId}`
+ });
+ } else {
+ this.router.navigate(url, {
+ queryParams: queryParams,
+ queryParamsHandling: 'merge'
+ });
+ }
+ this.clearParams = {};
+ }
+ });
+ }
+
+ /**
+ * Add the params to be cleared to the clearParams variable.
+ * When the updateRoute or updateRouteWithUrl these params will be removed from the route pagination
+ * @param paginationId - The ID for which to clear the params
+ */
+ clearPagination(paginationId: string) {
+ const params = {};
+ params[`${paginationId}.page`] = null;
+ params[`${paginationId}.rpp`] = null;
+ params[`${paginationId}.sf`] = null;
+ params[`${paginationId}.sd`] = null;
+
+ Object.assign(this.clearParams, params);
+ }
+
+ /**
+ * Retrieve the page parameter for the provided id
+ * @param paginationId - The ID for which to retrieve the page param
+ */
+ getPageParam(paginationId: string) {
+ return `${paginationId}.page`;
+ }
+
+ private getCurrentRouting(paginationId: string) {
+ return this.getFindListOptions(paginationId, {}, true).pipe(
+ take(1),
+ map((findListoptions: FindListOptions) => {
+ return {
+ page: findListoptions.currentPage,
+ pageSize: findListoptions.elementsPerPage,
+ sortField: findListoptions.sort.field,
+ sortDirection: findListoptions.sort.direction,
+ };
+ })
+ );
+ }
+
+ private getParametersWithIdName(paginationId: string, params: {
+ page?: number
+ pageSize?: number
+ sortField?: string
+ sortDirection?: SortDirection
+ }) {
+ const paramsWithIdName = {};
+ if (hasValue(params.page)) {
+ paramsWithIdName[`${paginationId}.page`] = `${params.page}`;
+ }
+ if (hasValue(params.pageSize)) {
+ paramsWithIdName[`${paginationId}.rpp`] = `${params.pageSize}`;
+ }
+ if (hasValue(params.sortField)) {
+ paramsWithIdName[`${paginationId}.sf`] = `${params.sortField}`;
+ }
+ if (hasValue(params.sortDirection)) {
+ paramsWithIdName[`${paginationId}.sd`] = `${params.sortDirection}`;
+ }
+ return paramsWithIdName;
+ }
+
+ private convertToNumeric(param, defaultValue) {
+ let result = defaultValue;
+ if (isNumeric(param)) {
+ result = +param;
+ }
+ return result;
+ }
+
+
+ private getBestMatchPageSize(pageSize: any, defaultPagination: PaginationComponentOptions): number {
+ const numberPageSize = this.convertToNumeric(pageSize, defaultPagination.pageSize);
+ const differenceList = defaultPagination.pageSizeOptions.map((pageSizeOption) => {
+ return Math.abs(pageSizeOption - numberPageSize);
+ });
+ const minDifference = Math.min.apply(Math, differenceList);
+ return defaultPagination.pageSizeOptions[differenceList.indexOf(minDifference)];
+ }
+
+}
diff --git a/src/app/core/services/route.service.ts b/src/app/core/services/route.service.ts
index f53b24c1c8..23b7ccec85 100644
--- a/src/app/core/services/route.service.ts
+++ b/src/app/core/services/route.service.ts
@@ -18,7 +18,7 @@ import { AddUrlToHistoryAction } from '../history/history.actions';
*/
export const routeParametersSelector = createSelector(
coreSelector,
- (state: CoreState) => state.route.params
+ (state: CoreState) => hasValue(state) && hasValue(state.route) ? state.route.params : undefined
);
/**
@@ -26,7 +26,7 @@ export const routeParametersSelector = createSelector(
*/
export const queryParametersSelector = createSelector(
coreSelector,
- (state: CoreState) => state.route.queryParams
+ (state: CoreState) => hasValue(state) && hasValue(state.route) ? state.route.queryParams : undefined
);
/**
diff --git a/src/app/core/shared/search/search-configuration.service.spec.ts b/src/app/core/shared/search/search-configuration.service.spec.ts
index 43c2c54427..061182c2fc 100644
--- a/src/app/core/shared/search/search-configuration.service.spec.ts
+++ b/src/app/core/shared/search/search-configuration.service.spec.ts
@@ -5,6 +5,7 @@ import { SortDirection, SortOptions } from '../../cache/models/sort-options.mode
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
import { SearchFilter } from '../../../shared/search/search-filter.model';
import { of as observableOf } from 'rxjs';
+import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
describe('SearchConfigurationService', () => {
let service: SearchConfigurationService;
@@ -15,7 +16,7 @@ describe('SearchConfigurationService', () => {
'f.date.max': ['2018']
};
const defaults = new PaginatedSearchOptions({
- pagination: Object.assign(new PaginationComponentOptions(), { currentPage: 1, pageSize: 20 }),
+ pagination: Object.assign(new PaginationComponentOptions(), { id: 'page-id', currentPage: 1, pageSize: 20 }),
sort: new SortOptions('score', SortDirection.DESC),
configuration: 'default',
query: '',
@@ -30,10 +31,13 @@ describe('SearchConfigurationService', () => {
getRouteParameterValue: observableOf('')
});
+ const paginationService = new PaginationServiceStub();
+
+
const activatedRoute: any = new ActivatedRouteStub();
beforeEach(() => {
- service = new SearchConfigurationService(routeService, activatedRoute);
+ service = new SearchConfigurationService(routeService, paginationService as any, activatedRoute);
});
describe('when the scope is called', () => {
beforeEach(() => {
@@ -95,25 +99,19 @@ describe('SearchConfigurationService', () => {
describe('when getCurrentSort is called', () => {
beforeEach(() => {
- service.getCurrentSort({} as any);
+ service.getCurrentSort(defaults.pagination.id, {} as any);
});
- it('should call getQueryParameterValue on the routeService with parameter name \'sortDirection\'', () => {
- expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('sortDirection');
- });
- it('should call getQueryParameterValue on the routeService with parameter name \'sortField\'', () => {
- expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('sortField');
+ it('should call getCurrentSort on the paginationService with the provided id and sort options', () => {
+ expect((service as any).paginationService.getCurrentSort).toHaveBeenCalledWith(defaults.pagination.id, {});
});
});
describe('when getCurrentPagination is called', () => {
beforeEach(() => {
- service.getCurrentPagination({ currentPage: 1, pageSize: 10 } as any);
+ service.getCurrentPagination(defaults.pagination.id, defaults.pagination);
});
- it('should call getQueryParameterValue on the routeService with parameter name \'page\'', () => {
- expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('page');
- });
- it('should call getQueryParameterValue on the routeService with parameter name \'pageSize\'', () => {
- expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('pageSize');
+ it('should call getCurrentPagination on the paginationService with the provided id and sort options', () => {
+ expect((service as any).paginationService.getCurrentPagination).toHaveBeenCalledWith(defaults.pagination.id, defaults.pagination);
});
});
@@ -145,7 +143,7 @@ describe('SearchConfigurationService', () => {
describe('when subscribeToPaginatedSearchOptions is called', () => {
beforeEach(() => {
- (service as any).subscribeToPaginatedSearchOptions(defaults);
+ (service as any).subscribeToPaginatedSearchOptions(defaults.pagination.id, defaults);
});
it('should call all getters it needs', () => {
expect(service.getCurrentPagination).toHaveBeenCalled();
diff --git a/src/app/core/shared/search/search-configuration.service.ts b/src/app/core/shared/search/search-configuration.service.ts
index edd3982319..798a0de287 100644
--- a/src/app/core/shared/search/search-configuration.service.ts
+++ b/src/app/core/shared/search/search-configuration.service.ts
@@ -14,17 +14,20 @@ import { RouteService } from '../../services/route.service';
import { getFirstSucceededRemoteData } from '../operators';
import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../../shared/empty.util';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
+import { PaginationService } from '../../pagination/pagination.service';
/**
* Service that performs all actions that have to do with the current search configuration
*/
@Injectable()
export class SearchConfigurationService implements OnDestroy {
+
+ public paginationID = 'spc';
/**
* Default pagination settings
*/
protected defaultPagination = Object.assign(new PaginationComponentOptions(), {
- id: 'search-page-configuration',
+ id: this.paginationID,
pageSize: 10,
currentPage: 1
});
@@ -75,6 +78,7 @@ export class SearchConfigurationService implements OnDestroy {
* @param {ActivatedRoute} route
*/
constructor(protected routeService: RouteService,
+ protected paginationService: PaginationService,
protected route: ActivatedRoute) {
this.initDefaults();
@@ -91,7 +95,7 @@ export class SearchConfigurationService implements OnDestroy {
this.paginatedSearchOptions = new BehaviorSubject(defs);
this.searchOptions = new BehaviorSubject(defs);
this.subs.push(this.subscribeToSearchOptions(defs));
- this.subs.push(this.subscribeToPaginatedSearchOptions(defs));
+ this.subs.push(this.subscribeToPaginatedSearchOptions(defs.pagination.id, defs));
}
);
}
@@ -140,34 +144,15 @@ export class SearchConfigurationService implements OnDestroy {
/**
* @returns {Observable} Emits the current pagination settings
*/
- getCurrentPagination(defaultPagination: PaginationComponentOptions): Observable {
- const page$ = this.routeService.getQueryParameterValue('page');
- const size$ = this.routeService.getQueryParameterValue('pageSize');
- return observableCombineLatest(page$, size$).pipe(map(([page, size]) => {
- return Object.assign(new PaginationComponentOptions(), defaultPagination, {
- currentPage: page || defaultPagination.currentPage,
- pageSize: size || defaultPagination.pageSize
- });
- })
- );
+ getCurrentPagination(paginationId: string, defaultPagination: PaginationComponentOptions): Observable {
+ return this.paginationService.getCurrentPagination(paginationId, defaultPagination);
}
/**
* @returns {Observable} Emits the current sorting settings
*/
- getCurrentSort(defaultSort: SortOptions): Observable {
- const sortDirection$ = this.routeService.getQueryParameterValue('sortDirection');
- const sortField$ = this.routeService.getQueryParameterValue('sortField');
- return observableCombineLatest(sortDirection$, sortField$).pipe(map(([sortDirection, sortField]) => {
- // Dirty fix because sometimes the observable value is null somehow
- sortField = this.route.snapshot.queryParamMap.get('sortField');
-
- const field = sortField || defaultSort.field;
- const direction = SortDirection[sortDirection] || defaultSort.direction;
- return new SortOptions(field, direction);
- }
- )
- );
+ getCurrentSort(paginationId: string, defaultSort: SortOptions): Observable {
+ return this.paginationService.getCurrentSort(paginationId, defaultSort);
}
/**
@@ -234,10 +219,10 @@ export class SearchConfigurationService implements OnDestroy {
* @param {PaginatedSearchOptions} defaults Default values for when no parameters are available
* @returns {Subscription} The subscription to unsubscribe from
*/
- private subscribeToPaginatedSearchOptions(defaults: PaginatedSearchOptions): Subscription {
+ private subscribeToPaginatedSearchOptions(paginationId: string, defaults: PaginatedSearchOptions): Subscription {
return observableMerge(
- this.getPaginationPart(defaults.pagination),
- this.getSortPart(defaults.sort),
+ this.getPaginationPart(paginationId, defaults.pagination),
+ this.getSortPart(paginationId, defaults.sort),
this.getConfigurationPart(defaults.configuration),
this.getScopePart(defaults.scope),
this.getQueryPart(defaults.query),
@@ -317,8 +302,8 @@ export class SearchConfigurationService implements OnDestroy {
/**
* @returns {Observable} Emits the current pagination settings as a partial SearchOptions object
*/
- private getPaginationPart(defaultPagination: PaginationComponentOptions): Observable {
- return this.getCurrentPagination(defaultPagination).pipe(map((pagination) => {
+ private getPaginationPart(paginationId: string, defaultPagination: PaginationComponentOptions): Observable {
+ return this.getCurrentPagination(paginationId, defaultPagination).pipe(map((pagination) => {
return { pagination };
}));
}
@@ -326,8 +311,8 @@ export class SearchConfigurationService implements OnDestroy {
/**
* @returns {Observable} Emits the current sorting settings as a partial SearchOptions object
*/
- private getSortPart(defaultSort: SortOptions): Observable {
- return this.getCurrentSort(defaultSort).pipe(map((sort) => {
+ private getSortPart(paginationId: string, defaultSort: SortOptions): Observable {
+ return this.getCurrentSort(paginationId, defaultSort).pipe(map((sort) => {
return { sort };
}));
}
diff --git a/src/app/core/shared/search/search.service.spec.ts b/src/app/core/shared/search/search.service.spec.ts
index 06208094bd..60cb0a87b9 100644
--- a/src/app/core/shared/search/search.service.spec.ts
+++ b/src/app/core/shared/search/search.service.spec.ts
@@ -22,6 +22,12 @@ import { routeServiceStub } from '../../../shared/testing/route-service.stub';
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
import { SearchObjects } from '../../../shared/search/search-objects.model';
+import { PaginationService } from '../../pagination/pagination.service';
+import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../../cache/models/sort-options.model';
+import { FindListOptions } from '../../data/request.models';
+import { SearchConfigurationService } from './search-configuration.service';
+import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
@Component({ template: '' })
class DummyComponent {
@@ -32,6 +38,7 @@ describe('SearchService', () => {
let searchService: SearchService;
const router = new RouterStub();
const route = new ActivatedRouteStub();
+ const searchConfigService = {paginationID: 'page-id'};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
@@ -51,6 +58,8 @@ describe('SearchService', () => {
{ provide: HALEndpointService, useValue: {} },
{ provide: CommunityDataService, useValue: {} },
{ provide: DSpaceObjectDataService, useValue: {} },
+ { provide: PaginationService, useValue: {} },
+ { provide: SearchConfigurationService, useValue: searchConfigService },
SearchService
],
});
@@ -94,6 +103,9 @@ describe('SearchService', () => {
}
};
+ const paginationService = new PaginationServiceStub();
+ const searchConfigService = {paginationID: 'page-id'};
+
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
@@ -113,6 +125,8 @@ describe('SearchService', () => {
{ provide: HALEndpointService, useValue: halService },
{ provide: CommunityDataService, useValue: {} },
{ provide: DSpaceObjectDataService, useValue: {} },
+ { provide: PaginationService, useValue: paginationService },
+ { provide: SearchConfigurationService, useValue: searchConfigService },
SearchService
],
});
@@ -124,18 +138,14 @@ describe('SearchService', () => {
it('should call the navigate method on the Router with view mode list parameter as a parameter when setViewMode is called', () => {
searchService.setViewMode(ViewMode.ListElement);
- expect(router.navigate).toHaveBeenCalledWith(['/search'], {
- queryParams: { view: ViewMode.ListElement, page: 1 },
- queryParamsHandling: 'merge'
- });
+ expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('page-id', ['/search'], {page: 1}, { view: ViewMode.ListElement }
+ );
});
it('should call the navigate method on the Router with view mode grid parameter as a parameter when setViewMode is called', () => {
searchService.setViewMode(ViewMode.GridElement);
- expect(router.navigate).toHaveBeenCalledWith(['/search'], {
- queryParams: { view: ViewMode.GridElement, page: 1 },
- queryParamsHandling: 'merge'
- });
+ expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('page-id', ['/search'], {page: 1}, { view: ViewMode.GridElement }
+ );
});
it('should return ViewMode.List when the viewMode is set to ViewMode.List in the ActivatedRoute', () => {
diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts
index b380a70d44..054bde4c08 100644
--- a/src/app/core/shared/search/search.service.ts
+++ b/src/app/core/shared/search/search.service.ts
@@ -1,7 +1,7 @@
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
import { Injectable, OnDestroy } from '@angular/core';
-import { NavigationExtras, Router } from '@angular/router';
-import { first, map, switchMap, take } from 'rxjs/operators';
+import { Router } from '@angular/router';
+import { map, switchMap, take } from 'rxjs/operators';
import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { LinkService } from '../../cache/builders/link.service';
import { PaginatedList } from '../../data/paginated-list.model';
@@ -37,6 +37,9 @@ import { ListableObject } from '../../../shared/object-collection/shared/listabl
import { getSearchResultFor } from '../../../shared/search/search-result-element-decorator';
import { FacetConfigResponse } from '../../../shared/search/facet-config-response.model';
import { FacetValues } from '../../../shared/search/facet-values.model';
+import { PaginationService } from '../../pagination/pagination.service';
+import { SearchConfigurationService } from './search-configuration.service';
+import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
/**
* Service that performs all general actions that have to do with the search page
@@ -75,7 +78,9 @@ export class SearchService implements OnDestroy {
private linkService: LinkService,
private halService: HALEndpointService,
private communityService: CommunityDataService,
- private dspaceObjectService: DSpaceObjectDataService
+ private dspaceObjectService: DSpaceObjectDataService,
+ private paginationService: PaginationService,
+ private searchConfigurationService: SearchConfigurationService
) {
}
@@ -380,20 +385,16 @@ export class SearchService implements OnDestroy {
* @param {ViewMode} viewMode Mode to switch to
*/
setViewMode(viewMode: ViewMode, searchLinkParts?: string[]) {
- this.routeService.getQueryParameterValue('pageSize').pipe(first())
- .subscribe((pageSize) => {
- let queryParams = { view: viewMode, page: 1 };
+ this.paginationService.getCurrentPagination(this.searchConfigurationService.paginationID, new PaginationComponentOptions()).pipe(take(1))
+ .subscribe((config) => {
+ let pageParams = { page: 1 };
+ const queryParams = { view: viewMode };
if (viewMode === ViewMode.DetailedListElement) {
- queryParams = Object.assign(queryParams, {pageSize: '1'});
- } else if (pageSize === '1') {
- queryParams = Object.assign(queryParams, {pageSize: '10'});
+ pageParams = Object.assign(pageParams, {pageSize: 1});
+ } else if (config.pageSize === 1) {
+ pageParams = Object.assign(pageParams, {pageSize: 10});
}
- const navigationExtras: NavigationExtras = {
- queryParams: queryParams,
- queryParamsHandling: 'merge'
- };
-
- this.router.navigate(hasValue(searchLinkParts) ? searchLinkParts : [this.getSearchLink()], navigationExtras);
+ this.paginationService.updateRouteWithUrl(this.searchConfigurationService.paginationID, hasValue(searchLinkParts) ? searchLinkParts : [this.getSearchLink()], pageParams, queryParams);
});
}
diff --git a/src/app/process-page/overview/process-overview.component.html b/src/app/process-page/overview/process-overview.component.html
index 30eb44430e..62b1433b2c 100644
--- a/src/app/process-page/overview/process-overview.component.html
+++ b/src/app/process-page/overview/process-overview.component.html
@@ -8,8 +8,7 @@
[pageInfoState]="(processesRD$ | async)?.payload"
[collectionSize]="(processesRD$ | async)?.payload?.totalElements"
[hideGear]="true"
- [hidePagerWhenSinglePage]="true"
- (pageChange)="onPageChange($event)">
+ [hidePagerWhenSinglePage]="true">
diff --git a/src/app/process-page/overview/process-overview.component.spec.ts b/src/app/process-page/overview/process-overview.component.spec.ts
index ffbcbb10cf..98e78f6b36 100644
--- a/src/app/process-page/overview/process-overview.component.spec.ts
+++ b/src/app/process-page/overview/process-overview.component.spec.ts
@@ -12,6 +12,12 @@ import { By } from '@angular/platform-browser';
import { ProcessStatus } from '../processes/process-status.model';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { createPaginatedList } from '../../shared/testing/utils.test';
+import { of as observableOf } from 'rxjs';
+import { PaginationService } from '../../core/pagination/pagination.service';
+import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
+import { FindListOptions } from '../../core/data/request.models';
+import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
describe('ProcessOverviewComponent', () => {
let component: ProcessOverviewComponent;
@@ -19,6 +25,7 @@ describe('ProcessOverviewComponent', () => {
let processService: ProcessDataService;
let ePersonService: EPersonDataService;
+ let paginationService;
let processes: Process[];
let ePerson: EPerson;
@@ -69,6 +76,8 @@ describe('ProcessOverviewComponent', () => {
ePersonService = jasmine.createSpyObj('ePersonService', {
findById: createSuccessfulRemoteDataObject$(ePerson)
});
+
+ paginationService = new PaginationServiceStub();
}
beforeEach(waitForAsync(() => {
@@ -78,7 +87,8 @@ describe('ProcessOverviewComponent', () => {
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([])],
providers: [
{ provide: ProcessDataService, useValue: processService },
- { provide: EPersonDataService, useValue: ePersonService }
+ { provide: EPersonDataService, useValue: ePersonService },
+ { provide: PaginationService, useValue: paginationService }
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
@@ -143,16 +153,4 @@ describe('ProcessOverviewComponent', () => {
});
});
});
-
- describe('onPageChange', () => {
- const toPage = 2;
-
- beforeEach(() => {
- component.onPageChange(toPage);
- });
-
- it('should call a new findAll with the corresponding page', () => {
- expect(processService.findAll).toHaveBeenCalledWith(jasmine.objectContaining({ currentPage: toPage }));
- });
- });
});
diff --git a/src/app/process-page/overview/process-overview.component.ts b/src/app/process-page/overview/process-overview.component.ts
index 541d6c212e..03fcf27222 100644
--- a/src/app/process-page/overview/process-overview.component.ts
+++ b/src/app/process-page/overview/process-overview.component.ts
@@ -8,8 +8,9 @@ import { FindListOptions } from '../../core/data/request.models';
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
import { EPerson } from '../../core/eperson/models/eperson.model';
-import { map } from 'rxjs/operators';
+import { map, switchMap } from 'rxjs/operators';
import { ProcessDataService } from '../../core/data/processes/process-data.service';
+import { PaginationService } from '../../core/pagination/pagination.service';
@Component({
selector: 'ds-process-overview',
@@ -36,7 +37,7 @@ export class ProcessOverviewComponent implements OnInit {
* The current pagination configuration for the page
*/
pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
- id: 'process-overview-pagination',
+ id: 'po',
pageSize: 20
});
@@ -46,6 +47,7 @@ export class ProcessOverviewComponent implements OnInit {
dateFormat = 'yyyy-MM-dd HH:mm:ss';
constructor(protected processService: ProcessDataService,
+ protected paginationService: PaginationService,
protected ePersonService: EPersonDataService) {
}
@@ -53,23 +55,13 @@ export class ProcessOverviewComponent implements OnInit {
this.setProcesses();
}
- /**
- * When the page is changed, make sure to update the list of processes to match the new page
- * @param event The page change event
- */
- onPageChange(event) {
- this.config = Object.assign(new FindListOptions(), this.config, {
- currentPage: event,
- });
- this.pageConfig.currentPage = event;
- this.setProcesses();
- }
-
/**
* Send a request to fetch all processes for the current page
*/
setProcesses() {
- this.processesRD$ = this.processService.findAll(this.config);
+ this.processesRD$ = this.paginationService.getFindListOptions(this.pageConfig.id, this.config).pipe(
+ switchMap((config) => this.processService.findAll(config))
+ );
}
/**
@@ -82,5 +74,8 @@ export class ProcessOverviewComponent implements OnInit {
map((eperson: EPerson) => eperson.name)
);
}
+ ngOnDestroy(): void {
+ this.paginationService.clearPagination(this.pageConfig.id);
+ }
}
diff --git a/src/app/search-navbar/search-navbar.component.spec.ts b/src/app/search-navbar/search-navbar.component.spec.ts
index 9eef81a42e..ba08c7ca75 100644
--- a/src/app/search-navbar/search-navbar.component.spec.ts
+++ b/src/app/search-navbar/search-navbar.component.spec.ts
@@ -8,6 +8,12 @@ import { SearchService } from '../core/shared/search/search.service';
import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock';
import { SearchNavbarComponent } from './search-navbar.component';
+import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
+import { of as observableOf } from 'rxjs';
+import { PaginationService } from '../core/pagination/pagination.service';
+import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
+import { PaginationServiceStub } from '../shared/testing/pagination-service.stub';
describe('SearchNavbarComponent', () => {
let component: SearchNavbarComponent;
@@ -15,6 +21,7 @@ describe('SearchNavbarComponent', () => {
let mockSearchService: any;
let router: Router;
let routerStub;
+ let paginationService;
beforeEach(waitForAsync(() => {
mockSearchService = {
@@ -26,6 +33,9 @@ describe('SearchNavbarComponent', () => {
routerStub = {
navigate: (commands) => commands
};
+
+ paginationService = new PaginationServiceStub();
+
TestBed.configureTestingModule({
imports: [
FormsModule,
@@ -40,7 +50,9 @@ describe('SearchNavbarComponent', () => {
declarations: [SearchNavbarComponent],
providers: [
{ provide: SearchService, useValue: mockSearchService },
- { provide: Router, useValue: routerStub }
+ { provide: PaginationService, useValue: paginationService },
+ { provide: Router, useValue: routerStub },
+ { provide: SearchConfigurationService, useValue: {paginationID: 'page-id'} }
]
})
.compileComponents();
@@ -88,7 +100,7 @@ describe('SearchNavbarComponent', () => {
}));
it('to search page with empty query', () => {
expect(component.onSubmit).toHaveBeenCalledWith({ query: '' });
- expect(router.navigate).toHaveBeenCalled();
+ expect(paginationService.updateRouteWithUrl).toHaveBeenCalled();
});
});
});
@@ -112,7 +124,7 @@ describe('SearchNavbarComponent', () => {
}));
it('to search page with query', async () => {
expect(component.onSubmit).toHaveBeenCalledWith({ query: 'test' });
- expect(router.navigate).toHaveBeenCalled();
+ expect(paginationService.updateRouteWithUrl).toHaveBeenCalled();
});
});
});
diff --git a/src/app/search-navbar/search-navbar.component.ts b/src/app/search-navbar/search-navbar.component.ts
index 1bedfb73ef..1e509a180b 100644
--- a/src/app/search-navbar/search-navbar.component.ts
+++ b/src/app/search-navbar/search-navbar.component.ts
@@ -3,6 +3,8 @@ import { FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { SearchService } from '../core/shared/search/search.service';
import { expandSearchInput } from '../shared/animations/slide';
+import { PaginationService } from '../core/pagination/pagination.service';
+import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
/**
* The search box in the header that expands on focus and collapses on focus out
@@ -24,7 +26,9 @@ export class SearchNavbarComponent {
// Search input field
@ViewChild('searchInput') searchField: ElementRef;
- constructor(private formBuilder: FormBuilder, private router: Router, private searchService: SearchService) {
+ constructor(private formBuilder: FormBuilder, private router: Router, private searchService: SearchService,
+ private paginationService: PaginationService,
+ private searchConfig: SearchConfigurationService) {
this.searchForm = this.formBuilder.group(({
query: '',
}));
@@ -63,9 +67,6 @@ export class SearchNavbarComponent {
this.collapse();
const linkToNavigateTo = this.searchService.getSearchLink().split('/');
this.searchForm.reset();
- this.router.navigate(linkToNavigateTo, {
- queryParams: Object.assign({}, { page: 1 }, data),
- queryParamsHandling: 'merge'
- });
+ this.paginationService.updateRouteWithUrl(this.searchConfig.paginationID, linkToNavigateTo, {page: 1}, data);
}
}
diff --git a/src/app/shared/browse-by/browse-by.component.spec.ts b/src/app/shared/browse-by/browse-by.component.spec.ts
index 9f3ceade1a..806f4bdb6f 100644
--- a/src/app/shared/browse-by/browse-by.component.spec.ts
+++ b/src/app/shared/browse-by/browse-by.component.spec.ts
@@ -18,6 +18,9 @@ import { PaginationComponentOptions } from '../pagination/pagination-component-o
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
import { storeModuleConfig } from '../../app.reducer';
+import { FindListOptions } from '../../core/data/request.models';
+import { PaginationService } from '../../core/pagination/pagination.service';
+import { PaginationServiceStub } from '../testing/pagination-service.stub';
describe('BrowseByComponent', () => {
let comp: BrowseByComponent;
@@ -45,6 +48,14 @@ describe('BrowseByComponent', () => {
];
const mockItemsRD$ = createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), mockItems));
+ const paginationConfig = Object.assign(new PaginationComponentOptions(), {
+ id: 'test-pagination',
+ currentPage: 1,
+ pageSizeOptions: [5, 10, 15, 20],
+ pageSize: 15
+ });
+ const paginationService = new PaginationServiceStub(paginationConfig);
+
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
@@ -63,7 +74,9 @@ describe('BrowseByComponent', () => {
BrowserAnimationsModule
],
declarations: [],
- providers: [],
+ providers: [
+ {provide: PaginationService, useValue: paginationService}
+ ],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
@@ -95,12 +108,8 @@ describe('BrowseByComponent', () => {
beforeEach(() => {
comp.enableArrows = true;
comp.objects$ = mockItemsRD$;
- comp.paginationConfig = Object.assign(new PaginationComponentOptions(), {
- id: 'test-pagination',
- currentPage: 1,
- pageSizeOptions: [5, 10, 15, 20],
- pageSize: 15
- });
+
+ comp.paginationConfig = paginationConfig;
comp.sortConfig = Object.assign(new SortOptions('dc.title', SortDirection.ASC));
fixture.detectChanges();
});
@@ -136,8 +145,8 @@ describe('BrowseByComponent', () => {
fixture.detectChanges();
});
- it('should emit a signal to the EventEmitter', () => {
- expect(comp.pageSizeChange.emit).toHaveBeenCalled();
+ it('should call the updateRoute method from the paginationService', () => {
+ expect(paginationService.updateRoute).toHaveBeenCalledWith('test-pagination', {pageSize: paginationConfig.pageSizeOptions[0]});
});
});
@@ -148,8 +157,8 @@ describe('BrowseByComponent', () => {
fixture.detectChanges();
});
- it('should emit a signal to the EventEmitter', () => {
- expect(comp.sortDirectionChange.emit).toHaveBeenCalled();
+ it('should call the updateRoute method from the paginationService', () => {
+ expect(paginationService.updateRoute).toHaveBeenCalledWith('test-pagination', {sortDirection: 'ASC'});
});
});
});
diff --git a/src/app/shared/browse-by/browse-by.component.ts b/src/app/shared/browse-by/browse-by.component.ts
index 8706c39e54..1f05ad2258 100644
--- a/src/app/shared/browse-by/browse-by.component.ts
+++ b/src/app/shared/browse-by/browse-by.component.ts
@@ -7,6 +7,7 @@ import { fadeIn, fadeInOut } from '../animations/fade';
import { Observable } from 'rxjs';
import { ListableObject } from '../object-collection/shared/listable-object.model';
import { getStartsWithComponent, StartsWithType } from '../starts-with/starts-with-decorator';
+import { PaginationService } from '../../core/pagination/pagination.service';
@Component({
selector: 'ds-browse-by',
@@ -96,7 +97,9 @@ export class BrowseByComponent implements OnInit {
*/
public sortDirections = SortDirection;
- public constructor(private injector: Injector) {
+ public constructor(private injector: Injector,
+ protected paginationService: PaginationService,
+ ) {
}
@@ -119,8 +122,7 @@ export class BrowseByComponent implements OnInit {
* @param size
*/
doPageSizeChange(size) {
- this.paginationConfig.pageSize = size;
- this.pageSizeChange.emit(size);
+ this.paginationService.updateRoute(this.paginationConfig.id,{pageSize: size});
}
/**
@@ -128,8 +130,7 @@ export class BrowseByComponent implements OnInit {
* @param direction
*/
doSortDirectionChange(direction) {
- this.sortConfig.direction = direction;
- this.sortDirectionChange.emit(direction);
+ this.paginationService.updateRoute(this.paginationConfig.id,{sortDirection: direction});
}
/**
@@ -141,7 +142,10 @@ export class BrowseByComponent implements OnInit {
ngOnInit(): void {
this.objectInjector = Injector.create({
- providers: [{ provide: 'startsWithOptions', useFactory: () => (this.startsWithOptions), deps:[] }],
+ providers: [
+ { provide: 'startsWithOptions', useFactory: () => (this.startsWithOptions), deps:[] },
+ { provide: 'paginationId', useFactory: () => (this.paginationConfig?.id), deps:[] }
+ ],
parent: this.injector
});
}
diff --git a/src/app/shared/item/item-versions/item-versions.component.html b/src/app/shared/item/item-versions/item-versions.component.html
index 44fc2a1023..34764e7925 100644
--- a/src/app/shared/item/item-versions/item-versions.component.html
+++ b/src/app/shared/item/item-versions/item-versions.component.html
@@ -8,8 +8,7 @@
[paginationOptions]="options"
[pageInfoState]="versions"
[collectionSize]="versions?.totalElements"
- [disableRouteParameterUpdate]="true"
- (pageChange)="switchPage($event)">
+ [retainScrollPosition]="true">
diff --git a/src/app/shared/item/item-versions/item-versions.component.spec.ts b/src/app/shared/item/item-versions/item-versions.component.spec.ts
index f35ec1993d..cc28779537 100644
--- a/src/app/shared/item/item-versions/item-versions.component.spec.ts
+++ b/src/app/shared/item/item-versions/item-versions.component.spec.ts
@@ -11,6 +11,11 @@ import { VersionHistoryDataService } from '../../../core/data/version-history-da
import { By } from '@angular/platform-browser';
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
import { createPaginatedList } from '../../testing/utils.test';
+import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
+import { of as observableOf } from 'rxjs';
+import { PaginationService } from '../../../core/pagination/pagination.service';
+import { PaginationServiceStub } from '../../testing/pagination-service.stub';
describe('ItemVersionsComponent', () => {
let component: ItemVersionsComponent;
@@ -52,12 +57,15 @@ describe('ItemVersionsComponent', () => {
getVersions: createSuccessfulRemoteDataObject$(createPaginatedList(versions))
});
+ const paginationService = new PaginationServiceStub();
+
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ItemVersionsComponent, VarDirective],
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([])],
providers: [
- { provide: VersionHistoryDataService, useValue: versionHistoryService }
+ { provide: VersionHistoryDataService, useValue: versionHistoryService },
+ { provide: PaginationService, useValue: paginationService }
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
@@ -107,16 +115,4 @@ describe('ItemVersionsComponent', () => {
expect(summary.nativeElement.textContent).toEqual(version.summary);
});
});
-
- describe('switchPage', () => {
- const page = 5;
-
- beforeEach(() => {
- component.switchPage(page);
- });
-
- it('should set the option\'s currentPage to the new page', () => {
- expect(component.options.currentPage).toEqual(page);
- });
- });
});
diff --git a/src/app/shared/item/item-versions/item-versions.component.ts b/src/app/shared/item/item-versions/item-versions.component.ts
index d25fdaa4f1..752cd55b5b 100644
--- a/src/app/shared/item/item-versions/item-versions.component.ts
+++ b/src/app/shared/item/item-versions/item-versions.component.ts
@@ -17,6 +17,7 @@ import { PaginatedSearchOptions } from '../../search/paginated-search-options.mo
import { AlertType } from '../../alert/aletr-type';
import { followLink } from '../../utils/follow-link-config.model';
import { hasValueOperator } from '../../empty.util';
+import { PaginationService } from '../../../core/pagination/pagination.service';
import { getItemPageRoute } from '../../../+item-page/item-page-routing-paths';
@Component({
@@ -81,7 +82,7 @@ export class ItemVersionsComponent implements OnInit {
* Start at page 1 and always use the set page size
*/
options = Object.assign(new PaginationComponentOptions(),{
- id: 'item-versions-options',
+ id: 'ivo',
currentPage: 1,
pageSize: this.pageSize
});
@@ -100,7 +101,9 @@ export class ItemVersionsComponent implements OnInit {
[itemId: string]: string
}>;
- constructor(private versionHistoryService: VersionHistoryDataService) {
+ constructor(private versionHistoryService: VersionHistoryDataService,
+ private paginationService: PaginationService
+ ) {
}
/**
@@ -119,10 +122,11 @@ export class ItemVersionsComponent implements OnInit {
getRemoteDataPayload(),
hasValueOperator(),
);
- this.versionsRD$ = observableCombineLatest(versionHistory$, this.currentPage$).pipe(
- switchMap(([versionHistory, page]: [VersionHistory, number]) =>
+ const currentPagination = this.paginationService.getCurrentPagination(this.options.id, this.options);
+ this.versionsRD$ = observableCombineLatest(versionHistory$, currentPagination).pipe(
+ switchMap(([versionHistory, options]: [VersionHistory, PaginationComponentOptions]) =>
this.versionHistoryService.getVersions(versionHistory.id,
- new PaginatedSearchOptions({pagination: Object.assign({}, this.options, { currentPage: page })}),
+ new PaginatedSearchOptions({pagination: Object.assign({}, options, { currentPage: options.currentPage })}),
true, true, followLink('item'), followLink('eperson')))
);
this.hasEpersons$ = this.versionsRD$.pipe(
@@ -143,13 +147,9 @@ export class ItemVersionsComponent implements OnInit {
);
}
- /**
- * Update the current page
- * @param page
- */
- switchPage(page: number) {
- this.options.currentPage = page;
- this.currentPage$.next(page);
+ ngOnDestroy(): void {
+ this.paginationService.clearPagination(this.options.id);
}
+
}
diff --git a/src/app/shared/object-select/collection-select/collection-select.component.spec.ts b/src/app/shared/object-select/collection-select/collection-select.component.spec.ts
index fd4a239cc4..199bc56647 100644
--- a/src/app/shared/object-select/collection-select/collection-select.component.spec.ts
+++ b/src/app/shared/object-select/collection-select/collection-select.component.spec.ts
@@ -13,6 +13,11 @@ import { CollectionSelectComponent } from './collection-select.component';
import { Collection } from '../../../core/shared/collection.model';
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
import { createPaginatedList } from '../../testing/utils.test';
+import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
+import { FindListOptions } from '../../../core/data/request.models';
+import { of as observableOf } from 'rxjs';
+import { PaginationService } from '../../../core/pagination/pagination.service';
+import { PaginationServiceStub } from '../../testing/pagination-service.stub';
describe('CollectionSelectComponent', () => {
let comp: CollectionSelectComponent;
@@ -36,13 +41,15 @@ describe('CollectionSelectComponent', () => {
currentPage: 1
});
+ const paginationService = new PaginationServiceStub();
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), SharedModule, RouterTestingModule.withRoutes([])],
declarations: [],
providers: [
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub([mockCollectionList[1].id]) },
- { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }
+ { provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
+ { provide: PaginationService, useValue: paginationService }
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
diff --git a/src/app/shared/object-select/item-select/item-select.component.spec.ts b/src/app/shared/object-select/item-select/item-select.component.spec.ts
index f99991f391..224fb764b6 100644
--- a/src/app/shared/object-select/item-select/item-select.component.spec.ts
+++ b/src/app/shared/object-select/item-select/item-select.component.spec.ts
@@ -11,14 +11,19 @@ import { HostWindowService } from '../../host-window.service';
import { HostWindowServiceStub } from '../../testing/host-window-service.stub';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
-import { of } from 'rxjs';
+import { of as observableOf, of } from 'rxjs';
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
import { createPaginatedList } from '../../testing/utils.test';
+import { PaginationService } from '../../../core/pagination/pagination.service';
+import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
+import { FindListOptions } from '../../../core/data/request.models';
+import { PaginationServiceStub } from '../../testing/pagination-service.stub';
describe('ItemSelectComponent', () => {
let comp: ItemSelectComponent;
let fixture: ComponentFixture;
let objectSelectService: ObjectSelectService;
+ let paginationService;
const mockItemList = [
Object.assign(new Item(), {
@@ -59,13 +64,18 @@ describe('ItemSelectComponent', () => {
currentPage: 1
});
+ paginationService = new PaginationServiceStub(mockPaginationOptions);
+
+
+
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), SharedModule, RouterTestingModule.withRoutes([])],
declarations: [],
providers: [
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub([mockItemList[1].id]) },
- { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }
+ { provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
+ { provide: PaginationService, useValue: paginationService }
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
diff --git a/src/app/shared/page-size-selector/page-size-selector.component.spec.ts b/src/app/shared/page-size-selector/page-size-selector.component.spec.ts
index f75b5841af..77931400a2 100644
--- a/src/app/shared/page-size-selector/page-size-selector.component.spec.ts
+++ b/src/app/shared/page-size-selector/page-size-selector.component.spec.ts
@@ -12,6 +12,8 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options
import { EnumKeysPipe } from '../utils/enum-keys-pipe';
import { VarDirective } from '../utils/var.directive';
import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
+import { PaginationService } from '../../core/pagination/pagination.service';
+import { PaginationServiceStub } from '../testing/pagination-service.stub';
describe('PageSizeSelectorComponent', () => {
@@ -33,6 +35,8 @@ describe('PageSizeSelectorComponent', () => {
sort
};
+ const paginationService = new PaginationServiceStub(pagination, sort);
+
const activatedRouteStub = {
queryParams: observableOf({
query: queryParam,
@@ -46,6 +50,7 @@ describe('PageSizeSelectorComponent', () => {
declarations: [PageSizeSelectorComponent, EnumKeysPipe, VarDirective],
providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub },
+ { provide: PaginationService, useValue: paginationService },
{
provide: SEARCH_CONFIG_SERVICE,
useValue: {
diff --git a/src/app/shared/page-size-selector/page-size-selector.component.ts b/src/app/shared/page-size-selector/page-size-selector.component.ts
index dfea7d423f..764a8063db 100644
--- a/src/app/shared/page-size-selector/page-size-selector.component.ts
+++ b/src/app/shared/page-size-selector/page-size-selector.component.ts
@@ -1,11 +1,12 @@
import { Component, Inject, OnInit } from '@angular/core';
import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
import { Observable } from 'rxjs';
-import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
import { PaginatedSearchOptions } from '../search/paginated-search-options.model';
-import { map } from 'rxjs/operators';
+import { map, take } from 'rxjs/operators';
+import { PaginationService } from '../../core/pagination/pagination.service';
@Component({
selector: 'ds-page-size-selector',
@@ -22,8 +23,10 @@ export class PageSizeSelectorComponent implements OnInit {
*/
paginationOptions$: Observable;
+
constructor(private route: ActivatedRoute,
private router: Router,
+ private paginationService: PaginationService,
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigurationService: SearchConfigurationService) {
}
@@ -40,13 +43,10 @@ export class PageSizeSelectorComponent implements OnInit {
*/
reloadRPP(event: Event) {
const value = (event.target as HTMLInputElement).value;
- const navigationExtras: NavigationExtras = {
- queryParams: {
- pageSize: value,
- page: 1
- },
- queryParamsHandling: 'merge'
- };
- this.router.navigate([], navigationExtras);
+ this.paginationOptions$.pipe(
+ take(1)
+ ).subscribe((pagination: PaginationComponentOptions) => {
+ this.paginationService.updateRoute(pagination.id, {page: 1, pageSize: +value});
+ }) ;
}
}
diff --git a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts
index 905b739270..0c6cd80a62 100644
--- a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts
+++ b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.spec.ts
@@ -11,6 +11,10 @@ import { PaginationComponent } from '../pagination/pagination.component';
import { createSuccessfulRemoteDataObject } from '../remote-data.utils';
import { createPaginatedList } from '../testing/utils.test';
import { ObjectValuesPipe } from '../utils/object-values-pipe';
+import { PaginationService } from '../../core/pagination/pagination.service';
+import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
+import { PaginationServiceStub } from '../testing/pagination-service.stub';
@Component({
selector: 'ds-mock-paginated-drag-drop-abstract',
@@ -22,8 +26,9 @@ class MockAbstractPaginatedDragAndDropListComponent extends AbstractPaginatedDra
protected elRef: ElementRef,
protected objectValuesPipe: ObjectValuesPipe,
protected mockUrl: string,
- protected mockObjectsRD$: Observable>>) {
- super(objectUpdatesService, elRef, objectValuesPipe);
+ protected paginationService: PaginationService,
+ protected mockObjectsRD$: Observable>>) {
+ super(objectUpdatesService, elRef, objectValuesPipe, paginationService);
}
initializeObjectsRD(): void {
@@ -43,10 +48,12 @@ describe('AbstractPaginatedDragAndDropListComponent', () => {
const url = 'mock-abstract-paginated-drag-and-drop-list-component';
+
const object1 = Object.assign(new DSpaceObject(), { uuid: 'object-1' });
const object2 = Object.assign(new DSpaceObject(), { uuid: 'object-2' });
const objectsRD = createSuccessfulRemoteDataObject(createPaginatedList([object1, object2]));
let objectsRD$: BehaviorSubject>>;
+ let paginationService;
const updates = {
[object1.uuid]: { field: object1, changeType: undefined },
@@ -69,8 +76,9 @@ describe('AbstractPaginatedDragAndDropListComponent', () => {
paginationComponent = jasmine.createSpyObj('paginationComponent', {
doPageChange: {}
});
+ paginationService = new PaginationServiceStub();
objectsRD$ = new BehaviorSubject(objectsRD);
- component = new MockAbstractPaginatedDragAndDropListComponent(objectUpdatesService, elRef, objectValuesPipe, url, objectsRD$);
+ component = new MockAbstractPaginatedDragAndDropListComponent(objectUpdatesService, elRef, objectValuesPipe, url, paginationService, objectsRD$);
component.paginationComponent = paginationComponent;
component.ngOnInit();
});
@@ -86,18 +94,6 @@ describe('AbstractPaginatedDragAndDropListComponent', () => {
});
});
- describe('switchPage', () => {
- const page = 3;
-
- beforeEach(() => {
- component.switchPage(page);
- });
-
- it('should set currentPage$ to the new page', () => {
- expect(component.currentPage$.value).toEqual(page);
- });
- });
-
describe('drop', () => {
const event = {
previousIndex: 0,
@@ -117,7 +113,7 @@ describe('AbstractPaginatedDragAndDropListComponent', () => {
it('should send out a dropObject event with the expected processed paginated indexes', () => {
expect(component.dropObject.emit).toHaveBeenCalledWith(Object.assign({
- fromIndex: ((component.currentPage$.value - 1) * component.pageSize) + event.previousIndex,
+ fromIndex: ((component.currentPage$.value.currentPage - 1) * component.pageSize) + event.previousIndex,
toIndex: ((hoverPage - 1) * component.pageSize),
finish: jasmine.anything()
}));
diff --git a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts
index 4cc647d091..f9b9ff8e90 100644
--- a/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts
+++ b/src/app/shared/pagination-drag-and-drop/abstract-paginated-drag-and-drop-list.component.ts
@@ -17,6 +17,7 @@ import { Component, ElementRef, EventEmitter, OnDestroy, Output, ViewChild } fro
import { PaginationComponent } from '../pagination/pagination.component';
import { ObjectValuesPipe } from '../utils/object-values-pipe';
import { compareArraysUsing } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
+import { PaginationService } from '../../core/pagination/pagination.service';
/**
* Operator used for comparing {@link FieldUpdate}s by their field's UUID
@@ -88,7 +89,7 @@ export abstract class AbstractPaginatedDragAndDropListComponent(1);
+ currentPage$ = new BehaviorSubject(this.options);
/**
* Whether or not we should display a loading animation
@@ -113,7 +114,9 @@ export abstract class AbstractPaginatedDragAndDropListComponent {
+ this.currentPage$.next(currentPagination);
+ });
+ }
+
/**
* Initialize the field-updates in the store
*/
@@ -164,14 +177,6 @@ export abstract class AbstractPaginatedDragAndDropListComponent) {
const dragIndex = event.previousIndex;
let dropIndex = event.currentIndex;
- const dragPage = this.currentPage$.value - 1;
- let dropPage = this.currentPage$.value - 1;
+ const dragPage = this.currentPage$.value.currentPage - 1;
+ let dropPage = this.currentPage$.value.currentPage - 1;
// Check if the user is hovering over any of the pagination's pages at the time of dropping the object
const droppedOnElement = this.elRef.nativeElement.querySelector('.page-item:hover');
@@ -228,5 +233,6 @@ export abstract class AbstractPaginatedDragAndDropListComponent hasValue(sub)).forEach((sub) => sub.unsubscribe());
+ this.paginationService.clearPagination(this.options.id);
}
}
diff --git a/src/app/shared/pagination/pagination.component.html b/src/app/shared/pagination/pagination.component.html
index 6c67b58a90..5f002e55d3 100644
--- a/src/app/shared/pagination/pagination.component.html
+++ b/src/app/shared/pagination/pagination.component.html
@@ -1,18 +1,18 @@
-
+
0) || !hideGear" class="pagination-masked clearfix top">
0" class="col-auto pagination-info">
{{ 'pagination.showing.label' | translate }}
- {{ 'pagination.showing.detail' | translate:getShowingDetails(collectionSize)}}
+ {{ 'pagination.showing.detail' | translate:(getShowingDetails(collectionSize)|async)}}
@@ -20,15 +20,15 @@
-