mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
fix pagination issues (#5881)
* fix pagination issues * angular workarounds
This commit is contained in:
parent
389a65860c
commit
33965d0a41
@ -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');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user