A simple pagination concern

“Ugh, I need to implement pagination, again.” Whether it’s pagination or something else, I’m never very fond of re-implementing or going on the search for yet another gem to take care of features that are trivially popular. Even as someone with a deep appreciation of Rails, this feels to me like a missing part. One more papercut, of sorts.

A May of WTFs thread, Active Pagination and Active Search, highlighted that need as well, with 600 views for now—an arguably popular topic overall.

My wish would be to write one line of code, and get sensible defaults and conventions, no decisions to make. So I went ahead and did it for my own need.

In my opinion, something like this should already be in Rails. Apparently it was in Rails 1, and it got removed for reasons which were probably sensible at the time.

I also saw that there were proposals in the works for an ActiveSearch module of some kind, but I thought I’d start smaller: just a simple limit-offset pagination.

Here it is:

# app/controllers/concerns/pagination.rb

module Pagination
  extend ActiveSupport::Concern

  def default_per_page
    25
  end

  def page_no
    params[:page]&.to_i || 1
  end

  def per_page
    params[:per_page]&.to_i || default_per_page
  end

  def paginate_offset
    (page_no-1)*per_page
  end

  def paginate
    ->(it){ it.limit(per_page).offset(paginate_offset) }
  end
end

It makes all these helpful methods available in any controller you include it. If like me, you include it in ApplicationController:

class ApplicationController < EvenMoreAbstractController
  include Pagination
end

It does not invoke itself everywhere, but you have access to it anywhere.

Usable like so:
(thanks to Ruby’s new-ish #then method)

def index
  records = Record.all
  records.then(&paginate)
end

# which is a shortcut for

def index
  records = Record.all
  records.limit(per_page).offset(paginate_offset)
end

The param names I’ve chosen are :page and :per_page, both optional. eg:

  • GET /records
  • GET /records?page=3
  • GET /records?page=2&per_page=100.

Done.

It is not perfect, I haven’t tested it extensively, but it feels to me like a simple, sensible defaults approach to pagination.

I would dream of a more polished version getting included in core, but I don’t have any expectations. I’m personally looking forward to never caring about this again, and gladly invite anyone along :slight_smile:

Let’s care about more interesting problems, my friends!

2 Likes

Thanks!

Is the kaminari gem a good option?

Have you found out what were the reasons for removing it in Rails 1. I don’t remember them, but would like to revise them.

This is a really nice and readable implementation. I think when it comes to being a drop-in, plug-and-play pagination scheme, you’re going to have to go down the slippery slope of the view layer, and add the options for page links, and the defaults plus overrides for windowing and so forth. Once you start down that path, where do you stop? Is there a default set of CSS for the links? Before long, you’ve written Kaminari or Pagy.

Walter

Actually, no!

Kaminari was the best until Pagy showed up, and just smoked it, performance-wise.

I have a hard time picturing a more-perfect solution at this stage.

Thanks for the blast-from-the-past. I’d completely forgotten that Rails 1 had a built-in pagination helper!

Hey there, pagy sounds perfect! Can pagy adjust the item number per page to odd or even depending on the number of columns in the grid issued by a vw mediaquery?

Could you ask that in a different way, because I’m not following.

Pagy is ultimately going to emit an HTML fragment based on the configuration options and recordset data that you give it.

That’ll happen on the server, though… so unless you convey the result of your mediaquery to the server as part of your request, Pagy’s role will already be complete before CSS rules are applied on the client.

It is highly configurable, though… so if you want to compute different outputs based on user screen size, you can get clever with your request.

Still, it might just be easier to hide columns that you don’t need on smaller devices using responsive breakpoints. As usual, there’s no one correct way to approach this.

1 Like

I have a gallery of images - paginated. If I limit to an even number I will have an orphan space in the last row of a 3 column layout. I want an odd number limit for that case and an even number for the 2/4/6 column view. I would have thought this is most important for a pagination logic to ask: columns - odd / even? But like you said - the logic is done before the view kicks in? So is this done in some gallery js usually?

maybe the pagination should load one flex container and the flex container does the right thing… but then I don’t need the pagination anymore - always 1 item per page …

I understand, now… though I do not approve. :wink:

My strong suggestion is that you should just go with the even layout and leave the last one blank or even just wrap it around - I think Pagy actually has an endless loop mode!

The reason is simple: having a gallery layout that randomly switches between column counts sounds like a UX nightmare. I cannot think of a single time in 25 years where I’ve seen a layout change potentially between page loads based on whether there is an odd number of widgets or not.

Ask yourself: what serves the interest of your user? What is least likely to confuse them?

I understand your point but as a user seeing the last space blank on page one tells me “that’s it. no more images coming, we don’t even have one to complete the last row”. :wink:

… yeah, maybe the endless page is the way to go, thank you.

I don’t know much about what you’re working on, but I have built enough navigation mechanisms to know that most users don’t tend to scroll until the end of the internet.

Still, I’m picturing looking at two users’ photo galleries, or paging through a gallery while new photos are being uploaded… and having the layout change fairly dramatically for no apparent reason is just not something I can get behind. And all so they can arrive at a conclusion where they are supposed to intuit that the reason they were seeing either even or odd numbers of widgets is because they don’t have to see an empty space at the end… it’s just a weird UX to reason about.

I’m happy to be wrong, though; can you point me to any popular examples of galleries on the web that change the number of columns based on whether there are an even or odd number of total records?

I am not talking about changing the layout during page loads but to settle for one or the other depending on the device/window the pages is rendered on.

Take a gallery of paintings on a webpage (never mind widgets - they fit everywhere). Your browser window is wide enough for 4 paintings per row. You get a last row on that page with 4 paintings if the pagy limit per page is an even number. The dude next door wants his browser window a bit smaller and only 3 paintings fit per row so he would get a blank space in the last row on every single page. Unless we fight for him and tell pagy to change the per page limit to an odd number. And no, I have no examples because it seems to be a hard problem. :slight_smile: I appreciate your input.

I am happy about page 234 (the last one) having a blank space at the very end of course, that’s ok, totally respected

considering that 6 (paintings portrait) can be maximum number per row, I have only 5 and 3 to worry about, single is phone and sorted… hmm

this one does what I want:

https://preview.colorlib.com/#photosen

you can resize the width and the gallery goes from 4 to 3 per row and no blank space bottom row. How is that done?

you have to click on one of the “more photos” to see it

… and then they go and repeat image sequences by reversing order so you always get 2 identical beside each other, thats a hard problem too hehe.

Respectfully, I think it’s less a hard problem then people universally understand that it’s a UX anti-pattern. There’s lots of solutions for actual hard problems out there.

What you’re failing to consider is that the total number of widgets in a gallery can change while you’re looking at it, between page loads.

People also resize their browsers.

If this is really keeping you up at night, you should investigate just using something like Isotope and leave Pagy out of it.

I concur but the total number can change as it sees fit whenever - still irrelevant for the gallery page layout. I am on about the per page number of images/widgets - 3 columns is odd and 4 columns is even. A simple pagination concern. Would be nice to have no orphan space in either.

No biggie though :slight_smile: and since I am a total noob there is probably a solution for that and I just have to find it. isotope looks great, thanks for that - will check it out.