Skip to content
Snippets Groups Projects
pagination.component.ts 6.83 KiB
Newer Older
import {
  ChangeDetectionStrategy,
  Component,
  EventEmitter,
  Input,
  OnDestroy,
  OnInit,
  Output,
  ViewEncapsulation
} from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router';
import { isNumeric } from "rxjs/util/isNumeric";
import 'rxjs/add/operator/switchMap';
import { Observable } from "rxjs";
// It is necessary to use ng2-pagination
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';

/**
 * The default pagination controls component.
 */
@Component({
  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;

  /**
   * 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<number> = new EventEmitter<number>();

  /**
   * An event fired when the page size is changed.
   * Event's payload equals to the newly selected page size.
   */
  @Output() pageSizeChange: EventEmitter<number> = new EventEmitter<number>();

  /**
   * Current page.
   */
  public currentPage = 1;
  
  /**
   * Current URL query parameters
   */
  public currentQueryParams = {};

  /**
   * An observable of HostWindowState type
   */
  public hostWindow: Observable<HostWindowState>;

  /**
   * 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;

  /**
   * A boolean that indicate if is an extra small devices viewport.
   */
  public isXs: boolean;

  /**
   * Number of items per page.
   */
  public pageSize: number = 10;

  /**
   * A number array that represents options for a context pagination limit.
   */
  private pageSizeOptions: Array<number>;

  /**
   * 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.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']);
        }
      });
    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);
    }
  }

  /**
   * 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(', '));
    }
  }
}