From 6db165b7a30ebf344bd96b7a92451514a468256f Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Thu, 15 Jun 2017 13:32:15 +0200 Subject: [PATCH] 41914: implemented pagination sort direction/field --- resources/i18n/en.json | 1 + src/app/core/data/data.service.ts | 6 +- .../top-level-community-list.component.html | 6 +- .../top-level-community-list.component.ts | 15 +- .../object-list/object-list.component.html | 5 +- src/app/object-list/object-list.component.ts | 13 + .../pagination/pagination.component.html | 4 +- .../shared/pagination/pagination.component.ts | 499 +++++++++++------- src/app/shared/shared.module.ts | 4 +- 9 files changed, 342 insertions(+), 211 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 7489228eb5..f10c0ae942 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -36,6 +36,7 @@ }, "pagination": { "results-per-page": "Results Per Page", + "sort-direction": "Sort Options", "showing": { "label": "Now showing items ", "detail": "{{ range }} of {{ total }}" diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index c3dd0d6689..4b52ef7d97 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -46,7 +46,11 @@ export abstract class DataService } 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)) { diff --git a/src/app/home/top-level-community-list/top-level-community-list.component.html b/src/app/home/top-level-community-list/top-level-community-list.component.html index 9acaf54dd9..ed592365e2 100644 --- a/src/app/home/top-level-community-list/top-level-community-list.component.html +++ b/src/app/home/top-level-community-list/top-level-community-list.component.html @@ -1,7 +1,9 @@

{{'home.top-level-communities.head' | translate}}

{{'home.top-level-communities.help' | translate}}

- + (pageSizeChange)="onPageSizeChange($event)" + (sortDirectionChange)="onSortDirectionChange($event)" + (sortFieldChange)="onSortDirectionChange($event)">
diff --git a/src/app/home/top-level-community-list/top-level-community-list.component.ts b/src/app/home/top-level-community-list/top-level-community-list.component.ts index 80a4fd967a..f884960f6b 100644 --- a/src/app/home/top-level-community-list/top-level-community-list.component.ts +++ b/src/app/home/top-level-community-list/top-level-community-list.component.ts @@ -13,6 +13,7 @@ import { SortOptions } from "../../core/cache/models/sort-options.model"; export class TopLevelCommunityListComponent implements OnInit { topLevelCommunities: RemoteData; config : PaginationComponentOptions; + sortConfig : SortOptions; constructor( private cds: ItemDataService @@ -29,6 +30,8 @@ export class TopLevelCommunityListComponent implements OnInit { this.config = new PaginationComponentOptions(); this.config.id = "top-level-pagination" this.config.pageSizeOptions = [ 5, 10, 20, 40, 60, 80, 100 ]; + + this.sortConfig = new SortOptions(); } onPageChange(currentPage): void { @@ -41,7 +44,17 @@ export class TopLevelCommunityListComponent implements OnInit { this.updateResults(); } + onSortDirectionChange(sortDirection): void { + this.sortConfig.direction = sortDirection; + this.updateResults(); + } + + onSortFieldChange(field): void { + this.sortConfig.field = field; + this.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 }); } } diff --git a/src/app/object-list/object-list.component.html b/src/app/object-list/object-list.component.html index fa2621d2d9..8d81b4003e 100644 --- a/src/app/object-list/object-list.component.html +++ b/src/app/object-list/object-list.component.html @@ -1,7 +1,10 @@ + (pageSizeChange)="onPageSizeChange($event)" + (sortDirectionChange)="onSortDirectionChange($event)" + (sortFieldChange)="onSortDirectionChange($event)">
  • diff --git a/src/app/object-list/object-list.component.ts b/src/app/object-list/object-list.component.ts index 545b106551..1f75f7d3ab 100644 --- a/src/app/object-list/object-list.component.ts +++ b/src/app/object-list/object-list.component.ts @@ -8,6 +8,7 @@ import { PageInfo } from "../core/shared/page-info.model"; import { Observable } from "rxjs"; import { PaginationComponentOptions } from "../shared/pagination/pagination-component-options.model"; import { EventEmitter } from "@angular/common/src/facade/async"; +import { SortOptions } from "../core/cache/models/sort-options.model"; @Component({ @@ -21,10 +22,13 @@ export class ObjectListComponent implements OnInit { @Input() objects: RemoteData; @Input() config : PaginationComponentOptions; + @Input() sortConfig : SortOptions; pageInfo : Observable; @Output() pageChange = new EventEmitter(); @Output() pageSizeChange = new EventEmitter(); + @Output() sortDirectionChange = new EventEmitter(); + @Output() sortFieldChange = new EventEmitter(); data: any = {}; constructor() { @@ -45,4 +49,13 @@ export class ObjectListComponent implements OnInit { onPageSizeChange(event) { this.pageSizeChange.emit(event); } + + onSortDirectionChange(event) { + this.sortDirectionChange.emit(event); + } + + onSortFieldChange(event) { + this.sortFieldChange.emit(event); + } + } diff --git a/src/app/shared/pagination/pagination.component.html b/src/app/shared/pagination/pagination.component.html index adaab7fcd9..021d42620b 100644 --- a/src/app/shared/pagination/pagination.component.html +++ b/src/app/shared/pagination/pagination.component.html @@ -9,7 +9,9 @@ diff --git a/src/app/shared/pagination/pagination.component.ts b/src/app/shared/pagination/pagination.component.ts index 8990ac27b5..0e11c4f500 100644 --- a/src/app/shared/pagination/pagination.component.ts +++ b/src/app/shared/pagination/pagination.component.ts @@ -1,12 +1,12 @@ import { - ChangeDetectionStrategy, - Component, - EventEmitter, - Input, - OnDestroy, - OnInit, - Output, - ViewEncapsulation + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, + ViewEncapsulation } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router'; import { isNumeric } from "rxjs/util/isNumeric"; @@ -18,230 +18,321 @@ import { DEFAULT_TEMPLATE, DEFAULT_STYLES } from 'ng2-pagination/dist/template'; import { HostWindowService } from "../host-window.service"; import { HostWindowState } from "../host-window.reducer"; import { PaginationComponentOptions } from './pagination-component-options.model'; +import { SortDirection, SortOptions } from "../../core/cache/models/sort-options.model"; /** * The default pagination controls component. */ @Component({ - exportAs: 'paginationComponent', - selector: 'ds-pagination', - templateUrl: 'pagination.component.html', - changeDetection: ChangeDetectionStrategy.Default, - encapsulation: ViewEncapsulation.Emulated + exportAs: 'paginationComponent', + selector: 'ds-pagination', + templateUrl: 'pagination.component.html', + changeDetection: ChangeDetectionStrategy.Default, + encapsulation: ViewEncapsulation.Emulated }) export class PaginationComponent implements OnDestroy, OnInit { - /** - * Number of items in collection. - */ - @Input() collectionSize: number; + /** + * Number of items in collection. + */ + @Input() collectionSize: number; - /** - * Configuration for the NgbPagination component. - */ - @Input() paginationOptions: PaginationComponentOptions; + /** + * Configuration for the NgbPagination component. + */ + @Input() paginationOptions: PaginationComponentOptions; - /** - * An event fired when the page is changed. - * Event's payload equals to the newly selected page. - */ - @Output() pageChange: EventEmitter = new EventEmitter(); + /** + * Sort configuration for this component. + */ + @Input() sortOptions: SortOptions; - /** - * An event fired when the page size is changed. - * Event's payload equals to the newly selected page size. - */ - @Output() pageSizeChange: EventEmitter = new EventEmitter(); + /** + * An event fired when the page is changed. + * Event's payload equals to the newly selected page. + */ + @Output() pageChange: EventEmitter = new EventEmitter(); - /** - * Current page. - */ - public currentPage = 1; - - /** - * Current URL query parameters - */ - public currentQueryParams = {}; + /** + * An event fired when the page wsize is changed. + * Event's payload equals to the newly selected page size. + */ + @Output() pageSizeChange: EventEmitter = new EventEmitter(); - /** - * An observable of HostWindowState type - */ - public hostWindow: Observable; + /** + * An event fired when the sort direction is changed. + * Event's payload equals to the newly selected sort direction. + */ + @Output() sortDirectionChange: EventEmitter = new EventEmitter(); - /** - * ID for the pagination instance. Only useful if you wish to - * have more than once instance at a time in a given component. - */ - private id: string; + /** + * An event fired when the sort field is changed. + * Event's payload equals to the newly selected sort field. + */ + @Output() sortFieldChange: EventEmitter = new EventEmitter(); - /** - * A boolean that indicate if is an extra small devices viewport. - */ - public isXs: boolean; + /** + * Current page. + */ + public currentPage = 1; - /** - * Number of items per page. - */ - public pageSize: number = 10; + /** + * Current URL query parameters + */ + public currentQueryParams = {}; - /** - * A number array that represents options for a context pagination limit. - */ - private pageSizeOptions: Array; + /** + * An observable of HostWindowState type + */ + public hostWindow: Observable; - /** - * Local variable, which can be used in the template to access the paginate controls ngbDropdown methods and properties - */ - public paginationControls; + /** + * ID for the pagination instance. Only useful if you wish to + * have more than once instance at a time in a given component. + */ + private id: string; - /** - * Subscriber to observable. - */ - private routeSubscription: any; + /** + * A boolean that indicate if is an extra small devices viewport. + */ + public isXs: boolean; - /** - * An object that represents pagination details of the current viewed page - */ - public showingDetail: any = { - range: null, - total: null - }; + /** + * Number of items per page. + */ + public pageSize: number = 10; - /** - * Subscriber to observable. - */ - private stateSubscription: any; + /** + * Declare SortDirection enumeration to use it in the template + */ + public sortDirections = SortDirection - /** - * Method provided by Angular. Invoked after the constructor. - */ - ngOnInit() { - this.stateSubscription = this.hostWindowService.isXs() - .subscribe((status: boolean) => { - this.isXs = status; - }); - this.checkConfig(this.paginationOptions); - this.id = this.paginationOptions.id || null; - this.currentPage = this.paginationOptions.currentPage; - this.pageSize = this.paginationOptions.pageSize; - this.pageSizeOptions = this.paginationOptions.pageSizeOptions; + /** + * A number array that represents options for a context pagination limit. + */ + private pageSizeOptions: Array; - this.routeSubscription = this.route.queryParams - .map(queryParams => queryParams) - .subscribe(queryParams => { - this.currentQueryParams = queryParams; - if(this.id == queryParams['pageId'] - && (this.paginationOptions.currentPage != queryParams['page'] - || this.paginationOptions.pageSize != queryParams['pageSize']) - ) { - this.validateParams(queryParams['page'], queryParams['pageSize']); + /** + * 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 + */ + public paginationControls; + + /** + * + */ + + /** + * Subscriber to observable. + */ + private routeSubscription: any; + + /** + * An object that represents pagination details of the current viewed page + */ + public showingDetail: any = { + range: null, + total: null + }; + + /** + * Subscriber to observable. + */ + private stateSubscription: any; + + /** + * Method provided by Angular. Invoked after the constructor. + */ + ngOnInit() { + this.stateSubscription = this.hostWindowService.isXs() + .subscribe((status: boolean) => { + this.isXs = status; + }); + this.checkConfig(this.paginationOptions); + this.id = this.paginationOptions.id || null; + this.currentPage = this.paginationOptions.currentPage; + this.pageSize = this.paginationOptions.pageSize; + this.pageSizeOptions = this.paginationOptions.pageSizeOptions; + this.sortDirection = this.sortOptions.direction; + this.sortField = this.sortOptions.field; + this.routeSubscription = this.route.queryParams + .map(queryParams => queryParams) + .subscribe(queryParams => { + this.currentQueryParams = queryParams; + if (this.id == queryParams['pageId'] + && (this.paginationOptions.currentPage != queryParams['page'] + || this.paginationOptions.pageSize != queryParams['pageSize'] + || this.sortOptions.direction != queryParams['sortDirection'] + || this.sortOptions.field != queryParams['sortField'] ) + ) { + this.validateParams(queryParams['page'], queryParams['pageSize'], queryParams['sortDirection'], queryParams['sortField']); + } + }); + this.setShowingDetail(); + } + + /** + * Method provided by Angular. Invoked when the instance is destroyed. + */ + ngOnDestroy() { + this.stateSubscription.unsubscribe(); + this.routeSubscription.unsubscribe(); + } + + /** + * @param route + * Route is a singleton service provided by Angular. + * @param router + * Router is a singleton service provided by Angular. + */ + constructor(private route: ActivatedRoute, + private router: Router, + public hostWindowService: HostWindowService) { + } + + /** + * Method to set set new page and update route parameters + * + * @param page + * The page being navigated to. + */ + public doPageChange(page: number) { + this.currentPage = page; + this.updateRoute(); + this.setShowingDetail(); + this.pageChange.emit(page); + } + + /** + * Method to set set new page size and update route parameters + * + * @param pageSize + * The new page size. + */ + public setPageSize(pageSize: number) { + this.pageSize = pageSize; + this.updateRoute(); + this.setShowingDetail(); + 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. + */ + private setShowingDetail() { + let firstItem; + let lastItem; + let lastPage = Math.round(this.collectionSize / this.pageSize); + + firstItem = this.pageSize * (this.currentPage - 1) + 1; + if (this.currentPage != lastPage) { + lastItem = this.pageSize * this.currentPage; + } else { + lastItem = this.collectionSize; + } + this.showingDetail = { + range: firstItem + ' - ' + lastItem, + total: this.collectionSize } - }); - this.setShowingDetail(); - } - - /** - * Method provided by Angular. Invoked when the instance is destroyed. - */ - ngOnDestroy() { - this.stateSubscription.unsubscribe(); - this.routeSubscription.unsubscribe(); - } - - /** - * @param route - * Route is a singleton service provided by Angular. - * @param router - * Router is a singleton service provided by Angular. - */ - constructor( - private route: ActivatedRoute, - private router: Router, - public hostWindowService: HostWindowService - ){ - } - - /** - * Method to set set new page and update route parameters - * - * @param page - * The page being navigated to. - */ - public doPageChange(page: number) { - this.router.navigate([], { queryParams: Object.assign({}, this.currentQueryParams, { pageId: this.id, page: page, pageSize: this.pageSize }) }); - this.currentPage = page; - this.setShowingDetail(); - this.pageChange.emit(page); - } - - /** - * Method to set set new page size and update route parameters - * - * @param pageSize - * The new page size. - */ - public setPageSize(pageSize: number) { - this.router.navigate([], { queryParams: Object.assign({}, this.currentQueryParams, { pageId: this.id, page: this.currentPage, pageSize: pageSize }) }); - this.pageSize = pageSize; - this.setShowingDetail(); - this.pageSizeChange.emit(pageSize); - } - - /** - * Method to set pagination details of the current viewed page. - */ - private setShowingDetail() { - let firstItem; - let lastItem; - let lastPage = Math.round(this.collectionSize / this.pageSize); - - firstItem = this.pageSize * (this.currentPage - 1) + 1; - if (this.currentPage != lastPage) { - lastItem = this.pageSize * this.currentPage; - } else { - lastItem = this.collectionSize; } - this.showingDetail = { - range: firstItem + ' - ' + lastItem, - total: this.collectionSize - } - } - /** - * Validate query params - * - * @param page - * The page number to validate - * @param pageSize - * The page size to validate - */ - private validateParams(page: any, pageSize: any) { - let filteredPageSize = this.pageSizeOptions.find(x => x == pageSize); - if (!isNumeric(page) || !filteredPageSize) { - let filteredPage = isNumeric(page) ? page : this.currentPage; - filteredPageSize = (filteredPageSize) ? filteredPageSize : this.pageSize; - this.router.navigate([{ pageId: this.id, page: filteredPage, pageSize: filteredPageSize }]); - } else { - // (+) converts string to a number - this.currentPage = +page; - this.pageSize = +pageSize; - this.pageChange.emit(this.currentPage); - this.pageSizeChange.emit(this.pageSize); + /** + * Validate query params + * + * @param page + * The page number to validate + * @param pageSize + * The page size to validate + */ + private validateParams(page: any, pageSize: any, sortDirection: any, sortField: any) { + let filteredPageSize = this.pageSizeOptions.find(x => x == pageSize); + if (!isNumeric(page) || !filteredPageSize) { + let filteredPage = isNumeric(page) ? page : this.currentPage; + filteredPageSize = (filteredPageSize) ? filteredPageSize : this.pageSize; + this.router.navigate([{ + pageId: this.id, + page: filteredPage, + pageSize: filteredPageSize, + sortDirection: sortDirection, + sortField: sortField + }]); + } else { + // (+) converts string to a number + this.currentPage = +page; + this.pageSize = +pageSize; + this.sortDirection = +sortDirection; + this.sortField = sortField; + this.pageChange.emit(this.currentPage); + this.pageSizeChange.emit(this.pageSize); + this.sortDirectionChange.emit(this.sortDirection); + this.sortFieldChange.emit(this.sortField); + } } - } - /** - * Ensure options passed contains the required properties. - * - * @param paginateOptions - * The paginate options object. - */ - private checkConfig(paginateOptions: any) { - let required = ['id', 'currentPage', 'pageSize', 'pageSizeOptions']; - let missing = required.filter(function (prop) { return !(prop in paginateOptions); }); - if (0 < missing.length) { - throw new Error("Paginate: Argument is missing the following required properties: " + missing.join(', ')); + /** + * Ensure options passed contains the required properties. + * + * @param paginateOptions + * The paginate options object. + */ + private checkConfig(paginateOptions: any) { + let required = ['id', 'currentPage', 'pageSize', 'pageSizeOptions']; + let missing = required.filter(function (prop) { + return !(prop in paginateOptions); + }); + if (0 < missing.length) { + throw new Error("Paginate: Argument is missing the following required properties: " + missing.join(', ')); + } } - } } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index b02c50957e..7fbca139b8 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -15,6 +15,7 @@ import { SafeUrlPipe } from "./utils/safe-url-pipe"; import { HostWindowService } from "./host-window.service"; import { NativeWindowFactory, NativeWindowService } from "./window.service"; import { TRUNCATE_PIPES } from "ng2-truncate"; +import { EnumKeysPipe } from "./utils/enum-keys-pipe"; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -30,7 +31,8 @@ const MODULES = [ const PIPES = [ FileSizePipe, SafeUrlPipe, - TRUNCATE_PIPES + TRUNCATE_PIPES, + EnumKeysPipe // put pipes here ];