Friday, July 17, 2009

CodeIgniter: AJAX Pagination

To use CodeIgniter's Pagination class to create pagination in one of our controller functions:


$this->load->library('pagination');

$config['base_url'] = 'http://example.com/index.php/test/page/';
$config['total_rows'] = '200';
$config['per_page'] = '20';

$this->pagination->initialize($config);

echo $this->pagination->create_links();

The $config array contains your configuration variables. It is passed to the $this->pagination->initialize function. At a minimum we need the three configuration variables shown above.
  • base_url: Full URL to the controller class/function containing our pagination.
  • total_rows: Total rows in the result set we are creating pagination for.
  • per_page: Number of rows we intend to show per page
The pagination links provided by create_links function may not be appropriate for all cases. After-all clicking on these links will do page load which may not be desired on many situation. We may be interested to invoke a javascript function while clicking these links for doing AJAX style pagination. Still we want to use all other features provided by CodeIgniter's Pagination class. This can be accomplished simply. Just follow me ;)

1. All you need to do is adding some more functionality to the existing library i.e. CodeIgniter's Pagination class. To extend the native Pagination class you'll create a file named application/libraries/MY_Pagination.php
, and declare your class with:


class MY_Pagination extends CI_Pagination
{
}

Here MY_ is the sub-class prefix, defined in application/config/config.php as follows:


$config['subclass_prefix'] = 'MY_';


If you need to use a constructor in yo ur class make sure you extend the parent constructor:


class MY_Pagination extends CI_Pagination
{
function MY_Pagination()
{
parent::CI_Pagination();
}
}


2. Next add some member variables to this class:


class MY_Pagination extends CI_Pagination
{
var $js_function_name = '';
var $js_function_params = array();

function MY_Pagination()
{
parent::CI_Pagination();
}
}

js_function_name is the name of the javascript function to invoke when pagination links are clicked.

js_function_params is an array of parameters required for that javascript function. Our customized pagination will; add one more parameter for that javascript function; offset. While viewing the first page, offset=0. In the second page, offset = rows_per_page as defined by $config['per_page'] provided to the $this->pagination->initialize function.

js_href is the name of the div used in the pagination links as the value of the href attribute prefixed with #

3. Add a member function
initialize_js_function to the class:


function initialize_js_function($jsFunction = array())
{
if (count($jsFunction) > 0) {
if (isset($jsFunction['name'])) {
$this->js_function_name = $jsFunction['name'];
}
if (isset($jsFunction['params'])) {
for ($i = 0; $i <>js_function_params[$i] = $jsFunction['params'][$i];
}
}
}
}

This function will be called from the controller function where we are using pagination stuffs.

4. Now modify the function create_links and add this modified function to the MY_Pagination class as create_js_links function. The modification basically ensures that each of the anchor tags generated, will have value of onclick attribute equals the javascript function as defined by the member variable js_function_name having the parameters as defined by the member variable js_function_params plus one extra parameter offset. A comma sperated list of the items in js_function_params will be created. Then the offset parameter will be appended in that comma separated list.



function create_js_links()
{
// If our item count or per-page total is zero there is no need to continue.
if ($this->total_rows == 0 OR $this->per_page == 0)
{
return '';
}

// Calculate the total number of pages
$num_pages = ceil($this->total_rows / $this->per_page);

// Is there only one page? Hm... nothing more to do here then.
if ($num_pages == 1)
{
return '';
}

// Determine the current page number.
$CI =& get_instance();

if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE)
{
if ($CI->input->get($this->query_string_segment) != 0)
{
$this->cur_page = $CI->input->get($this->query_string_segment);

// Prep the current page - no funny business!
$this->cur_page = (int) $this->cur_page;
}
}
else
{
if ($CI->uri->segment($this->uri_segment) != 0)
{
$this->cur_page = $CI->uri->segment($this->uri_segment);

// Prep the current page - no funny business!
$this->cur_page = (int) $this->cur_page;
}
}

$this->num_links = (int)$this->num_links;

if ($this->num_links <>cur_page))
{
$this->cur_page = 0;
}

// Is the page number beyond the result range?
// If so we show the last page
if ($this->cur_page > $this->total_rows)
{
$this->cur_page = ($num_pages - 1) * $this->per_page;
}

$uri_page_number = $this->cur_page;
$this->cur_page = floor(($this->cur_page/$this->per_page) + 1);

// Calculate the start and end numbers. These determine
// which number to start and end the digit links with
$start = (($this->cur_page - $this->num_links) > 0) ? $this->cur_page - ($this->num_links - 1) : 1;
$end = (($this->cur_page + $this->num_links) < $num_pages) ? $this->cur_page + $this->num_links : $num_pages;

