jason
jason

Reputation: 3615

CakePHP 3 create Ajax Paginination/Sorting

I've been looking around for this, but have been having a hard time finding any examples. I have pagination added to my app but I would like to make this an Ajax/Async feature, so the full page doesn't reload. Does anyone have any examples/tutorials on best practices for this? Here is the code I already have:

Controller:

 public $acronym_paginate = [
        'limit' => 25,
        'fields' => ['id','abbreviation', 'full_text'],
        'order' => [
            'Acronyms.abbreviation' => 'asc'
        ]
    ];
    public function initialize() {
        parent::initialize();        
        $this->loadComponent('RequestHandler');
        $this->loadComponent("Flash");
    }
    public function index() {
        $this->loadModel('Acronyms');       
        $acronyms = $this->Acronyms->find('All')->where();      
        $this->set([
            'acronyms' => $this->paginate($acronyms),
            '_serialize' => ['acronyms']
        ]);       
    }

}

View:

<table class = "acronymTable" id = "acronymTable" name = "acronymTable" >
    <thead>        
        <tr>
            <td style="display:none;">
                ID
            </td>
            <td width = "10%" style="border:1px solid black; font-weight: bold; padding:10px;">
                Acronym
            </td>
            <td width = "60%" style = "border:1px solid black;  font-weight: bold; padding:10px;">
                Definition
            </td>                
        </tr>
    </thead>
    <tbody>
        <?php foreach ($acronyms as $acronym): ?>
            <tr id = "<?= $acronym->id ?>" name = "<?= $acronym->id ?>">
                <td style="display:none;"><?= $acronym->id ?></td>
                <td style="padding-left:10px;" id="acronymName-<?= $acronym->id ?>"  name="acronymName-<?= $acronym->id ?>" ><?= $acronym->abbreviation ?></td>
                <td style="padding-left:10px;" id="acronymDef-<?= $acronym->id ?>"  name="acronymDef-<?= $acronym->id ?>"><?= $acronym->full_text ?></td>                                      

            </tr>
        <?php endforeach; ?>              

    </tbody>
    <tfoot>
        <tr>
            <td colspan="5" style="text-align: center;">
                 <div class="pagination pagination-large">
                    <ul class="pagination">
                        <?php
                            echo $this->Paginator->prev(__('prev'), array('tag' => 'li'), null, array('tag' => 'li','class' => 'disabled','disabledTag' => 'a'));
                            echo $this->Paginator->numbers(array('separator' => '','currentTag' => 'a', 'currentClass' => 'active','tag' => 'li','first' => 1));
                            echo $this->Paginator->next(__('next'), array('tag' => 'li','currentClass' => 'disabled'), null, array('tag' => 'li','class' => 'disabled','disabledTag' => 'a'));
                        ?>
                    </ul>
                </div>               

            </td>
        </tr>
    </tfoot>
</table>

If there are no "best practices" for this, I guess my biggest question would be how to keep the pagination buttons, but have them submit requests via jQuery Ajax call.

thanks for your help

Upvotes: 0

Views: 575

Answers (1)

Marijan
Marijan

Reputation: 1855

Since the state of the Pagination changes with every request – previous/next link may become inactive/active and numbers may be truncated/released – I’d suggest to also render the pagination in your Ajax request.

  1. Move your replacable output to Elements to stay DRY.

/src/Template/Element/Acronyms/list.ctp

<?php foreach ($acronyms as $acronym): ?>
    <tr id = "<?= $acronym->id ?>" name = "<?= $acronym->id ?>">
        <td style="display:none;"><?= $acronym->id ?></td>
        <td style="padding-left:10px;" id="acronymName-<?= $acronym->id ?>"  name="acronymName-<?= $acronym->id ?>" ><?= $acronym->abbreviation ?></td>
        <td style="padding-left:10px;" id="acronymDef-<?= $acronym->id ?>"  name="acronymDef-<?= $acronym->id ?>"><?= $acronym->full_text ?></td>                                      

    </tr>
<?php endforeach; ?> 

/src/Template/Element/Acronyms/pagination.ctp

<div class="pagination pagination-large">
    <ul class="pagination">
        <?php
            echo $this->Paginator->prev(__('prev'), array('tag' => 'li'), null, array('tag' => 'li','class' => 'disabled','disabledTag' => 'a'));
            echo $this->Paginator->numbers(array('separator' => '','currentTag' => 'a', 'currentClass' => 'active','tag' => 'li','first' => 1));
            echo $this->Paginator->next(__('next'), array('tag' => 'li','currentClass' => 'disabled'), null, array('tag' => 'li','class' => 'disabled','disabledTag' => 'a'));
        ?>
    </ul>
</div>

Then in your view replace the appropriate code with the Elements:

<?= $this->element('Acronyms/list', ['acronyms' => $acronyms]) ?>
<?= $this->element('Acronyms/pagination') ?>
  1. Create a View for Ajax requests on your index method.

/src/Template/Acronyms/ajax/index.ctp

<table id="list">
    <?= $this->element('Acronyms/list', ['acronyms' => $acronyms]) ?>
</table>
<div id="pagination">
    <?= $this->element('Acronyms/pagination') ?>
</div>
  1. Check for Ajax requests and render the View in your Controller.

/src/Controller/AcronymsController.php

public function index() {
    $this->loadModel('Acronyms');       
    $acronyms = $this->Acronyms->find('All')->where();      
    $this->set([
    'acronyms' => $this->paginate($acronyms),
    '_serialize' => ['acronyms']
    ]);

    // render our View for Ajax requests
    if ($this->request->is('ajax')) {
        $this->render('ajax/index');
    }
}
  1. Finally listen for click events on pagination links and replace the contents.

jQuery

$(document).on('click', '.pagination a', function(e) {
    e.preventDefault();

    var $link = $(this);

    if (!$('body').hasClass('loading')) {
        $('body').addClass('loading');

        $.ajax({           
            url: $link.attr('href')        
        }).done(function(html) {

            var $html;

            // grab Ajax response into jQuery object
            $html = $(html);

            // replace table body content with new <tr> elements
            $('#acronymTable tbody').html($html.find('#list').html());

            // replace pagination
            $('#acronymTable .pagination').replaceWith($html.find('#pagination').children());

            $('body').removeClass('loading');

        }).fail(function() {

            // if Ajax request fails fall back to just loading the target page
            window.location.href = $link.attr('href');

        });
    }

});

Upvotes: 2

Related Questions