Skip to content

feat(pagination): Add custom pagination with variable page sizes #5174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 16, 2016

Conversation

chatcher
Copy link
Contributor

Use Case:
Client requests page breaks by (e.g.) Date column. This can currently be
accomplished by external pagination, but the 'page size' dropdown
becomes irrelevant, and the page-count on the right can no longer
correctly determine the correct row range.

The pagination template has been modified to hide the page-size dropdown
when custom pagination is enabled, and to use new API methods for the
row range.

A new tutorial page has been added as lesson 406-Custom-Pagination
which demonstrates two usages: pre-sorted data, with pre-calculated pages
sizes; and unsorted data, with external pagination.

Use Case:
 Client requests page breaks by Date column. This can currently be
accomplished by external pagination, but the 'page size' dropdown
becomes irrelevant, and the page-count on the right can no longer
correctly determine the correct row range.

The pagination template has been modified to hide the page-size dropdown
then custom pagination is enabled, and to use new API methods for the
row range.
@chatcher chatcher closed this Feb 28, 2016
@chatcher chatcher reopened this Feb 28, 2016
@chatcher
Copy link
Contributor Author

chatcher commented Feb 28, 2016

hmm, this passes locally...
Tests have been fixed.
New tests have been added.

@AgDude
Copy link
Contributor

AgDude commented Jul 30, 2016

This looks like a very specific use case. I think I would prefer to see this in an independent plugin (or all of the pagination UI in a plugin for that matter).

@mportuga
Copy link
Member

@chatcher Can you fix unit tests?

page index and page number were confused.
Copy link
Member

@mportuga mportuga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made some comments on your code now. I had some questions, one request and one suggestion. Thank you for fixing the unit tests earlier however.

@@ -146,12 +176,14 @@
var visibleRows = renderableRows.filter(function (row) { return row.visible; });
grid.options.totalItems = visibleRows.length;

var firstRow = (currentPage - 1) * pageSize;
var firstRow = publicApi.methods.pagination.getFirstRowIndex();
var lastRow = publicApi.methods.pagination.getLastRowIndex();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These definitions can be comma separated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like so?

var firstRow = blahBlah(),
    lastRow  = blahBlah();

I don't see that pattern used elsewhere. Can you clarify what you mean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is what I meant. But I suppose you are right and we are not using this anywhere else in this project right now. It is my personal preference. No need to change it, in that case.

if (firstRow > visibleRows.length) {
currentPage = grid.options.paginationCurrentPage = 1;
firstRow = (currentPage - 1) * pageSize;
}
return visibleRows.slice(firstRow, firstRow + pageSize);
return visibleRows.slice(firstRow, lastRow);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your function for getLastRowIndex doesn't appear to match the firstRow + pageSize calculation that I see here. Am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

firstRow is (currentPage - 1) * pageSize
Previously, lastRow was firstRow + pageSize, which is algebraically

lR = fR + pS          | fR = (cP - 1) * pS
   = (cP-1) * pS + pS | distribute pS over (cP-1)
   = cP*pS - pS + pS  | cancel addition
   = cP*pS

With the change, lastRow is currentPage * pageSize (or totalItems), which simply eliminates the math in the middle.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the lesson in algebra right now. I should have caught that.

if (grid.options.useCustomPagination) {
return publicApi.methods.pagination.getFirstRowIndex() + grid.options.paginationPageSizes[grid.options.paginationCurrentPage - 1];
}
return Math.min(grid.options.paginationCurrentPage * grid.options.paginationPageSize, grid.options.totalItems);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is Math.min being used here? That doesn't appear to be there in the original function for when useCustomPagination is false. Shouldn't this be the following?
publicApi.methods.pagination.getFirstRowIndex() + grid.options.paginationPageSize

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was taken from $scope.showingHigh (line 372), which is replaced by 1+getLastRowIndex().

@@ -368,8 +410,6 @@
$scope.$on('$destroy', dataChangeDereg);

var setShowing = function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is an empty function now. Was that on purpose? If so, what is the intention here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was in the original code; I could not find a usage or reassignment of it, but I didn't want to touch it in case I was wrong.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set showing is called in lines 415 and 432. One of those usages also creates a watch, I would argue that if this function can definitely stay empty that you should remove this function, the watch on line 415 and the usage on line 432. Also, the call to destroy said watch on line 438

@@ -64,6 +64,8 @@ describe('ui.grid.pagination uiGridPaginationService', function () {
describe('initialisation', function () {
it('registers the API and methods', function () {
expect(gridApi.pagination.getPage).toEqual(jasmine.any(Function));
expect(gridApi.pagination.getFirstRowIndex).toEqual(jasmine.any(Function));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add more unit tests for these two functions than simply checking if they exist and are functions. Would you mind adding tests to ensure they return what is expected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added tests for custom pagination.

Rather than testing getFirstRowIndex and getLastRowIndex directly, we test the resulting pages.

The previous tests covered one branch inside the two methods, and the tests I've added cover the other branch.

@@ -368,8 +410,6 @@
$scope.$on('$destroy', dataChangeDereg);

var setShowing = function () {
$scope.showingLow = ((options.paginationCurrentPage - 1) * options.paginationPageSize) + 1;
$scope.showingHigh = Math.min(options.paginationCurrentPage * options.paginationPageSize, options.totalItems);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are showingHight and last page index really the same thing? If so, how did you come to that conclusion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The usage of showingLow and showingHigh was in the template to show the row range.
Instead of calculating the range here, I instead call the new methods from the template and add one there.

Since custom pagination allows for arbitrary pages of rows, we pick some
feature of the existing data to paginate by.

I've chosen the approximate number of pencil strokes to write the
letters on column 2 (with a line through 'Z' so that it gets four
strokes, and we can have two pages with 4 rows).
Copy link
Contributor Author

@chatcher chatcher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Defend usage of Math.min() in getLastRowIndex().
  • Address comma-separated var declarations.
  • Clarify calculation changes on getLastRowIndex()
  • Remove empty setShowing() function and related watcher.
  • Defend removal of showingLow and showingHigh.
  • Add more unit tests to ensure quality.

Copy link
Member

@mportuga mportuga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your changes!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants