swimlane / ngx-datatable

✨ A feature-rich yet lightweight data-table crafted for Angular
http://swimlane.github.io/ngx-datatable/
MIT License
4.63k stars 1.68k forks source link

DataTablePagerComponent custom number of displayed pages. #802

Open tplk opened 7 years ago

tplk commented 7 years ago

I'm submitting a ... (check one with "x")

[ ] bug report => search github for a similar issue or PR before submitting
[x ] feature request
[ ] support request => Please do not submit support request here, post on Stackoverflow or Gitter

Current behavior DataTablePagerComponent can't be configured to display custom number of pages. Currently it's hardcoded to display 5 pages always.

const maxSize = 5;

Expected behavior User should be able to set a custom number of elements to be displayed which fits his project requirements.

I already implemented it in my current project by adding a variable which can be passed from the template. If anyone interested I can create a pull request.

mvhecke commented 7 years ago

@t-p-l-k I don't really get the use case, could you be more specific? When external paging is enabled the page size is automatically calculated, so I don't get the need to expose this.

MorlaRamakrishna commented 7 years ago

@mvhecke How can we control automatic calculation of the pageSize. Because it forcing to miscalculate the number pages.

Please refer the issue "Issue with Number of pages" #804

tplk commented 7 years ago

@mvhecke here's an example:

image

Pager displays 5 pages and it's okay when you only have pagination in the footer. But what if we need extra information there?

image

Well, it seems logical to reduce pager's width so it doesn't take 1/3 of footer's width.

image

Another use case is a mobile layout where viewport width causes default pager to wrap.

image

tplk commented 7 years ago

Last use case can be solved with css media queries but anyway.

mvhecke commented 7 years ago

Aah, a very good example. Now I get why you would want this, should be pretty easy to expose this.

tplk commented 7 years ago

Offtopic: @mvhecke btw, I don't understand how can I change row limit dynamically. You've mentioned somewhere that datatable uses OnPush change detection strategy but it doesn't work with limit value (works for pager maxSize without any extra work, I just switched constant maxSize with an input variable). Should I create a new issue to solve this?

tplk commented 7 years ago

Oh, I think I figured it out: https://plnkr.co/edit/wRWlit

dylandrollut commented 7 years ago

@t-p-l-k do you mind explaining how you went about finding your solution? I modified it to work for a drop-down menu to choose the number of items per page, but I don't fully understand what's going on code wise.

rahul-winner commented 7 years ago

@t-p-l-k I am little curious to know how could we get selection box in the footer to allow changing the pageSize. Please see the highlighted section in image. image

I'd tried to achieve it however could not make it work. I've seen you got good hold on this code, could you please help me out achieve this.

Thanks. Rahul

rahul-winner commented 7 years ago

Adding to my query :

@mvhecke Would it be a good idea to have this feature [allow to change page size from footer] OOTB !

tplk commented 7 years ago

@rahul-winner sure, here's working example: https://plnkr.co/edit/ym689j You can create custom footer template and move select element there:

<ngx-datatable-footer>
  <ng-template
    ngx-datatable-footer-template
    let-rowCount="rowCount"
    let-pageSize="pageSize"
    let-selectedCount="selectedCount"
    let-curPage="curPage"
    let-offset="offset"
  >
    <div class="datatable-footer_pagination">
      <datatable-pager
        [page]="curPage"
        [size]="pageSize"
        [count]="rowCount"
        [hidden]="false"
        (change)="table.onFooterPage($event)">
      </datatable-pager>
      <div class="page-limit-container">
        <select (change)="onLimitChange($event.target.value)" class="page-limit">
          <option
            *ngFor="let option of pageLimitOptions"
            [value]="option.value"
            [selected]="option.value == currentPageLimit"
          >
            {{option.value}} per page
          </option>
        </select>
      </div>
    </div>
  </ng-template>
</ngx-datatable-footer>
tplk commented 7 years ago

@dylandrollut well, basically @ViewChild(DatatableComponent) private table: DatatableComponent; gives you a pointer to the table. DatatableComponent has limit attribute which defines number of rows per page. In my example I change it directly but you can probably do the same by binding a variable in the template:

