
The Codeigniter framework makes it incredibly simple to paginate query results. With a little bit of jQuery we can easily create a fully dynamic ajax pager with history enabled (the ability to use the back button).
Here is the finished product.
Functionality:
1. The ability to have history stored in a hash so users can use their browser’s back button.
2. Pagination will still function if Javascript is disabled.
3. The javascript needs to be cross browser.
Required:
Codeigniter 1.7.2 (most recent at the time)
jQuery 1.3.2 (most recent at the time)
jquery.history.js which can be found here.
First we need to make sure our framework is configured correctly and connected to our database. Assuming all is well inside the config directory we can begin to setup the model.
<?php
/* -------------------------- */
/* models > pager_model.php */
/* -------------------------- */
class Pager_model extends Model {
function get_posts($limit, $offset)
{
$query = $this->db->get('posts', $limit, $offset);
if ($query->num_rows()>0)//if result
{
return $query;
}
else
{
return false;
}
}
}
The above script simply makes our query to the database returning the results based on the 2 parameters; $limit and $offset. This will allow the controller to specify which rows to return based on the hash or uri segment.
<?php
/* -------------------------- */
/* controllers > pager.php */
/* -------------------------- */
class Pager extends Controller {
/* Constructor */
function __construct()
{
parent::Controller();
$this->load->model('pager_model');
$this->load->library('pagination');
}
function index()
{
/* Get Total Results */
$num_posts = $this->pager_model->get_posts(null,null);
/* Pagination Config */
$config['base_url'] = 'http://petesaia.com/pager';
$config['total_rows'] = $num_posts->num_rows();
$config['per_page'] = '2';
$config['full_tag_open'] = '<ul class="pager">';
$config['full_tag_close'] = '</ul>';
$config['cur_tag_open'] = '<li><strong>';
$config['cur_tag_close'] = '</strong></li>';
$config['num_tag_open'] = '<li>';
$config['num_tag_close'] = '</li>';
$config['next_link'] = 'Next';
$config['prev_link'] = 'Previous';
$config['prev_tag_open'] = '<li>';
$config['prev_tag_close'] = '</li>';
$config['first_tag_open'] = '<li>';
$config['first_tag_close'] = '</li>';
$config['next_tag_open'] = '<li>';
$config['next_tag_close'] = '</li>';
$config['last_tag_open'] = '<li>';
$config['last_tag_close'] = '</li>';
$config['uri_segment'] = '2';
$this->pagination->initialize($config);
/* Make Query */
$data['posts'] = $this->pager_model->get_posts($config['per_page'],$this->uri->segment(2));
$this->load->view('ajaxpager_view', $data);
}
}
Our controller is first going to load the pagination library and our pager_model in the constructor. The pagination library creates our links based on the total number of results and the current page which is specified in the second uri segment of the links it creates. If you haven’t already done so, make sure you specify that you will be using parameters on this class in the routes folder. It will look something like this:
$route['pager/:num'] = "pager";
This says if there is a number after the first slash to reroute to the class pager allowing us to use the number as a parameter.
Finally, we load our view which contains all of our xhtml and Javascript.
<?php
/* -------------------------- */
/* views > ajaxpager_view.php */
/* -------------------------- */
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="/resources/css/style.css" type="text/css" />
<script type="text/javascript" src="/resources/js/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="/resources/js/jquery.history.js"></script>
<title>Codeigniter - AJAX Pagination With History</title>
</head>
<body>
<?php
if ($posts) //if there are in fact posts...
{
echo '
<div id="container">
<div id="results">
';
foreach($posts->result_array() as $row)
{
echo '
<div class="post">
<p>'.$row['text'].'</p>
</div>
';
}
echo '
</div>
</div>
<div id="controls">
'.$this->pagination->create_links().'
</div>
';
}
else //if no results in database
{
echo '<p>There are no records.</p>';
}
?>
<script type="text/javascript">
function pageload(hash)
{
if(hash) {
//give some time for safari to catch up
setTimeout("goto()",200);
}
else
{
//if on page 1
reset();
}
}
function goto()
{
/* Make adapt to if there is a slash at the end of the url or not */
if (location.href.match('/#'))
{
var goto = location.href.replace("#","");
}
else
{
var goto = location.href.replace("#","/");
}
/* Load Pager */
$("#controls").load(goto+' #controls ul',
function()
{
/* Load Content */
$("#container").load(goto+' #results',{},
function(){
$('.loader').remove();
}
);
}
);
}
function reset()
{
$("#results").remove();
$("#container").load(location.href.replace(/[1-9$-]/g, "")+' #results');
$.historyLoad('0');
}
$(document).ready(
function(){
$.historyInit(pageload, "/pager");
$(".pager a").live('click',
function(){
$("#results").remove();
$('#container').append('<img class="loader" src="/resources/images/ajax-loader.gif"/>');
hash = this.href.split('/').pop();
if (hash)
{
$.historyLoad(hash);
}
else
{
reset();
}
return false;
}
);
}
);
</script>
</body>
</html>
First we loop through the results that we passed from the controller in the $data array using the result_array() function.
Then the $this->pagination->create_links() creates our links. At this point our pager should work fine with no javascript at all.
The final element of this project is the jQuery. The idea is to still pass the correct uri segment to our controller as if we wern’t using javascript at all, but to mask it from the browser using a hash (#) to store the page number. So in the browser the url will look like: pager#2, pager#8, ect… But what we are really passing via ajax is still the same pager/2 and pager/8 format as we did without javascript. We are able to do this by filtering the link through a regular expression before loading the content.
I wrote all of the Javascript inline for demonstration purposes only. For larger projects I highly suggest creating a plugin.