olalonde / handlebars-paginate

Pagination helper for Handlebars.
http://syskall.com/pagination-with-handlebars/
59 stars 21 forks source link

Can I do a dynamic page count? #20

Closed benbagley closed 6 years ago

benbagley commented 6 years ago

Here is my users page

https://gyazo.com/981af3a4c3c065ba4f31c055c2fd5977

As you can see I only have two users here so ideally only one button in the pagination should show here.

In the production app I have 20 users and I have limited the page to only show 6 records in the using the page prefix in the pagination settings however I am still getting 10 pages in the pagination.

Can this be dynamic so the right about of pages in the pagination given the amount of records created?

benbagley commented 6 years ago

FYI here is the route

router.get('/dashboard/users', ensureAuthenticated, (req, res) => {
  User.find({}, function(err, users) {
    res.render('dashboard/users/index.hbs', {
      pageTitle: 'Users',
      users: users,
      pagination: {
        page: 6,
        pageCount: 10
      }
    });
  })
});
jimf commented 6 years ago

I should document the options better, but I believe what you're looking for is the limit option, which controls how many pages are displayed at a time. For example:

{
  pagination: {
    page: 6,
    pageCount: 10,
    limit: 5
  }
}

limit behaves like a moving window, so once you start getting into the middle ranges, rather than displaying the next n, it displays pages before and after the current page.

benbagley commented 6 years ago

I'll give it a shot, thanks @jimf

benbagley commented 6 years ago

Ok here's what I get now @jimf https://gyazo.com/546071207377595a763acb49f92c2dd0 so see how 10 pages are still showing, obviously there is only one page of records here not 10.

So as more users are added the pagination should ideally be able to look at the amount of records in the collection (in this case users) and change the pagination accordingly i.e if there are 12 records two pages should show in the pagination not 10.

jimf commented 6 years ago

Okay, if you can provide the pagination portion of your Handlebars template, as well as the exact behavior you're trying to achieve, I'll try to see if we can figure this out. I'm pretty confident that what you want can be achieved.

benbagley commented 6 years ago

Ok here's what I have in the pagination so far @jimf

