tigrang / cakephp-datatable

JQuery DataTable plugin server-side processing component for CakePHP
47 stars 29 forks source link

Trouble to get it working #15

Closed cbueche closed 11 years ago

cbueche commented 11 years ago

Hi,

I'm trying to use the plugin on top of a cakePHP 2.2.2 application. I have followed the README, but I have a problem with the Ajax calls, they are not going to the View/stuff/datatable/action.ctp. Instead, they are directed to View/stuff/action.ctp, which is my regular cake view file.

Then, datatable in the browser complains that the returning data is not JSON (of course it's not). In Firebug, I see an Ajax query going to stuff/action?long-params...

Maybe a reason, but not sure about its real impact : I upgraded the app from cakePHP 2.0, and I'm using JSON views elsewhere.

Help appreciated, TIA, Charles

tigrang commented 11 years ago

By default, datatable processing is triggered if the request is an ajax one and the current action is index. If this isn't the case, you can set the triggerAction setting to the action you need as a string, or an array of multiple actions for that controller that should be triggered if the request is an ajax one. You can also set trigger setting to a callback in your controller to do custom detection of when to process a request as a datatable request (it should return true/false).

cbueche commented 11 years ago

Thanks for your willingness to help !

I see your point. I'm now using static code for the datatable part in my view to avoid side-effects, the code looks like this:

<script type="text/javascript">
$(document).ready(function() {
    $('#dataTable').dataTable( {
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": '<?php echo $this->Html->url(array("action" => "idt")); ?>'
    } );
} );
</script>

Using FirebugLite, I then see a query going to the web server, the query looks like this:

GET /prolix/ck/devices/idt?sEcho=1&iColumns=13&sColumns=&iDisplayStart=0 .......

It has Content-Type "application/json", Accept "application/json, text/javascript, /; q=0.01", X-Requested-With "XMLHttpRequest" so I guess this a valid Ajax query trying to get action "idt" from controller "Device". So far so good.

The server answer is :

{
"code":"500",
"url":"\/prolix\/ck\/devices\/idt?sEcho=1&amp;amp;iColumns=13&amp;amp;sColumns=&amp;amp;iDisplayStart=0 ...... ",
"name":"View file &quot;\/Users\/cbueche\/Data\/Tools\/cakePHP\/prolix\/ck\/app\/View\/Devices\/json\/idt.ctp&quot; is missing."
}

So it seems the handling of the request in Cake is wrong : it is trying to get a JSON view from View/Device/json/idt.ctp instead of View/Device/datatable/idt.ctp

Any idea why ?

tigrang commented 11 years ago

Post your controller code. Include your action and how you configured the plugin (components array, etc). Btw you'll need a model get param like so:

echo $this->Html->url(array("action" => "idt", "?" => array("model" => "TheModelYouArePaginating"))
cbueche commented 11 years ago

Here are all the parts you asked for :

DevicesController.php

<?php
App::uses('AppController', 'Controller');
/**
 * Devices Controller
 *
 * @property Device $Device
 */
class DevicesController extends AppController {

    # for JSON views and datatable
    var $components = array(
        'DataTable.DataTable' => array(
            'columns' => array(
                'id' => false,                      // bSearchable and bSortable will be false
                'name' => 'Name',                   // bSearchable and bSortable will be true, with a custom label `Name`
                                                    // by default, the label with be a Inflector::humanize() version of the key
                'mgmt_ip_4' => 'IP',
                'Actions' => null,                  // tells DataTable that this column is not tied to a field
            ),
            //'triggerAction' => array('idt'),
        ),
        'RequestHandler',
    );

    public $helpers = array(
        'Time',
        'DataTable.DataTable' => array(
           'scriptBlock' => false,
        )
        );

    public $paginate = array(
        'limit' => 2000,
        'maxLimit' => 2000,
        'order' => array(
        'Customer.name' => 'asc',
        'Device.name' => 'asc'
        )
    );

/**
 * idt method
 *
 * @return void
 */
    public function idt() {
        $this->DataTable->paginate = array('Device');
        $this->log("in idt", LOG_DEBUG);
    }

... more methods ...

The action (currently not linked, calling it from the browser)

I'm not sure to understand where I will need the "model get param" you mention above. Is it simply a menu entry somewhere to call the below URL ?

http://127.0.0.1/prolix/ck/devices/idt

Model/Device.php

<?php
App::uses('AppModel', 'Model');
/**
 * Device Model
 */
class Device extends AppModel {

    public $belongsTo = array(
...
    )

    public $validate = array(
...
    );

    public function customSearch($field, $searchTerm, $columnSearchTerm, $conditions) {
        if ($searchTerm) {
            $conditions[] = array("$field LIKE" => '%' . $searchTerm);  // only do left search
        }
        if ($columnSearchTerm) {
            $conditions[] = array($field => $columnSearchTerm);         // only do exact match
        }
    }

View/Devices/idt.ctp

<?php
$this->Html->script('DataTables/media/js/jquery.js', array('inline' => false));
$this->Html->script('DataTables/media/js/jquery.dataTables.js', array('inline' => false));
?>
<script type="text/javascript">
$(document).ready(function() {
    $('#dataTable').dataTable( {
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": '<?php echo $this->Html->url(array("action" => "idt")); ?>'
    } );
} );
</script>
<div class="devices index">
        <h2><?php echo __('Devices');?></h2>
        <table id="dataTable" data-model="Device">
        <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>IP</th>
            <th>Actions</th>
        </tr>
        </thead>
        <tbody>
            <tr>
                <td colspan="3">Loading data from server</td> 
            </tr>
        </tbody>
        </table>
</div>
...

View/Devices/datatable/idt.ctp

<?php
//debug($dtResults);
foreach($dtResults as $result) {
    $this->dtResponse['aaData'][] = array(
        $result['Device']['id'],
        $result['Device']['name'],
        $result['Device']['mgmt_ip_4'],
        'actions',
    );
}

route.php

...
    Router::parseExtensions('json');

bootstrap.php

...
CakePlugin::load('DataTable');
tigrang commented 11 years ago

Add:

var $components = array(
    'DataTable.DataTable' => array(
        'triggerAction' => array('idt')

Your sAjaxSource should be http://127.0.0.1/prolix/ck/devices/idt?model=Device

cbueche commented 11 years ago

Bingo! It was lacking two things :

The desired functionality is present. However, I have one more problem to solve: the helper not rendering the javascript part (it looks like it does nothing). It works because I statically create the javascript code in the view :

<script type="text/javascript">
$(document).ready(function() {
    $('#dataTable').dataTable( {
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": '<?php echo $this->Html->url(array("action" => "idt", "?" => array("model" => "Device"))); ?>'
    } );
} );
</script>

If I understand the doc correctly, instead of creating the javascript code above, I should be able to use this

$this->DataTable->render('Device');

in my View/Devices/idt.ctp, but it produces nothing. Any idea why ?

tigrang commented 11 years ago

In the controller helpers var remove 'scriptBlock' => false if you want to use the default init script. Also, add <?php echo $this->fetch('dataTableSettings'); ?> before <?php echo $this->fetch('script'); ?>

$this->fetch('dataTableSettings'); outputs a js var with all the settings the jquery plugin needs - so if you don't want to use the default init script, you would use that js var for the settings (its needed for the default init script, also). Look at the page source to see its values and get an idea of what's going on behind the scenes.

Sorry for the lacking documentation.

cbueche commented 11 years ago

Thanks Tigrang for all the help. The helper works and generate the javascript piece, but I then can't write my code so it works with datatables. Problem is mine, not yours, I'm stuck with lacking javascript know-how, and will need help from a local colleague, might take a few weeks to see him again.

meanwhile, I have reverted to coding the javascript entry in my view manually, it works this way, and the back-end works well. Thanks for your code, and let me know if you have a Paypal account so I can reward you for your work and code.

Regs, Charles

tigrang commented 11 years ago

No problem. I hope you get it working soon. Works open-source and free, but if you feel like you want to donate anyways, it's tigrangab at gmail dot com