41914: implemented pagination sort direction/field

This commit is contained in:
Lotte Hofstede
2017-06-15 13:32:15 +02:00
parent c63bd7c2ef
commit 6db165b7a3
9 changed files with 342 additions and 211 deletions

View File

@@ -36,6 +36,7 @@
}, },
"pagination": { "pagination": {
"results-per-page": "Results Per Page", "results-per-page": "Results Per Page",
"sort-direction": "Sort Options",
"showing": { "showing": {
"label": "Now showing items ", "label": "Now showing items ",
"detail": "{{ range }} of {{ total }}" "detail": "{{ range }} of {{ total }}"

View File

@@ -46,7 +46,11 @@ export abstract class DataService<TNormalized extends CacheableObject, TDomain>
} }
if (hasValue(options.sort)) { if (hasValue(options.sort)) {
args.push(`sort=${options.sort.field},${options.sort.direction}`); let direction = 'asc';
if (options.sort.direction === 1) {
direction = 'desc';
}
args.push(`sort=${options.sort.field},${direction}`);
} }
if (isNotEmpty(args)) { if (isNotEmpty(args)) {

View File

@@ -1,7 +1,9 @@
<div *ngIf="topLevelCommunities.hasSucceeded | async"> <div *ngIf="topLevelCommunities.hasSucceeded | async">
<h2>{{'home.top-level-communities.head' | translate}}</h2> <h2>{{'home.top-level-communities.head' | translate}}</h2>
<p class="lead">{{'home.top-level-communities.help' | translate}}</p> <p class="lead">{{'home.top-level-communities.help' | translate}}</p>
<ds-object-list [config]="config" [objects]="topLevelCommunities" <ds-object-list [config]="config" [sortConfig]="sortConfig" [objects]="topLevelCommunities"
(pageChange)="onPageChange($event)" (pageChange)="onPageChange($event)"
(pageSizeChange)="onPageSizeChange($event)"></ds-object-list> (pageSizeChange)="onPageSizeChange($event)"
(sortDirectionChange)="onSortDirectionChange($event)"
(sortFieldChange)="onSortDirectionChange($event)"></ds-object-list>
</div> </div>

View File

@@ -13,6 +13,7 @@ import { SortOptions } from "../../core/cache/models/sort-options.model";
export class TopLevelCommunityListComponent implements OnInit { export class TopLevelCommunityListComponent implements OnInit {
topLevelCommunities: RemoteData<Item[]>; topLevelCommunities: RemoteData<Item[]>;
config : PaginationComponentOptions; config : PaginationComponentOptions;
sortConfig : SortOptions;
constructor( constructor(
private cds: ItemDataService private cds: ItemDataService
@@ -29,6 +30,8 @@ export class TopLevelCommunityListComponent implements OnInit {
this.config = new PaginationComponentOptions(); this.config = new PaginationComponentOptions();
this.config.id = "top-level-pagination" this.config.id = "top-level-pagination"
this.config.pageSizeOptions = [ 5, 10, 20, 40, 60, 80, 100 ]; this.config.pageSizeOptions = [ 5, 10, 20, 40, 60, 80, 100 ];
this.sortConfig = new SortOptions();
} }
onPageChange(currentPage): void { onPageChange(currentPage): void {
@@ -41,7 +44,17 @@ export class TopLevelCommunityListComponent implements OnInit {
this.updateResults(); this.updateResults();
} }
onSortDirectionChange(sortDirection): void {
this.sortConfig.direction = sortDirection;
this.updateResults();
}
onSortFieldChange(field): void {
this.sortConfig.field = field;
this.updateResults();
}
updateResults() { updateResults() {
this.topLevelCommunities = this.cds.findAll({ currentPage: this.config.currentPage, elementsPerPage: this.config.pageSize }); this.topLevelCommunities = this.cds.findAll({ currentPage: this.config.currentPage, elementsPerPage: this.config.pageSize, sort: this.sortConfig });
} }
} }

View File

@@ -1,7 +1,10 @@
<ds-pagination [paginationOptions]="config" <ds-pagination [paginationOptions]="config"
[collectionSize]="(pageInfo | async)?.totalElements" [collectionSize]="(pageInfo | async)?.totalElements"
[sortOptions]="sortConfig"
(pageChange)="onPageChange($event)" (pageChange)="onPageChange($event)"
(pageSizeChange)="onPageSizeChange($event)"> (pageSizeChange)="onPageSizeChange($event)"
(sortDirectionChange)="onSortDirectionChange($event)"
(sortFieldChange)="onSortDirectionChange($event)">
<ul *ngIf="objects.hasSucceeded | async"> <ul *ngIf="objects.hasSucceeded | async">
<li *ngFor="let object of (objects.payload | async) | paginate: { itemsPerPage: (pageInfo | async)?.elementsPerPage, currentPage: (pageInfo | async)?.currentPage, totalItems: (pageInfo | async)?.totalElements }"> <li *ngFor="let object of (objects.payload | async) | paginate: { itemsPerPage: (pageInfo | async)?.elementsPerPage, currentPage: (pageInfo | async)?.currentPage, totalItems: (pageInfo | async)?.totalElements }">
<ds-object-list-element [object]="object"></ds-object-list-element> <ds-object-list-element [object]="object"></ds-object-list-element>

View File

@@ -8,6 +8,7 @@ import { PageInfo } from "../core/shared/page-info.model";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { PaginationComponentOptions } from "../shared/pagination/pagination-component-options.model"; import { PaginationComponentOptions } from "../shared/pagination/pagination-component-options.model";
import { EventEmitter } from "@angular/common/src/facade/async"; import { EventEmitter } from "@angular/common/src/facade/async";
import { SortOptions } from "../core/cache/models/sort-options.model";
@Component({ @Component({
@@ -21,10 +22,13 @@ export class ObjectListComponent implements OnInit {
@Input() objects: RemoteData<DSpaceObject[]>; @Input() objects: RemoteData<DSpaceObject[]>;
@Input() config : PaginationComponentOptions; @Input() config : PaginationComponentOptions;
@Input() sortConfig : SortOptions;
pageInfo : Observable<PageInfo>; pageInfo : Observable<PageInfo>;
@Output() pageChange = new EventEmitter(); @Output() pageChange = new EventEmitter();
@Output() pageSizeChange = new EventEmitter(); @Output() pageSizeChange = new EventEmitter();
@Output() sortDirectionChange = new EventEmitter();
@Output() sortFieldChange = new EventEmitter();
data: any = {}; data: any = {};
constructor() { constructor() {
@@ -45,4 +49,13 @@ export class ObjectListComponent implements OnInit {
onPageSizeChange(event) { onPageSizeChange(event) {
this.pageSizeChange.emit(event); this.pageSizeChange.emit(event);
} }
onSortDirectionChange(event) {
this.sortDirectionChange.emit(event);
}
onSortFieldChange(event) {
this.sortFieldChange.emit(event);
}
} }

View File

@@ -9,7 +9,9 @@
<button class="btn btn-outline-primary" id="paginationControls" (click)="$event.stopPropagation(); (paginationControls.isOpen())?paginationControls.close():paginationControls.open();"><i class="fa fa-cog" aria-hidden="true"></i></button> <button class="btn btn-outline-primary" id="paginationControls" (click)="$event.stopPropagation(); (paginationControls.isOpen())?paginationControls.close():paginationControls.open();"><i class="fa fa-cog" aria-hidden="true"></i></button>
<div class="dropdown-menu dropdown-menu-right" id="paginationControlsDropdownMenu" aria-labelledby="paginationControls"> <div class="dropdown-menu dropdown-menu-right" id="paginationControlsDropdownMenu" aria-labelledby="paginationControls">
<h6 class="dropdown-header">{{ 'pagination.results-per-page' | translate}}</h6> <h6 class="dropdown-header">{{ 'pagination.results-per-page' | translate}}</h6>
<button class="dropdown-item" style="padding-left: 20px" *ngFor="let item of pageSizeOptions " (click)="setPageSize(item)"><i class="fa fa-check {{(item != paginationOptions.pageSize) ? 'invisible' : ''}}" aria-hidden="true"></i> {{item}} </button> <button class="dropdown-item" style="padding-left: 20px" *ngFor="let item of pageSizeOptions" (click)="setPageSize(item)"><i class="fa fa-check {{(item != paginationOptions.pageSize) ? 'invisible' : ''}}" aria-hidden="true"></i> {{item}} </button>
<h6 class="dropdown-header">{{ 'pagination.sort-direction' | translate}}</h6>
<button class="dropdown-item" style="padding-left: 20px" *ngFor="let direction of (sortDirections | dsKeys)" (click)="setSortDirection(direction.key)"><i class="fa fa-check {{(direction.key != sortOptions.direction) ? 'invisible' : ''}}" aria-hidden="true"></i> {{direction.value}} </button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -18,6 +18,7 @@ import { DEFAULT_TEMPLATE, DEFAULT_STYLES } from 'ng2-pagination/dist/template';
import { HostWindowService } from "../host-window.service"; import { HostWindowService } from "../host-window.service";
import { HostWindowState } from "../host-window.reducer"; import { HostWindowState } from "../host-window.reducer";
import { PaginationComponentOptions } from './pagination-component-options.model'; import { PaginationComponentOptions } from './pagination-component-options.model';
import { SortDirection, SortOptions } from "../../core/cache/models/sort-options.model";
/** /**
* The default pagination controls component. * The default pagination controls component.
@@ -41,6 +42,11 @@ export class PaginationComponent implements OnDestroy, OnInit {
*/ */
@Input() paginationOptions: PaginationComponentOptions; @Input() paginationOptions: PaginationComponentOptions;
/**
* Sort configuration for this component.
*/
@Input() sortOptions: SortOptions;
/** /**
* An event fired when the page is changed. * An event fired when the page is changed.
* Event's payload equals to the newly selected page. * Event's payload equals to the newly selected page.
@@ -48,11 +54,23 @@ export class PaginationComponent implements OnDestroy, OnInit {
@Output() pageChange: EventEmitter<number> = new EventEmitter<number>(); @Output() pageChange: EventEmitter<number> = new EventEmitter<number>();
/** /**
* An event fired when the page size is changed. * An event fired when the page wsize is changed.
* Event's payload equals to the newly selected page size. * Event's payload equals to the newly selected page size.
*/ */
@Output() pageSizeChange: EventEmitter<number> = new EventEmitter<number>(); @Output() pageSizeChange: EventEmitter<number> = new EventEmitter<number>();
/**
* An event fired when the sort direction is changed.
* Event's payload equals to the newly selected sort direction.
*/
@Output() sortDirectionChange: EventEmitter<SortDirection> = new EventEmitter<SortDirection>();
/**
* An event fired when the sort field is changed.
* Event's payload equals to the newly selected sort field.
*/
@Output() sortFieldChange: EventEmitter<string> = new EventEmitter<string>();
/** /**
* Current page. * Current page.
*/ */
@@ -84,16 +102,35 @@ export class PaginationComponent implements OnDestroy, OnInit {
*/ */
public pageSize: number = 10; public pageSize: number = 10;
/**
* Declare SortDirection enumeration to use it in the template
*/
public sortDirections = SortDirection
/** /**
* A number array that represents options for a context pagination limit. * A number array that represents options for a context pagination limit.
*/ */
private pageSizeOptions: Array<number>; private pageSizeOptions: Array<number>;
/**
* Direction in which to sort: ascending or descending
*/
public sortDirection: SortDirection = SortDirection.Ascending;
/**
* Name of the field that's used to sort by
*/
public sortField: string = "id";
/** /**
* Local variable, which can be used in the template to access the paginate controls ngbDropdown methods and properties * Local variable, which can be used in the template to access the paginate controls ngbDropdown methods and properties
*/ */
public paginationControls; public paginationControls;
/**
*
*/
/** /**
* Subscriber to observable. * Subscriber to observable.
*/ */
@@ -125,16 +162,19 @@ export class PaginationComponent implements OnDestroy, OnInit {
this.currentPage = this.paginationOptions.currentPage; this.currentPage = this.paginationOptions.currentPage;
this.pageSize = this.paginationOptions.pageSize; this.pageSize = this.paginationOptions.pageSize;
this.pageSizeOptions = this.paginationOptions.pageSizeOptions; this.pageSizeOptions = this.paginationOptions.pageSizeOptions;
this.sortDirection = this.sortOptions.direction;
this.sortField = this.sortOptions.field;
this.routeSubscription = this.route.queryParams this.routeSubscription = this.route.queryParams
.map(queryParams => queryParams) .map(queryParams => queryParams)
.subscribe(queryParams => { .subscribe(queryParams => {
this.currentQueryParams = queryParams; this.currentQueryParams = queryParams;
if(this.id == queryParams['pageId'] if (this.id == queryParams['pageId']
&& (this.paginationOptions.currentPage != queryParams['page'] && (this.paginationOptions.currentPage != queryParams['page']
|| this.paginationOptions.pageSize != queryParams['pageSize']) || this.paginationOptions.pageSize != queryParams['pageSize']
|| this.sortOptions.direction != queryParams['sortDirection']
|| this.sortOptions.field != queryParams['sortField'] )
) { ) {
this.validateParams(queryParams['page'], queryParams['pageSize']); this.validateParams(queryParams['page'], queryParams['pageSize'], queryParams['sortDirection'], queryParams['sortField']);
} }
}); });
this.setShowingDetail(); this.setShowingDetail();
@@ -154,11 +194,9 @@ export class PaginationComponent implements OnDestroy, OnInit {
* @param router * @param router
* Router is a singleton service provided by Angular. * Router is a singleton service provided by Angular.
*/ */
constructor( constructor(private route: ActivatedRoute,
private route: ActivatedRoute,
private router: Router, private router: Router,
public hostWindowService: HostWindowService public hostWindowService: HostWindowService) {
){
} }
/** /**
@@ -168,8 +206,8 @@ export class PaginationComponent implements OnDestroy, OnInit {
* The page being navigated to. * The page being navigated to.
*/ */
public doPageChange(page: number) { public doPageChange(page: number) {
this.router.navigate([], { queryParams: Object.assign({}, this.currentQueryParams, { pageId: this.id, page: page, pageSize: this.pageSize }) });
this.currentPage = page; this.currentPage = page;
this.updateRoute();
this.setShowingDetail(); this.setShowingDetail();
this.pageChange.emit(page); this.pageChange.emit(page);
} }
@@ -181,12 +219,53 @@ export class PaginationComponent implements OnDestroy, OnInit {
* The new page size. * The new page size.
*/ */
public setPageSize(pageSize: number) { public setPageSize(pageSize: number) {
this.router.navigate([], { queryParams: Object.assign({}, this.currentQueryParams, { pageId: this.id, page: this.currentPage, pageSize: pageSize }) });
this.pageSize = pageSize; this.pageSize = pageSize;
this.updateRoute();
this.setShowingDetail(); this.setShowingDetail();
this.pageSizeChange.emit(pageSize); this.pageSizeChange.emit(pageSize);
} }
/**
* Method to set set new sort direction and update route parameters
*
* @param sortDirection
* The new sort direction.
*/
public setSortDirection(sortDirection: SortDirection) {
this.sortDirection = sortDirection;
this.updateRoute();
this.setShowingDetail();
this.sortDirectionChange.emit(sortDirection);
}
/**
* Method to set set new sort field and update route parameters
*
* @param sortField
* The new sort field.
*/
public setSortField(field: string) {
this.sortField = field;
this.updateRoute();
this.setShowingDetail();
this.sortFieldChange.emit(field);
}
/**
* Method to update the route parameters
*/
private updateRoute() {
this.router.navigate([], {
queryParams: Object.assign({}, this.currentQueryParams, {
pageId: this.id,
page: this.currentPage,
pageSize: this.pageSize,
sortDirection: this.sortDirection,
sortField: this.sortField
})
});
}
/** /**
* Method to set pagination details of the current viewed page. * Method to set pagination details of the current viewed page.
*/ */
@@ -215,18 +294,28 @@ export class PaginationComponent implements OnDestroy, OnInit {
* @param pageSize * @param pageSize
* The page size to validate * The page size to validate
*/ */
private validateParams(page: any, pageSize: any) { private validateParams(page: any, pageSize: any, sortDirection: any, sortField: any) {
let filteredPageSize = this.pageSizeOptions.find(x => x == pageSize); let filteredPageSize = this.pageSizeOptions.find(x => x == pageSize);
if (!isNumeric(page) || !filteredPageSize) { if (!isNumeric(page) || !filteredPageSize) {
let filteredPage = isNumeric(page) ? page : this.currentPage; let filteredPage = isNumeric(page) ? page : this.currentPage;
filteredPageSize = (filteredPageSize) ? filteredPageSize : this.pageSize; filteredPageSize = (filteredPageSize) ? filteredPageSize : this.pageSize;
this.router.navigate([{ pageId: this.id, page: filteredPage, pageSize: filteredPageSize }]); this.router.navigate([{
pageId: this.id,
page: filteredPage,
pageSize: filteredPageSize,
sortDirection: sortDirection,
sortField: sortField
}]);
} else { } else {
// (+) converts string to a number // (+) converts string to a number
this.currentPage = +page; this.currentPage = +page;
this.pageSize = +pageSize; this.pageSize = +pageSize;
this.sortDirection = +sortDirection;
this.sortField = sortField;
this.pageChange.emit(this.currentPage); this.pageChange.emit(this.currentPage);
this.pageSizeChange.emit(this.pageSize); this.pageSizeChange.emit(this.pageSize);
this.sortDirectionChange.emit(this.sortDirection);
this.sortFieldChange.emit(this.sortField);
} }
} }
@@ -238,7 +327,9 @@ export class PaginationComponent implements OnDestroy, OnInit {
*/ */
private checkConfig(paginateOptions: any) { private checkConfig(paginateOptions: any) {
let required = ['id', 'currentPage', 'pageSize', 'pageSizeOptions']; let required = ['id', 'currentPage', 'pageSize', 'pageSizeOptions'];
let missing = required.filter(function (prop) { return !(prop in paginateOptions); }); let missing = required.filter(function (prop) {
return !(prop in paginateOptions);
});
if (0 < missing.length) { if (0 < missing.length) {
throw new Error("Paginate: Argument is missing the following required properties: " + missing.join(', ')); throw new Error("Paginate: Argument is missing the following required properties: " + missing.join(', '));
} }

View File

@@ -15,6 +15,7 @@ import { SafeUrlPipe } from "./utils/safe-url-pipe";
import { HostWindowService } from "./host-window.service"; import { HostWindowService } from "./host-window.service";
import { NativeWindowFactory, NativeWindowService } from "./window.service"; import { NativeWindowFactory, NativeWindowService } from "./window.service";
import { TRUNCATE_PIPES } from "ng2-truncate"; import { TRUNCATE_PIPES } from "ng2-truncate";
import { EnumKeysPipe } from "./utils/enum-keys-pipe";
const MODULES = [ const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here // Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -30,7 +31,8 @@ const MODULES = [
const PIPES = [ const PIPES = [
FileSizePipe, FileSizePipe,
SafeUrlPipe, SafeUrlPipe,
TRUNCATE_PIPES TRUNCATE_PIPES,
EnumKeysPipe
// put pipes here // put pipes here
]; ];