(You are Anonymous)

Paging Data Between Multiple Screens

This is summary of suggestions for paging data between screens.

Looking through CPAN, an "obvious" solution to this might seem to be HTML::Pager, because it's built to work with HTML::Template, which already integrates with CGI::App.

Looking closely at HTML::Pager, it has a number of features that make it undesirable:

  • The interface seemed more complex than is needed
  • It relies on JavaScript, which isn't necessary
  • It relies on HTML embedded in the code. Even if you use a template, there are some parts of the output that you can't change, so you may not be able to achieve a specific look, or have your page validate under the HTML/XHTML standard that you'd like.

Paging modules based on Data::Page are recommended. They include:

At first glance, Data::Page may seem too simple. It doesn't include any structure that includes "all the pages". This 'talk' about Data::Pageset may help you to understand it better

Data::Page doesn't need to be tied to any display system, and it's not. It has a very easy to use and comprehensible interface.

To generate a string of each page's number starting with 1 (which can be used as a line on a web page so the reader can navigate to other pages of data), all you need to know is the number of the last page. You get that from the last_page method.

my $pager = Data::Page->new();

$pager->total_entries( '125' ); # Need to have queried your database to get this number
$pager->entries_per_page( '20' ); # You pick
$pager->current_page( '1' );

my $pages_string;
for ( 1 .. $pager->last_page ) {
   $pages_string .= $_ . " ";
}

print "<p>" . $pages_string . "</p>"; 
# ... yields <p>1 2 3 4 5 6 7</p>

To generate a "[To next page]" HTML link, for example, change the value of a CGI script's page parameter by adding 1 to the value it has when the script is called. This is no big deal, of course, but the win is that the resulting HTML page can show updated entry number information ("Now showing entries 41 through 60").

Clicking on any of the page number links on a page generated by the following script produces a new page that shows the updated entry number information and uses the Data::Page methods to determine whether to display "[To previous page]" and "[To next page]" in the new page's navigation string.

#!/usr/local/bin/perl

use strict;
use CGI;
use Data::Page;

my $q = new CGI;

# Here you might fiddle with some incoming parameters
# from the script's URL; resetting a parameter as in
# $q->param( 'color', 'blue' ) will enable CGI.pm to
# include the reset value as part of $q->self_url
# which we'll use below to creating a URL back to
# this script.

my $pager = Data::Page->new();

$pager->total_entries('125');     # Need to have queried your database to get this number
$pager->entries_per_page('20');   # You pick
my $page =
  defined $q->param('page') && $q->param('page')
  ? $q->param('page')
  : 1;

$pager->current_page($page);
my $first = $pager->first;
my $last  = $pager->last;

print $q->header;
print "<p>Now viewing entries $first through $last</p>";

# Insert code here to fetch and display the contents of
# entries number $first through number $last from your database.

unless ( $page eq $pager->first_page ) {
    $q->param( 'page', $page - 1 );
    print q{<a href="} . $q->self_url . q{">[To previous page]</a> };
}

my $pages_string;
for ( 1 .. $pager->last_page ) {
    $q->param( 'page', $_ );
    $pages_string .=
      q{  <a href="} . $q->self_url . q{">} . $_ . "</a>" . " ";
}

print $pages_string;

unless ( $page eq $pager->last_page ) {
    $q->param( 'page', $page + 1 );
    print q{ <a href="} . $q->self_url . q{">[To next page]</a>};
}

If you are paging data from database, you can usually get the data you need with two queries that share the same 'where' clause. The first one simply selects "COUNT(*)" to find out the total number of items in the result set. The second query selects just the data you need for the page you are displaying. The SQL standard has "LIMIT" and "OFFSET" for this purpose. Both PostgreSQL and MySQL support this.

See Also