Krang::HTMLPager - Web-paginate lists of records
# In your Krang::CGI::* module...
use Krang::ClassLoader 'HTMLPager';
# In a run-mode, instantiate new pager object...
my $pager = pkg('HTMLPager')->new(
cgi_query => $query,
use_module => pkg('Contrib'),
columns => ['last', 'first', 'command_column', 'checkbox_column'],
columns_sortable => ['last', 'first'],
command_column_commands => ['edit_contrib'],
id_handler => sub { return $_[0]->contrib_id },
row_handler => sub { $_[0]->{last} = $_[1]->last(); $_[0]->{first} = $_[1]->first(); },
);
# Run the pager
my $pager_html = $pager->output();
$template->param(pager_html => $pager_html);
# In your HTML::Template file...
<!-- pkg('HTMLPager') Output START -->
<tmpl_var pager_html>
<!-- pkg('HTMLPager') Output END -->
The primary purpose of Krang::HTMLPager is to allow Krang-style page able lists of results to be easily created. The secondary purpose is to enforce a standard function and appearance to these lists.
The pager interface is designed to work specifically with the Krang system, and to be as simple to use as possible. It is modeled after HTML::Pager, but is more specialized for use with Krang. In particular Krang::HTMLPager provides the following functions which are unique to Krang:
find() methods
Krang::HTMLPager implements the following primary methods:
new()
my $pager = pkg('HTMLPager')->new(%pager_props);
The new() method instantiates a new pager. It takes a litany of
parameters, which are documented in full later in this POD in the section
``Krang::HTMLPager Properties''.
output()
my $pager_html = $pager->output();
The output() method is one of two ways to execute a paged view and
utilize the output. This method is intended for use when the standard
built-in pager templates are being employed, as opposed to a custom
pager template.
The output() method runs the Krang::HTMLPager and returns a block
of HTML containing the data output. This is expected to be used in the
context of a larger template:
$template_object->param( pager_html => $pager->output() );
The output returned is contained in a form with the name ``krang_pager_form''. This is important to know if you have a checkbox column on which you want to operate. In this case you are expected to implement a button which calls a javascript function. The javascript function would have to submit the pager form to get access to the checked rows. For example:
function delete_selected () {
var myform = document.forms["krang_pager_form"];
myform.rm.value = "delete_selected";
myform.submit();
}
This assumes that your run-mode parameter is ``rm'' and that you have
set ``rm'' to be included in the pager form via the persist_vars
pager property.
fill_template()
$pager->fill_template($template_object);
The fill_template() method is one of two ways to execute a paged view
and utilize the output. This method is used in the context of a custom
pager template. The section later in this POD, ``Creating Custom Pager
Templates'', more fully describes how and why you would want to use a
custom template.
The fill_template() method runs the Krang::HTMLPager and sets template
variables in the $template_object you provide. It is then your
responsibility to output that $template_object.
row_count()
my $row_count = $pager->row_count();
The row_count() method returns the number of rows output by the pager.
It is expected to be called after output() or fill_template().
Until one of those methods are called row_count() will return undef.
This method is useful for changing the UI based on whether or not any results were found.
make_internal_template()
my $template = $pager->make_internal_template();
The make_internal_template() method returns a dynamically created
template for use with Krang::HTMLPager. This method is used internally
by output() to generate a template based on your specification.
This method is made public to assist in the creation of custom pager templates. Using this method you can specify and create a template which can be saved to a file and customized as needed.
$pager->column_display(status => 1, ...)
This method allows to control whether certain columns should be displayed or not. It is meant to be used in row handlers. This way, columns that would be displayed per default can be hidden depending on some row object property. Likewise, columns that would be hidden (see the property 'columns_hidden' below) can be shown.
For each column name passed to this method, the pager creates a special tmpl_if that can be used to actually control the column display in the templates:
<tmpl_if __show_status__><td><tmpl_var status></tmpl_if>
This method can be called multiple times for the same column name allowing for more involved control schemes.
Displaying the list buttons can be controlled via the checkbox_column display.
Krang::HTMLPager expects a number of parameters to be set via the
new() method. These parameters set properties which are used to
create a specification for your pager, such as the list of columns and
which of those columns are sortable.
Following is a list of the parameters for Krang::HTMLPager. These parameters are also accessible via object methods.
cgi_query => $query
Contains the CGI.pm query object for this request. The query
object is needed to read in the pager state parameters, such
as krang_pager_curr_page_num, krang_pager_sort_field, and
krang_pager_sort_order.
persist_vars => {
rm => 'search',
search_filter => $search_filter,
}
A hashref containing the names and values of CGI parameters which should be remembered as hidden data within the pager form. This is necessary for maintaining web application state. The pager expects to have its own form for paging through data and re-sorting results.
Values set in persist_vars will be implemented via CGI.pm's hidden()
method with -override=>1 set. This will ensure that the value
you specify will be set regardless of the current state of that form
parameter.
use_module => pkg('Contrib')
The name of the Krang object module which contains the find() method
pager should use for retrieving data. This module's find() method will
be used for handling all queries, sorting, and paging (limit, offset).
Although it is expected that use_module will specify a Krang object
module, any module which implements a sufficiently compatible find()
method can be used. In this case, ``sufficiently compatible'' means the
following parameters are supported:
In some cases you might already have the data you want to page and you just need HTMLPager to do the formatting. In this case pass in an array ref to your data here instead of providing a value for use_module.
If you are using use_data and what you provided was not the entire dataset, then you can let HTMLPager know what the full size is. This allows it to accurately build the paging links.
HTMLPager uses the session cache to store some information about a paged list to re-use that same information on future requests for the same list. By default we use use_module as the cache key, but you may need to provide your own key if you use the same use_module in multiple places or you are using use_data.
find_params => { simple_search => $q->param('search_filter') }
A hashref containing the search parameters exactly as they should
be passed to find(). This hashref will be augmented to include
parameters related to sorting (order_by, order_desc) and paging
(limit, offset).
columns => [qw( last first_middle type command_column checkbox_column )]
An arrayref containing columns names. This property defines two things: The order of the columns in the table (left to right), and the key names which will be expected in custom templates.
There are two special values which can be included in the list of columns. If included in the column list, these columns will be automagically handled by pager as follows:
A list of actions, implemented as button controls.
A series of checkboxes, one per record/row.
command_column is used for button links such as ``Edit'' or
``View Detail''. How these buttons are configured is described in
more detail below (pager parameters command_column_commands and
command_column_labels).
command_column also sets up the column header as a blank field.
This column header functionality can be overridden via column_labels
below.
checkbox_column is used for interfaces where the user is allowed
to check a set of records for the purpose of processing them in a
particular way. For example, ``delete checked'', ``check out'', and
``associate'' are examples of functionality which use these checkboxes.
Pager will automatically create these checkboxes for you. The
checkboxes will be implemented as CGI form inputs which all are named
krang_pager_rows_checked. The value of each checkbox is set by pager
to be the ``ID'' as returned per row by the id_handler pager parameter
(described below). In HTML, a typical set of checkboxes might look
like this:
<input type="checkbox" name="krang_pager_rows_checked" value="1">
<input type="checkbox" name="krang_pager_rows_checked" value="2">
<input type="checkbox" name="krang_pager_rows_checked" value="3">
<input type="checkbox" name="krang_pager_rows_checked" value="4">
This will allow you to easily retrieve an array of checked rows via CGI.pm:
my @rows_checked = $query->param('krang_pager_rows_checked');
checkbox_column also sets up the column header as a widget through
which ``select all'' and ``un-select all'' functions can be triggered.
This column header functionality can be overridden via column_labels
below.
columns_hidden => [qw( status )]
An arrayref containing the names of columns that should not be displayed per default. Members of this list must also be members of the arrayref 'columns'. Together with column_display(), this list allows to display the named columns depending on some row object property.
For each member of this list, the pager creates a special tmpl_if that
can be used to control the column display in the templates. The
default value of those tmpl_if is '0', but can be changed via
column_display().
B<Example:>
On Retired Asset screens, the status column should normally not show up. There is however one edge case making it desirable to display this column. When searching an asset by ID, all assets are found, no matter whether they are actually retired or live or trashed. A live or trashed asset should indicate its living place and the status column seems the right place for this piece of information. For this edge the status column hidden per default should be displayed nonetheless. The default -- hide the column -- is specified using the 'columns_hidden' list, inverting the default from within a row handler is done via column_display().
<tmpl_if __show_status__><td><tmpl_var status></tmpl_if>
column_labels => { last=>'Last Name', first_middle=>'First, Middle Name' }
A hashref mapping column names (as defined by the columns pager
parameter) to the text label which should appear in the column header
line at the top of the table.
It is not necessary to define a column label for every column. If not
supplied, the column name will be used instead, except in the case of
magic internal column types, command_column and checkbox_column.
A command_column header is automatically set to be blank. You could
use column_labels to change it to ``Commands'' or something else
intuitive. In the case of a checkbox_column, overriding the label
via column_labels is probably undesirable. A checkbox_column
puts a highly functional gadget in the header, which is probably required.
command_column_commands => [qw( edit_contrib )]
An arrayref containing the names of the JavaScript functions to be
called when the user clicks on a particular command. The function will
be called with the ``ID'' (as returned per row by the id_handler pager
parameter described below) as the first argument. Additional parameters
can be passed in via the command_column_extra_args option. For example,
following would be the HTML generated if ID was set to ``4'':
<input onclick="edit_contrib('4')" type="button" class="button">
It is expected that a corresponding JavaScript function would be written to implement the functionality desired when the user clicks on the command link for a particular row.
command_column_extra_args => sub { return ('foo', 'bar') },
A subroutine reference that returns extra parameters that will be passed
to each command_column_commands javascript function in addition to
the row's ID.
This subroutine receives the object for the row being created.
command_column_labels => { edit_contrib => 'Edit' }
A hashref containing a map of command names (as defined by
command_column_commands) to the text which should be in the link
which appears to the user. For example, the above might generate:
<input value="Edit" onclick="edit_contrib('4')" type="button" class="button">
If a label is not defined for a particular command, the name will be used instead.
columns_sortable => [qw( last first_middle )]
An arrayref containing the names of the columns (as defined by columns)
by which the user is allowed to sort. These column headers will be
clickable JavaScript links which will modify the sorting order of the
data listed.
An arrow will appear next to the current sort column. This graphical arrow will identify if the current sort order is ascending (up arrow) or descending (down arrow).
The first item in the list will be regarded as the default sort column.
This column will be used for sort the first time the pager is invoked.
This behavior, combined with the default_sort_order_desc property,
allows you to control the default pager sort behavior.
columns_sort_map => { first_middle => 'first,middle' }
A hashref mapping the name of a sortable column to the string which should
be passed to find() via the order_by parameter. If a particular
sortable column is not specified, its name will be used instead. This is
probably adequate in most cases.
default_sort_order_desc => 1
A scalar containing a Boolean (1 or 0) value. If true, when the pager
is first called the sort order will be set to descending If not
set, this property will default to ``0'' and sort order will consequently
default to ascending.
row_handler => sub { $self->my_row_handler(@_) }
A subroutine reference pointing to a custom function to process each row of data. This function will receive, as arguments, a hashref into which row data should be placed, a reference to the object to be displayed on this row and a reference to the pager object. The job of your custom function is to convert the object attributes into template data and set that data in the hashref. For example:
sub my_row_handler {
my ($self, $row_hashref, $row_obj, $pager) = @_;
$row_hashref->{first_middle} = $row_obj->first() . " " . $row_obj->middle();
$row_hashref->{last} = $row_obj->last();
$row_hashref->{type} = join(", ", ($row_obj->contrib_type_names()));
$pager->column_display('checkbox_column' => 1) if $row_obj->may_edit;
}
The purpose of passing in the pager object is to make the display of list controls (list checkbox and buttons) dependant on some row object attribute. The Trash for example may contain stories, media and templates. If a user has only template edit permission, but the trash contains no templates, the list controls should not be displayed.
id_handler => sub { return $_[0]->contrib_id }
A subroutine reference pointing to a custom function to return a unique identifier for each row of data. This ID is needed for creating the checkbox columns and command columns.
The referenced subroutine receives a reference to the object to be displayed on this row. The job of your custom function is to return a unique identifier for this row.
max_page_links => 10
Set this to the maximum number of page links that the pager should display. Any more pages outside this number will be represented by a ``...'' in the output HTML. If set to 0 all page links are shown. The default is 10.
It is expected that most of the time you will use the output()
method to run the pager and return a rendered block of HTML with your
interface. The HTML which is returned is generated internally within
Krang::HTMLPager.
In some cases, this internally-created HTML will not suffice. You may have to implement a screen which has a slightly different style to it. You may have additional functionality which is not compatible with that provided by the stock Krang::HTMLPager output. In these cases you may want to create your own custom Krang::HTMLPager template to replace the internal one.
This is not a task for the faint of heart. Your template must be
structured to be fully compatible with the internal template in terms
of HTML::Template structures. The easiest way to get started with
a custom template is to have Krang::HTMLPager dynamically generate a
template for you, which you can then customize. This can be done via
the make_internal_template() method:
my $pager = pkg('HTMLPager')->new(%pager_props);
my $template = $pager->make_internal_template();
Refer to make_internal_template() in this document for more details.
The template which is created contains all the variables necessary
in a custom template, for the pager specification (%pager_props)
you have provided. Following is a summary of the variables you
will find.
This tmpl_include brings in a special template containing JavaScript and CGI form elements required for all pagers. (This should not be confused with the ``internal'' template. The template name, pager-internals.tmpl, is coincidental.)
This variable contains the rows of data on the current screen of the
pager. It is expected to be called as a <TMPL_LOOP>. It is
also used in the context of a <TMPL_IF>/<TMPL_ELSE> to provide
alternate output if there are no results -- for instance, a search for
which there are no matching records found.
The number of records found as the result of a search, in total.
The number of the row in the total result set at which the current page is starting its display.
The number of the row in the total result set at which the current page is ending its display.
It is the responsibility of Krang::HTMLPager to build the ``header'' row of the results table. For the most part, this row contains the names of all the columns. Columns which are sortable are made to be links. State information is added to sortable column headers if they are the currently selected sort row. Other behaviors are articulated here, as documented elsewhere in this POD.
In order to implement this functionality, one <TMPL_VAR> is
expected for each column in the pager. These variables are named
using the column name (as defined by the ``columns'' pager property),
with the prefix, ``colhead_''.
The rows within the ``krang_pager_rows'' <TMPL_LOOP> are expected to
each contain one <TMPL_VAR> for each column. These variables are
named using the column name (as defined by the columns pager property).
The pager is expected to provide a link to the previous page
if the current page is not the first page. The <TMPL_VAR>
prev_page_number contains the number of the previous page, or ``0''
if we're already on the first page.
The internal template uses prev_page_number in the context of a <
<TMPL_IF >> to hide the previous page button on the first page.
(N.b.: A JavaScript function, Krang.Pager.goto_page(), is provided for
navigation between pages by pager-internals.tmpl. You are encouraged
to use it.)
The pager is expected to provide a link to the next page if the current
page is not the last page. The <TMPL_VAR> next_page_number
contains the number of the next page, or ``0'' if we're already on the
last page.
The internal template uses next_page_number in the context of a <
<TMPL_IF >> to hide the next page button on the last page.
(N.b.: A JavaScript function, Krang.Pager.goto_page(), is provided for
navigation between pages by pager-internals.tmpl. You are encouraged
to use it.)
The pager is expected to provide a list of page numbers which are
links to allow the user to jump between pages. The <TMPL_LOOP>
>> C<page_numbers> contains this list. Each for in C<page_numbers>
is expected to implement three variables, C<page_number>,
C<page_number_label> and C<is_current_page> which are described below.
Available in the context of the <TMPL_LOOP> page_numbers,
the <TMPL_VAR> page_number contains the number of the page.
This is used by the internal template as a link to jump to a particular
page of output.
(N.b.: A JavaScript function, Krang.Pager.goto_page(), is provided for
navigation between pages by pager-internals.tmpl. You are encouraged
to use it.)
Available in the context of the <TMPL_LOOP> page_numbers,
the <TMPL_VAR> page_number_label contains the label for a page
number. This might be the number itself or it might be the string ``...''.
Available in the context of the <TMPL_LOOP> page_numbers,
the <TMPL_VAR> is_current_page contains ``1'' if the current
page_number is, in fact, the current page being viewed, ``0''
if not. This is used by the internal template in the context of a
<TMPL_IF>/<TMPL_ELSE> to conditionally disable the link to the
current page.
(N.b.: A JavaScript function, Krang.Pager.goto_page(), is provided for
navigation between pages by pager-internals.tmpl. You are encouraged
to use it.)
Krang::HTMLPager allows the user to toggle between two page sizes:
Custom size (set by user preference) and ``Show 100 rows'' (unless Custom
size is 100, then this becomes ``Show 20 rows''). The <TMPL_VAR>
show_big_view is set to ``1'' if the user is in the ``100 rows'' mode,
``0'' otherwise.
(N.b.: A JavaScript function, Krang.Pager.show_big_view(), is provided
for toggling between modes by pager-internals.tmpl. You are encouraged
to use it.)
Krang::HTMLPager allows the user to toggle between two page sizes:
Custom size (set by user preference) and ``Show 100 rows'' (unless Custom
size is 100, then this becomes ``Show 20 rows''). The <TMPL_VAR>
user_page_size is set to the custom page size.
The number of items to show when show_big_view is true. Normally this
is 100, but if the user's preference is for 100, then this becomes 20.