fix pagination issues (#5881)

* fix pagination issues

* angular workarounds
This commit is contained in:
Denys Vuika 2020-07-16 17:52:49 +01:00 committed by GitHub
parent 389a65860c
commit 33965d0a41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 124 additions and 98 deletions

View File

@ -26,11 +26,11 @@ import { CoreTestingModule } from '../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
class FakePaginationInput implements Pagination { class FakePaginationInput implements Pagination {
count: number = 25; count = 25;
hasMoreItems: boolean; hasMoreItems = false;
totalItems: number = null; totalItems = 0;
skipCount: number = null; skipCount = 0;
maxItems: number = 25; maxItems = 25;
constructor(pagesCount: number, currentPage: number, lastPageItems: number) { constructor(pagesCount: number, currentPage: number, lastPageItems: number) {
this.totalItems = ((pagesCount - 1) * this.maxItems) + lastPageItems; this.totalItems = ((pagesCount - 1) * this.maxItems) + lastPageItems;
@ -54,19 +54,23 @@ describe('PaginationComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(PaginationComponent); fixture = TestBed.createComponent(PaginationComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges();
}); });
afterEach(() => { afterEach(() => {
fixture.destroy(); fixture.destroy();
}); });
it('should have an "empty" class if no items present', () => { it('should have an "empty" class if no items present', async (done) => {
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement.classList.contains('adf-pagination__empty')).toBeTruthy(); expect(fixture.nativeElement.classList.contains('adf-pagination__empty')).toBeTruthy();
done();
}); });
describe('Single page', () => { describe('Single page', () => {
beforeEach(() => { beforeEach(async () => {
component.pagination = new FakePaginationInput(1, 1, 10); component.pagination = new FakePaginationInput(1, 1, 10);
}); });
@ -238,21 +242,19 @@ describe('PaginationComponent', () => {
it('has defaults', () => { it('has defaults', () => {
component.ngOnInit(); component.ngOnInit();
const { expect(component.lastPage).toBe(1, 'lastPage');
current, lastPage, isFirstPage, isLastPage, expect(component.previous).toBe(1, 'previous');
next, previous, range, pages expect(component.current).toBe(1, 'current');
} = component; expect(component.next).toBe(1, 'next');
expect(lastPage).toBe(1, 'lastPage'); expect(component.isFirstPage).toBe(true, 'isFirstPage');
expect(previous).toBe(1, 'previous'); expect(component.isLastPage).toBe(true, 'isLastPage');
expect(current).toBe(1, 'current');
expect(next).toBe(1, 'next');
expect(isFirstPage).toBe(true, 'isFirstPage'); // tslint:disable-next-line: no-console
expect(isLastPage).toBe(true, 'isLastPage'); console.log(JSON.stringify(component.pagination));
expect(range).toEqual([ 0, 0 ], 'range'); expect(component.range).toEqual([ 0, 0 ], 'range');
expect(pages).toEqual([ 1 ], 'pages'); expect(component.pages).toEqual([ 1 ], 'pages');
}); });
}); });

View File

@ -15,12 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, OnDestroy, ElementRef, ChangeDetectionStrategy, ChangeDetectorRef, Renderer2 } from '@angular/core';
ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation,
ChangeDetectorRef, OnDestroy, HostBinding
} from '@angular/core';
import { Pagination } from '@alfresco/js-api';
import { PaginatedComponent } from './paginated-component.interface'; import { PaginatedComponent } from './paginated-component.interface';
import { PaginationComponentInterface } from './pagination-component.interface'; import { PaginationComponentInterface } from './pagination-component.interface';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -29,6 +24,12 @@ import { UserPreferencesService, UserPreferenceValues } from '../services/user-p
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
export type PaginationAction =
| 'NEXT_PAGE'
| 'PREV_PAGE'
| 'CHANGE_PAGE_SIZE'
| 'CHANGE_PAGE_NUMBER';
@Component({ @Component({
selector: 'adf-pagination', selector: 'adf-pagination',
host: { 'class': 'adf-pagination' }, host: { 'class': 'adf-pagination' },
@ -38,20 +39,18 @@ import { TranslateService } from '@ngx-translate/core';
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class PaginationComponent implements OnInit, OnDestroy, PaginationComponentInterface { export class PaginationComponent implements OnInit, OnDestroy, PaginationComponentInterface {
static DEFAULT_PAGINATION: PaginationModel = {
static DEFAULT_PAGINATION: Pagination = new Pagination({
skipCount: 0, skipCount: 0,
maxItems: 25, maxItems: 25,
totalItems: 0 totalItems: 0,
}); count: 0,
hasMoreItems: false
static ACTIONS = {
NEXT_PAGE: 'NEXT_PAGE',
PREV_PAGE: 'PREV_PAGE',
CHANGE_PAGE_SIZE: 'CHANGE_PAGE_SIZE',
CHANGE_PAGE_NUMBER: 'CHANGE_PAGE_NUMBER'
}; };
private _pagination: PaginationModel;
private _isEmpty = true;
private _hasItems = false;
/** Component that provides custom pagination support. */ /** Component that provides custom pagination support. */
@Input() @Input()
target: PaginatedComponent; target: PaginatedComponent;
@ -60,40 +59,70 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
@Input() @Input()
supportedPageSizes: number[]; supportedPageSizes: number[];
get pagination(): PaginationModel {
return this._pagination;
}
/** Pagination object. */ /** Pagination object. */
@Input() @Input()
pagination: PaginationModel = PaginationComponent.DEFAULT_PAGINATION; set pagination(value: PaginationModel) {
value = value || PaginationComponent.DEFAULT_PAGINATION;
this._pagination = value;
this._hasItems = value && value.count > 0;
this._isEmpty = !this.hasItems;
// TODO: Angular 10 workaround for HostBinding bug
if (this._isEmpty) {
this.renderer.addClass(this.elementRef.nativeElement, 'adf-pagination__empty');
} else {
this.renderer.removeClass(this.elementRef.nativeElement, 'adf-pagination__empty');
}
this.cdr.detectChanges();
}
/** Emitted when pagination changes in any way. */ /** Emitted when pagination changes in any way. */
@Output() @Output()
change: EventEmitter<PaginationModel> = new EventEmitter<PaginationModel>(); change = new EventEmitter<PaginationModel>();
/** Emitted when the page number changes. */ /** Emitted when the page number changes. */
@Output() @Output()
changePageNumber: EventEmitter<PaginationModel> = new EventEmitter<PaginationModel>(); changePageNumber = new EventEmitter<PaginationModel>();
/** Emitted when the page size changes. */ /** Emitted when the page size changes. */
@Output() @Output()
changePageSize: EventEmitter<PaginationModel> = new EventEmitter<PaginationModel>(); changePageSize = new EventEmitter<PaginationModel>();
/** Emitted when the next page is requested. */ /** Emitted when the next page is requested. */
@Output() @Output()
nextPage: EventEmitter<PaginationModel> = new EventEmitter<PaginationModel>(); nextPage = new EventEmitter<PaginationModel>();
/** Emitted when the previous page is requested. */ /** Emitted when the previous page is requested. */
@Output() @Output()
prevPage: EventEmitter<PaginationModel> = new EventEmitter<PaginationModel>(); prevPage = new EventEmitter<PaginationModel>();
private onDestroy$ = new Subject<boolean>(); private onDestroy$ = new Subject<boolean>();
constructor(private cdr: ChangeDetectorRef, private userPreferencesService: UserPreferencesService, private translate: TranslateService) { constructor(
private elementRef: ElementRef,
private renderer: Renderer2,
private cdr: ChangeDetectorRef,
private userPreferencesService: UserPreferencesService,
private translate: TranslateService) {
} }
ngOnInit() { ngOnInit() {
this.userPreferencesService this.userPreferencesService
.select(UserPreferenceValues.PaginationSize) .select(UserPreferenceValues.PaginationSize)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntil(this.onDestroy$))
.subscribe(pagSize => this.pagination.maxItems = pagSize); .subscribe(maxItems => {
this.pagination = {
...PaginationComponent.DEFAULT_PAGINATION,
...this.pagination,
maxItems
};
});
if (!this.supportedPageSizes) { if (!this.supportedPageSizes) {
this.supportedPageSizes = this.userPreferencesService.supportedPageSizes; this.supportedPageSizes = this.userPreferencesService.supportedPageSizes;
@ -107,13 +136,16 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
this.goPrevious(); this.goPrevious();
} }
this.pagination = pagination; this.pagination = {
this.cdr.detectChanges(); ...pagination
};
}); });
} }
if (!this.pagination) { if (!this.pagination) {
this.pagination = PaginationComponent.DEFAULT_PAGINATION; this.pagination = {
...PaginationComponent.DEFAULT_PAGINATION
};
} }
} }
@ -153,24 +185,24 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
} }
get hasItems(): boolean { get hasItems(): boolean {
return this.pagination && this.pagination.count > 0; return this._hasItems;
} }
@HostBinding('class.adf-pagination__empty') // TODO: not working correctly in Angular 10
// @HostBinding('class.adf-pagination__empty')
get isEmpty(): boolean { get isEmpty(): boolean {
return !this.hasItems; return this._isEmpty;
} }
get range(): number[] { get range(): number[] {
const { skipCount, maxItems, totalItems } = this.pagination; const { skipCount, maxItems, totalItems } = this.pagination;
const { isLastPage } = this;
let start = 0; let start = 0;
if (totalItems || totalItems !== 0) { if (totalItems || totalItems !== 0) {
start = skipCount + 1; start = skipCount + 1;
} }
const end = isLastPage ? totalItems : skipCount + maxItems; const end = this.isLastPage ? totalItems : skipCount + maxItems;
return [start, end]; return [start, end];
} }
@ -183,13 +215,16 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
get itemRangeText(): string { get itemRangeText(): string {
const rangeString = this.range.join('-'); const rangeString = this.range.join('-');
let translation = this.translate.instant('CORE.PAGINATION.ITEMS_RANGE', { let translation = this.translate.instant('CORE.PAGINATION.ITEMS_RANGE', {
range: rangeString, range: rangeString,
total: this.pagination.totalItems total: this.pagination.totalItems
}); });
if (!this.pagination.totalItems) { if (!this.pagination.totalItems) {
translation = translation.substr(0, translation.indexOf(rangeString) + rangeString.length); translation = translation.substr(0, translation.indexOf(rangeString) + rangeString.length);
} }
return translation; return translation;
} }
@ -197,12 +232,13 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
if (this.hasItems) { if (this.hasItems) {
const maxItems = this.pagination.maxItems; const maxItems = this.pagination.maxItems;
const skipCount = (this.next - 1) * maxItems; const skipCount = (this.next - 1) * maxItems;
this.pagination.skipCount = skipCount;
this.handlePaginationEvent(PaginationComponent.ACTIONS.NEXT_PAGE, { this.pagination = {
skipCount, ...this.pagination,
maxItems skipCount
}); };
this.handlePaginationEvent('NEXT_PAGE');
} }
} }
@ -210,12 +246,13 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
if (this.hasItems) { if (this.hasItems) {
const maxItems = this.pagination.maxItems; const maxItems = this.pagination.maxItems;
const skipCount = (this.previous - 1) * maxItems; const skipCount = (this.previous - 1) * maxItems;
this.pagination.skipCount = skipCount;
this.handlePaginationEvent(PaginationComponent.ACTIONS.PREV_PAGE, { this.pagination = {
skipCount, ...this.pagination,
maxItems skipCount
}); };
this.handlePaginationEvent('PREV_PAGE');
} }
} }
@ -223,22 +260,25 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
if (this.hasItems) { if (this.hasItems) {
const maxItems = this.pagination.maxItems; const maxItems = this.pagination.maxItems;
const skipCount = (pageNumber - 1) * maxItems; const skipCount = (pageNumber - 1) * maxItems;
this.pagination.skipCount = skipCount;
this.handlePaginationEvent(PaginationComponent.ACTIONS.CHANGE_PAGE_NUMBER, { this.pagination = {
skipCount, ...this.pagination,
maxItems skipCount
}); };
this.handlePaginationEvent('CHANGE_PAGE_NUMBER');
} }
} }
onChangePageSize(maxItems: number) { onChangePageSize(maxItems: number) {
this.pagination.skipCount = 0; this.pagination = {
this.userPreferencesService.paginationSize = maxItems; ...this.pagination,
this.handlePaginationEvent(PaginationComponent.ACTIONS.CHANGE_PAGE_SIZE, {
skipCount: 0, skipCount: 0,
maxItems maxItems
}); };
this.userPreferencesService.paginationSize = maxItems;
this.handlePaginationEvent('CHANGE_PAGE_SIZE');
} }
ngOnDestroy() { ngOnDestroy() {
@ -246,45 +286,29 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
this.onDestroy$.complete(); this.onDestroy$.complete();
} }
handlePaginationEvent(action: string, params: PaginationModel) { handlePaginationEvent(action: PaginationAction) {
const { const paginationModel = { ...this.pagination };
NEXT_PAGE,
PREV_PAGE,
CHANGE_PAGE_NUMBER,
CHANGE_PAGE_SIZE
} = PaginationComponent.ACTIONS;
const { if (action === 'NEXT_PAGE') {
change, this.nextPage.emit(paginationModel);
changePageNumber,
changePageSize,
nextPage,
prevPage,
pagination
} = this;
const paginationModel: PaginationModel = Object.assign({}, pagination, params);
if (action === NEXT_PAGE) {
nextPage.emit(paginationModel);
} }
if (action === PREV_PAGE) { if (action === 'PREV_PAGE') {
prevPage.emit(paginationModel); this.prevPage.emit(paginationModel);
} }
if (action === CHANGE_PAGE_NUMBER) { if (action === 'CHANGE_PAGE_NUMBER') {
changePageNumber.emit(paginationModel); this.changePageNumber.emit(paginationModel);
} }
if (action === CHANGE_PAGE_SIZE) { if (action === 'CHANGE_PAGE_SIZE') {
changePageSize.emit(paginationModel); this.changePageSize.emit(paginationModel);
} }
change.emit(params); this.change.emit(paginationModel);
if (this.target) { if (this.target) {
this.target.updatePagination(params); this.target.updatePagination(paginationModel);
} }
} }
} }