// Is pagination being used over GET or POST? If get, add a per_page query
// string. If post, add a trailing slash to the base URL if needed
if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE)
{
$this->base_url = rtrim($this->base_url).'&'.$this->query_string_segment.'=';
}
else
{
$this->base_url = rtrim($this->base_url, '/') .'/';
}

//$js_output = $this->js_function_name . '(';
$js_output = '';
for ($paramIndex = 0; $paramIndex <>js_function_params); $paramIndex++) {
$js_output = $js_output . $this->js_function_params[$paramIndex] . ',';
}
//$js_output = rtrim($js_output, ',');
//$js_output .= ')';

// And here we go...
$output = '';

// Render the "First" link
if ($this->cur_page > ($this->num_links + 1))
{
$js_output = rtrim($js_output, ',');
$output .= $this->first_tag_open.'js_function_name.'('.$js_output.'0)'.'">'.$this->first_link.''.$this->first_tag_close;
}

// Render the "previous" link
if ($this->cur_page != 1)
{
$i = $uri_page_number - $this->per_page;
//if ($i == 0) $i = '';
$output .= $this->prev_tag_open.'js_function_name.'('.$js_output.$i.')'.'">'.$this->prev_link.''.$this->prev_tag_close;
}

// Write the digit links
for ($loop = $start -1; $loop <= $end; $loop++) { $i = ($loop * $this->per_page) - $this->per_page;

if ($i >= 0)
{
if ($this->cur_page == $loop)
{
$output .= $this->cur_tag_open.$loop.$this->cur_tag_close; // Current page
}
else
{
$n = ($i == 0) ? '' : $i;
$output .= $this->num_tag_open.'js_function_name.'('.$js_output.$n.')'.'">'.$loop.''.$this->num_tag_close;
}
}
}

// Render the "next" link
if ($this->cur_page < $num_pages) { $output .= $this->next_tag_open.'js_function_name.'('.$js_output.($this->cur_page * $this->per_page).')'.'">'.$this->next_link.''.$this->next_tag_close;
}

// Render the "Last" link
if (($this->cur_page + $this->num_links) < $num_pages) { $i = (($num_pages * $this->per_page) - $this->per_page);
$output .= $this->last_tag_open.'js_function_name.'('.$js_output.$i.')'.'">'.$this->last_link.''.$this->last_tag_close;
}

// Kill double slashes. Note: Sometimes we can end up with a double slash
// in the penultimate link so we'll kill all double slashes.
//$output = preg_replace("#([^:])//+#", "\\1/", $output);

// Add the wrapper HTML if exists
$output = $this->full_tag_open.$output.$this->full_tag_close;

return $output;
}



5. Now in the controller, where you are using pagination stuffs, initialize the pagition in this way:



//In actual scenario, populate it with number rows to paginate
$config['total_rows'] = 100;
$config['per_page'] = 10;
$config['first_link'] = 'First';
$config['last_link'] = 'Last';
//change it as per your controller's function's number of parameters
$config['uri_segment'] = 3;
$this->pagination->initialize($config);

$jsFunction['name'] = 'your_javascript_function_name';
//provide your params for the javascript function if there is any
//In my case, it is empty
$jsFunction['params'] = array();
$this->pagination->initialize_js_function($jsFunction);
//pass/use this $page_link in your view as per your need
$page_link = $this->pagination->create_js_links($pageNo);


This is the basic outline or structure. You have to write down your own javascript function to make ajax call and fill in the missing or leftover details ;)

Now taste the AJAXified pagiantion in action :D We have done a great job already ;)

You can download a complete sample or demo from here: http://www.mediafire.com/file/4ygym0trmjh/ajax_pagination_demo.zip

8 comments:

webanalytics said...

Nice but the download link doesn't work, password protection?

Alain said...

Nice too
But why to put the source code in this site! Impossible to get it

K.M. Fazle Azim Babu said...

Sorry. I forgot to mention the password. Even I have forgotten the password:( I'll try to upload the source as soon as I can manage some free time although I think, the content of the post is enough to write the code:)

Alain said...

Thanks for answering so quickly

I am not an expert, so I need the complete pagination class, please

Besides, u don't use JQuery so it so hard to me to build the code from this post

retaKs said...

Hi. Can you send me the codeigniter class you have? I tried copying this code but it had a lot of parse errors.

Thanks!

K.M. Fazle Azim Babu said...

the post has been updated and a demo's link is given there. There were some errors in my previous version of this post. This has been fixed. Thanks for finding out the problems :)

Dimas said...

Thx for the tips! I tried your complete example and it works well, except the one thing : the onClick link for the first page is always code(), not code(0)

K.M. Fazle Azim Babu said...

Thanks Dimas. I am out of PHP stuffs for several months and notoriously busy with Java EE related projects. If you can manage some time to fix this and share it with us, it will be great :)