<nav aria-label="Page navigation">
  <ul class="pagination">
  {{#paginate pagination type="first"}}
  <li class="page-item{{#if disabled}} disabled{{/if}}">
    <a class="page-link" href="?page={{n}}" aria-label="First">
      <span aria-hidden="true">&laquo;</span>
      <span class="sr-only">First</span>
    </a>
  </li>
  {{/paginate}}
  {{#paginate pagination type="middle" limit="10"}}
  <li class="page-item"><a class="page-link{{#if disabled}} disabled{{/if}}" href="?page={{n}}">{{n}}</a></li>
  {{/paginate}}
  {{#paginate pagination type="last"}}
  <li class="page-item{{#if disabled}} disabled{{/if}}">
    <a class="page-link" href="?page={{n}}" aria-label="Last">
      <span aria-hidden="true">&raquo;</span>
      <span class="sr-only">Last</span>
    </a>
  </li>
  {{/paginate}}
  </ul>
</nav>

The behaviour I'm trying to accomplish here is explained in previous posts.

jimf commented 6 years ago

Sorry, I had a bit of a brainfart with what you were asking. I believe I have this working, but admittedly it's not entirely baked into this library. I got it working by decorating the plugin:

const Handlebars = require('handlebars');
const paginateHelper = require('handlebars-paginate');

function paginateWithDynamicLimit(origPaginateHelper) {
  return function(paginate, options) {
    if (options.hash.maxlimit) {
      options.hash.limit = Math.min(Number(options.hash.maxlimit), paginate.pageCount);
    }
    return origPaginateHelper(paginate, options);
  };
}

Handlebars.registerHelper('paginate', paginateWithDynamicLimit(paginateHelper));

This adds a new option maxlimit, which behaves similarly to limit (it in fact uses limit under the hood), but will use the lesser value of pagination.pageCount and whatever you set maxlimit to. You may need to adjust as needed.

Does this achieve what you're trying to do?

jimf commented 6 years ago

Slightly shorter/less noisy version if you aren't gaining anything with the extra function:

const Handlebars = require('handlebars');
const paginateHelper = require('handlebars-paginate');

Handlebars.registerHelper('paginate', function(paginate, options) {
  if (options.hash.maxlimit) {
    options.hash.limit = Math.min(Number(options.hash.maxlimit), paginate.pageCount);
  }
  return paginateHelper(paginate, options);
});
benbagley commented 6 years ago

Looks good @jimf I'll give it a shot. Just so I'm not confusing myself should I now have something like this?

Route

// /users
router.get('/dashboard/users', ensureAuthenticated, (req, res) => {
  User.find({}, function(err, users) {
    res.render('dashboard/users/index.hbs', {
      pageTitle: 'Users',
      users: users,
      pagination: {
        page: 6,
        pageCount: 10,
        limit: 5
      }
    });
  })
});

view

<nav aria-label="Page navigation">
  <ul class="pagination">
  {{#paginate pagination type="first"}}
  <li class="page-item{{#if disabled}} disabled{{/if}}">
    <a class="page-link" href="?page={{n}}" aria-label="First">
      <span aria-hidden="true">&laquo;</span>
      <span class="sr-only">First</span>
    </a>
  </li>
  {{/paginate}}
  {{#paginate pagination type="middle" limit="10"}}
  <li class="page-item"><a class="page-link{{#if disabled}} disabled{{/if}}" href="?page={{n}}">{{n}}</a></li>
  {{/paginate}}
  {{#paginate pagination type="last"}}
  <li class="page-item{{#if disabled}} disabled{{/if}}">
    <a class="page-link" href="?page={{n}}" aria-label="Last">
      <span aria-hidden="true">&raquo;</span>
      <span class="sr-only">Last</span>
    </a>
  </li>
  {{/paginate}}
  </ul>
</nav>

app.js

hbs .registerHelper('paginate', function(paginate, options) {
  if (options.hash.maxlimit) {
    options.hash.limit = Math.min(Number(options.hash.maxlimit), paginate.pageCount);
  }
  return paginateHelper(paginate, options);
});

Do I need to change the route options at all?

jimf commented 6 years ago

Should only require 2 changes:

  1. Wrap the handlebars-paginate export with the above when you register it as a helper
  2. Replace limit in your Handlebars template(s) with maxlimit (feel free to rename this)
benbagley commented 6 years ago

Ok so I now have the following

// /users
router.get('/dashboard/users', ensureAuthenticated, (req, res) => {
  User.find({}, function(err, users) {
    res.render('dashboard/users/index.hbs', {
      pageTitle: 'Users',
      users: users,
      pagination: {
        page: 6,
        pageCount: 10,
        maxlimit: 5
      }
    });
  })
});

and

const hbs = require('hbs'); // Handlebars
const paginateHelper = require('handlebars-paginate');

hbs.registerHelper('paginate', function(paginate, options) {
  if (options.hash.maxlimit) {
    options.hash.limit = Math.min(Number(options.hash.maxlimit), paginate.pageCount);
  }
  return paginateHelper(paginate, options);
});

I'm getting the following https://gyazo.com/518a681f21db96e13f6d2c9311ba24f2

If I'm missing something obvious let me know, still new to express and node 👍

jimf commented 6 years ago

maxlimit is a template option. Try removing it from the pagination object and adding it in your template:

// /users
router.get('/dashboard/users', ensureAuthenticated, (req, res) => {
  User.find({}, function(err, users) {
    res.render('dashboard/users/index.hbs', {
      pageTitle: 'Users',
      users: users,
      pagination: {
        page: 6,
        pageCount: 10
      }
    });
  })
});
<snip>
{{#paginate pagination type="middle" maxlimit="10"}}
<li class="page-item"><a class="page-link{{#if disabled}} disabled{{/if}}" href="?page={{n}}">{{n}}</a></li>
{{/paginate}}
<snip>

That said, you are still saying you have 10 pages worth of users. Just so I'm clear, are you trying to effectively limit the number of pages displayed to 10, but if you have less than 10 pages, to stop at whatever that number is? If so, then so long as pageCount is >= 10, I would expect 10 page numbers to get rendered.

jimf commented 6 years ago

Actually, I'm now realizing that the wrapper I wrote above isn't even achieving anything. handlebars-paginate shouldn't be rendering page numbers for pages that don't exist. I think you might be misusing one or more of the options. For reference:

handlebars-paginate is actually completely agnostic to the number of items you have per page. It's only concerned with what the current page is, and now many there are in total.

benbagley commented 6 years ago

Ok so I'm still getting the same here.

That said, you are still saying you have 10 pages worth of users. Just so I'm clear, are you trying to effectively limit the number of pages displayed to 10, but if you have less than 10 pages, to stop at whatever that number is? If so, then so long as pageCount is >= 10, I would expect 10 page numbers to get rendered.

Yes that's what I'm trying to do here, so for an example let's say I limit to 6 records per page.

So in theory the output should look like this:

6 records = 1 page in the pagination 12 records = 2 pages in the pagination 18 records = 3 pages in the pagination

etc...

What I'm trying to do here is only have the right amount of pages listed in the pagination, if there aren't 10 pages of records, then a pagination of 10 shouldn't show.

jimf commented 6 years ago

See my most recent comment. I believe your context.pagination options are incorrect. { page: 6, pageCount: 10 } translates to I'm on page 6 of 10.

benbagley commented 6 years ago

Yes but I don't want 10 pagination links when there aren't 10 pages of records.

If there is three pages of records only three pagination links should show. Not 10.

jimf commented 6 years ago

When there are 3 pages of records, you need to set pageCount to 3.

benbagley commented 6 years ago

So have to set it manually? Anyway to make this dynamic?

jimf commented 6 years ago

You're getting into the realm of your template rendering lifecycle, which is out of the scope of this library. If your collection changes after initial render, you need to render your pagination template again if you want that state to be reflected in your UI.