Paged Scopes, Part III: Paginating Your Views
In my two previous articles, I’ve been describing my new Paged Scopes pagination gem for ActiveRecord and Rails. In this final installment I’ll describe how to use the library to render pagination links in your views.
Pagination Links
The basic idea is to render a row of numbered links for a few pages either side of the one being viewed. This is referred to as the inner window. An outer window is often also included – this shows links for the first and last few pages at the start and end of the list. Usually, next page and previous page links are also sandwiched around the numbered links.
The will_paginate rdoc has some good links to articles on pagination UI design:
- a Yahoo Design Pattern Library article describing two styles of pagination;
- a Smashing Magazine article with good practices and examples; and
- another article with heaps of examples, both good and bad.
In the will_paginate gem, the eponymous will_paginate view helper is provided to render these links in your view. It works well, but one look at the method’s options gives you an idea what you’ll be up for if you want to customize the HTML structure of your pagination links. Want to render your pagination links as a list? You’ll have to write your own LinkRenderer subclass.
There has to be a better way. There is of course, and it comes from a less-is-more approach.
Using the Window Helper
With the PagedScopes gem, each page has an associated paginator which provides some simple methods for generating page links. First, we need to call set_path to tell the paginator how to generate links for a pages:
@page.paginator.set_path { |page| page_articles_path(page) }The block we supply will be used by the paginator to generate a paged URL whenever one is needed. (Alternately, we can set this in the controller using the :path option in the paginate method.)
Next, we use the window method to render the page links. We supply a block which the paginator will call for each page in the window, allowing us to render the link exactly as we want to. Let’s render that list we were talking about:
<% @page.paginator.window(:inner => 2, :outer => 1) do |page, path, classes| %> <% content_tag_for :li, page, :class => classes.join(" ") do %> <%= link_to_if path, page.number, path %> <% end %> <% end %>
Here we’ve specified an inner window of size 2 (meaning we want links for two pages either side of the current page) and an outer window of size 1 (meaning we want links for just the first and last pages).
The window helper passes a succession of pages to our block for us to render. The block arguments are:
- The page itself, from which we can get the page number.
- The path for the page, produced using the set_path proc we’ve already specified. If the page is the current page, then nil is passed as the path – this is because we shouldn’t render a link for the current page. (Hence our use of link_to_if.)
- An optional array of classes describing the link. Possible values for the classes are :selected if the page is the current page, :gap_before if there’s a gap in the numbering before the page, and :gap_after if there’s a gap after. You can use these as you see fit, but they’re intended to be passed through to your link container as classes for styling. (We’ve done this above with the :class => classes.join(" ") option.)
Within the block, the page link can be rendered as we please. In our example we’re putting it inside an element. For page 7, the window function would produce the following markup:
class="page gap_after" id="page_1"> href="/pages/1/articles">1 class="page gap_before" id="page_5"> href="/pages/5/articles">5 class="page" id="page_6"> href="/pages/6/articles">6 class="page selected" id="page_7"> 7 class="page" id="page_8"> href="/pages/8/articles">8 class="page gap_after" id="page_9"> href="/pages/9/articles">9 class="page gap_before" id="page_12"> href="/pages/12/articles">12
Styling the Output
Add some styling, using our classes to distinguish the currently selected pages and to add a separator where there are numbering gaps:
li.page { display: inline } li.page a { text-decoration: none } li.page span { border: 1px solid gray; padding: 0.2em 0.5em } li.page.selected span, li.page span:hover { background: gray; color: white } li.page.gap_before:before { content: "..." }
The result: a nice-looking set of page links.
Too easy!
Adding Extra Controls
How do we get add previous and next links? This is pretty easy, too – just specify the :extras we want as an option. (Choose from :first, :previous, :next and :last.) Those symbols will be passed to our block as the page when they need to be rendered.
We’ll move our pagination links to a helper for clarity:
module ArticlesHelper MARKER = { :previous => "< newer", :next => "older >" } def article_page_links @page.paginator.window(:inner => 2, :outer => 1, :extras => [ :previous, :next ]) do |page, path, classes| content_tag :li, :class => (classes << :page).join(" ") do content_tag :li, link_to_if(path, MARKER[page] || page.number, path) end end end end>
Which renders as follows (for page 4 this time):
Just what we want!
Links for the :first and :last pages can also be specified as extras; these will appear outside the previous and next links. (If you use these extras, you’ll want to omit the :outer window option.)
Wrap-Up
Finally! That’s just about it for the Paged Scopes pagination gem. There’s a few small bits and pieces I haven’t described, but you’ll find them in the code if you need them.
Once again, to install:
gem sources -a http://gems.github.com # just once sudo gem install mholling-paged_scopes
Enjoy! And comment away. (But be nice while you do.)


Recent comments
1 year 23 weeks ago
1 year 23 weeks ago
1 year 25 weeks ago
1 year 27 weeks ago
1 year 42 weeks ago
1 year 45 weeks ago
1 year 45 weeks ago
1 year 45 weeks ago
1 year 46 weeks ago
1 year 48 weeks ago