<ngx-datatable
  [rows]='rows'
  [limit]='pageLimit'
  [columns]='[{name:"Name"},{name:"Gender"},{name:"Company"}]'
  [columnMode]='"force"'
  [headerHeight]='50'
  [footerHeight]='50'
  [rowHeight]='"auto"'>
</ngx-datatable>

I thought that it doesn't matter since you still need a pointer to the table so you can call recalculate() method.

setTimeout(() => {
  if (this.table.bodyComponent.temp.length <= 0) {
    this.table.offset = Math.floor((this.table.rowCount - 1) / this.table.limit);
  }
});

This is a hackish way to switch active page to the last one available. this.table.bodyComponent.temp is an array of currently visible rows if you're using pagination. When you have 100 items total and 5 items per page then you have 20 pages. If you go to the 20th page and switch page limit to 50 items per page then you'll only have 2 pages. this.table.offset = Math.floor((this.table.rowCount - 1) / this.table.limit); changes active page to the last page available (2 in this example). This was only tested with client-side pagination and I have no idea what's going to happen if you use it with server-side. You can just change offset to 0 so when you change page limit your paging will reset. It will give protection from navigating to unexisting page and will work with server-side pagination I think.

dylandrollut commented 7 years ago

Thank you for the reply @t-p-l-k. I actually did bind it to a variable for the drop-down menu. Any idea on how to do that but with two drop-downs and two tables on the same page?

tplk commented 7 years ago

@dylandrollut move it to a separate component?

mvhecke commented 7 years ago

I think is can be a valuable feature, especially for large datasets. But it shouldn't be enabled by default as most applications don't allow you to do this.

irco commented 7 years ago

Was the original feature request ever looked into? I was interested in displaying a much smaller number of pages in the paginator to avoid overflowing.

tplk commented 7 years ago

@irco I've implemented it in my project, I can share my code here but I don't have time to test everything and create a PR.

wizarrc commented 7 years ago

Possibly related. https://github.com/swimlane/ngx-datatable/issues/963

I fixed a bug that didn't allow you to bind the limit and it was only allowed to be set once without a workaround. It now updates when you change `limit' from the template.

I'm assuming you want a more customizable pager though.

irco commented 7 years ago

@t-p-l-k would love to see what you did. I might have the time to do a pr if this is something desired.

duard commented 7 years ago

@t-p-l-k could you share your css for for this pagination ?

amalasquezm commented 7 years ago

Hello, I think it would help to can set the maximun of custom number of pages. Currently it's hardcoded to display 5 pages always.

const maxSize = 5;

Looks messy for mobile. image

amalasquezm commented 7 years ago

You can prevent that using styles. I just fixed using this:

@media (max-width: 560px) { .ngx-datatable .datatable-footer .datatable-pager .pager li.pages { display: none !important; } }

cole-paciano commented 6 years ago

@t-p-l-k : Not sure if you're still checking this page, but would love to know how you were able to override the 'const maxSize = 5' statement. I really need to set it to 3 due to limited horizontal space at lower resolutions (as you illustrated previously with your screenshots above) but can't figure out how to accomplish that since the calcPages() function only has one parameter (not the one I need) as well as maxSize is set as a constant.

tplk commented 6 years ago

@cole-paciano hello!

I don't use ngx-datatable anymore really, I usually implement my own data grids based on angular/cdk DataTable.

But I found my old implementation and updated it to work with current version of ngx-datatable, here you go: https://stackblitz.com/edit/tplk-angular-ngx-datatable-custom-paginator

There are two dropdowns to change rows per page and visible pages in paginator. Custom paginator is basically ngx-datatable's paginator with additional visiblePagesCount input and updated calcPages method.

It can definitely be improved, it's just a really quick example. Hope it helps!

tplk commented 6 years ago

@duard Hi! I'm sorry for such late reply. I don't know if you still need this and I don't have time to clean it up, but here's what I've got:

@import '~@swimlane/ngx-datatable/release/index.css';
@import '~@swimlane/ngx-datatable/release/themes/material.css';
@import '~@swimlane/ngx-datatable/release/assets/icons.css';
@import './shared/variables';
@import './shared/breakpoints';

// Component styles are so messed up that it's impossible
// to override them without breaking style guide.

/* stylelint-disable */
.ngx-datatable.material.single-selection .datatable-body-row.active,
.ngx-datatable.material.single-selection .datatable-body-row.active .datatable-row-group,
.ngx-datatable.material.multi-selection .datatable-body-row.active,
.ngx-datatable.material.multi-selection .datatable-body-row.active .datatable-row-group,
.ngx-datatable.material.multi-click-selection .datatable-body-row.active,
.ngx-datatable.material.multi-click-selection .datatable-body-row.active .datatable-row-group {
  background-color: #54af3b;
  color: #fff;

  &:hover {
    background-color: #55b93c;
    color: #fff;
  }
}

.ngx-datatable.material {

  .progress-linear {
    position: fixed !important;
    bottom: 0px;
  }

  .datatable-footer_scroll-to-top {
    margin: 1em;

    .scroll-to-top-button {
      padding: 0.5em 1em;
      color: #fff;
      background-color: #4d87ea;
    }
  }

  .datatable-header {
    background-color: #383a3c;
  }

  .datatable-header-cell-label {
    color: #fff;
    cursor: pointer;
  }

  .datatable-body-cell-label {
    cursor: default;
  }

  .datatable-footer {
    font-size: 1em;

    .datatable-footer_pagination {
      width: 100%;
      padding: 0.5em 1em;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
  }

  .glp-datatable-pager {
    flex: none;
    margin-right: 1em;
    margin-left: 0;
    text-align: initial;

    ul {
      display: flex;
      flex-direction: row;
      flex-wrap: nowrap;
    }

    li {
      width: 2.5em;
      height: 2.5em;
      margin-right: 0.3em;
      cursor: pointer;

      &.active {
        font-weight: bold;
      }

      &.disabled {
        opacity: 0.6;
      }

      &.nav {
        background-color: #e68628;
        color: #fff;

        a {
          font-size: 1.5em;
        }
      }

      &.pages {
        background-color: #e6e6e6;
        display: none;

        @include gt-xs {
          display: inherit;
        }
      }
    }

    a {
      display: flex;
      justify-content: center;
      align-items: center;
      width: 100%;
      height: 100%;
    }
  }

  .datatable-footer__page-limit {
    padding: 0.4em;
    font-size: 1.2em;
    width: 10em;
  }

  &.scroll-vertical {
    max-height: 90vh;
    min-height: 20em;
  }

  ::-webkit-scrollbar {
    width: 0.35em;
    height: 0.35em;
  }

  ::-webkit-scrollbar-thumb {
    background-color: transparent;
  }

  ::-webkit-scrollbar-thumb:vertical {
    border-left: 0.1em solid $scrollbar-background-color;
  }

  ::-webkit-scrollbar-thumb:horizontal {
    border-top: 0.1em solid $scrollbar-background-color;
  }

  ::-webkit-scrollbar-track {
    background-color: transparent;
  }
}

Related HTML:

    <ngx-datatable-footer>
      <ng-template
        let-rowCount="rowCount"
        let-pageSize="pageSize"
        let-selectedCount="selectedCount"
        let-curPage="curPage"
        let-offset="offset"
        ngx-datatable-footer-template
      >
        <div class="datatable-footer_pagination">
          <glp-datatable-pager
            [page]="curPage"
            [visiblePagesCount]="3"
            [size]="pageSize"
            [count]="rowCount"
            [hidden]="false"
            (change)="table.onFooterPage($event)">
          </glp-datatable-pager>
          <div class="datatable-footer__page-limit-container">
            <select (change)="onLimitChange($event.target.value)" class="datatable-footer__page-limit">
              <option
                *ngFor="let option of pageLimitOptions"
                [value]="option.value"
                [selected]="option.value == currentPageLimit"
              >
                {{option.value}} {{ 'COMPONENTS.DATATABLE.PER_PAGE' | translate }}
              </option>
            </select>
          </div>
        </div>
      </ng-template>
    </ngx-datatable-footer>
cole-paciano commented 6 years ago

@t-p-l-k Thank you so much for taking the time to put that example together! That did the trick. So basically, you overwrote the existing pager.component.ts file with a modified version that allows for setting the amount of pager buttons to be viewed. I wonder if I should be concerned for any potential conflicts if/when future updates are made to the original file by swimlane down the road? In any event, thanks again for your quick and helpful response!

tplk commented 6 years ago

@cole-paciano I guess I'll put a PR together later this week if I have spare time. Yeah, on upgrade you should check if there are any major changes in Paginator to keep it up to date. Also, since in my example it extends default paginator, you can remove any methods/vars which you don't want to override from custom paginator.

ArfanMirza commented 6 years ago

Make an extension.ts and import that in component

import 'app/...../xxx-datatable-pager.extensions'; //Import this one time and it will effect everywhere in project.

import { DataTablePagerComponent } from "@swimlane/ngx-datatable";

declare module '@swimlane/ngx-datatable/release/components/footer/pager.component' { 

}

DataTablePagerComponent.prototype.calcPages = function (page) {
    console.log('DataTablePagerComponent.prototype.calcPages');
    var pages = [];
    var startPage = 1;
    var endPage = this.totalPages;
    var maxSize = 3;
    var isMaxSized = maxSize < this.totalPages;
    page = page || this.page;
    if (isMaxSized) {
        startPage = page - Math.floor(maxSize / 2);
        endPage = page + Math.floor(maxSize / 2);
        if (startPage < 1) {
            startPage = 1;
            endPage = Math.min(startPage + maxSize - 1, this.totalPages);
        }
        else if (endPage > this.totalPages) {
            startPage = Math.max(this.totalPages - maxSize + 1, 1);
            endPage = this.totalPages;
        }
    }
    for (var num = startPage; num <= endPage; num++) {
        pages.push({
            number: num,
            text: num
        });
    }
    return pages;
};
santanu720 commented 4 years ago

image need left alignment who can i ?

{{row.submiteddate}} {{row.currency}} {{row.address}} {{row.amount}} {{row.snapamount}} {{row.status}} {{row.hash}}
santanu720 commented 4 years ago

angular 8

i already play with scss but not working

image need left alignment who can i ?

      <ngx-datatable class="bootstrap" [rows]="rows" [columnMode]="'force'"
            [headerHeight]="50" [footerHeight]="50" [rowHeight]="'auto'" [limit]="10" [sorts]="[{prop: 'submiteddate', dir: 'desc'}]">
            <ngx-datatable-column name="Submited Date">
                <ng-template let-row="row" ngx-datatable-cell-template>
                    {{row.submiteddate}}
                </ng-template>
            </ngx-datatable-column>
            <ngx-datatable-column name="Currency">
                <ng-template let-row="row" ngx-datatable-cell-template>
                    {{row.currency}}
                </ng-template>
            </ngx-datatable-column>
            <ngx-datatable-column name="Address">
                <ng-template let-row="row" ngx-datatable-cell-template>
                    {{row.address}}
                </ng-template>
            </ngx-datatable-column>
          <ngx-datatable-column name="Amount">
                <ng-template let-row="row" ngx-datatable-cell-template>
                    {{row.amount}}
                </ng-template>
            </ngx-datatable-column>
          <ngx-datatable-column name="Snap Amount">
                <ng-template let-row="row" ngx-datatable-cell-template>
                    {{row.snapamount}}
                </ng-template>
            </ngx-datatable-column>
          <ngx-datatable-column name="Status">
                <ng-template let-row="row" ngx-datatable-cell-template>
                    {{row.status}}
                </ng-template>
            </ngx-datatable-column>
          <ngx-datatable-column name="Hash">
                <ng-template let-row="row" ngx-datatable-cell-template>
                    {{row.hash}}
                </ng-template>
            </ngx-datatable-column>
        </ngx-datatable>

    </div>