edatatables

DataTables grid widget with CGridView API.
34 followers

Goal of this widget/wrapper is to provide a drop in replacement for base CGridView widget from the Yii framework, using DataTables plugin.

It's usable, but feedback is needed. Please post issues on project's page.

Features

  • Redrawing of table contents (after paging/sorting/searching) using AJAX calls.
  • Using CGridView columns definition format, supports all basic special columns like Buttons, Checkbox, etc.
  • Custom buttons in table header.
  • Smoothness theme from JUI by default
  • Twitter Bootstrap support through the bootstrap extension
  • Partial editable cells support.

Requirements

Changes

0.9.2

  • re-enabled beforeAjaxUpdate option
  • disabled default configure button, as this requires the select2 extension

0.9.1

  • removed fixed width on first column if selectableRows != 0
  • allow passing null to buttons option to disable all default buttons
  • fix default JS action on delete button in EButtonColumn
  • register select2 JS plugin from ESelect2 extension if configure button is enabled
  • removed all the JS code for drawing active forms in modal dialogs

0.9.0

  • updated DataTables to 1.9.4
  • refactored editable columns and changed the way rows are selected, read more on the wiki
  • refactored and moved all ajax related code from the class file to the js plugin file
  • added refresh and search JS methods to trigger reloading of grid's contents
  • added column names to the dataTables definition, send their order and visibility to the server with every ajax request
  • added a static restoreStateSession method to save and restore pagination, sorting and columns order/visibility in/from session, see the wiki
  • fixed retrieving key values from dataProvider
  • add an implementation of parse_str borrowed from CakePHP framework to restore contents of editable columns
  • changed every occurence of $_REQUEST to $_GET
  • fixed EDTPagination not storing itemCount properly
  • fixed delete button JavaScript callback that should refresh the grid's contents
  • added a definition for a "history" button, that's supposed to open a record modifications log

Usage

It's not 100% compatible with CGridView. I've decided not to alter the GET parameter names used by DataTables, so you have to use the provided EDTSort and EDTPagination classes as well as alter filter processing. See below.

Installation

Extract into extensions dir.

Import in config/main.php

'import' => array(
  ...
  'ext.EDataTables.*',
  ...

Using

Use similar to CGridView. If displayed in a normal call just run the widget. To fetch AJAX response send json encoded result of $widget->getFormattedData().

The action in a controller:

$widget=$this->createWidget('ext.EDataTables.EDataTables', array(
 'id'            => 'products',
 'dataProvider'  => $dataProvider,
 'ajaxUrl'       => Yii::app()->getBaseUrl().'/products/index',
 'columns'       => $columns,
));
if (!Yii::app()->getRequest()->getIsAjaxRequest()) {
  $this->render('index', array('widget' => $widget,));
  return;
} else {
  echo json_encode($widget->getFormattedData(intval($_REQUEST['sEcho'])));
  Yii::app()->end();
}

The index view (for non-ajax requests):

<?php $widget->run(); ?>

Preparing the dataprovider

To use features like sorting, pagination and filtering (by quick search field in the toolbar or a custom advanced search filter form) the dataprovider object passed to the widget must be prepared using provided EDTSort and EDTPagination class and CDbCriteria filled after parsing sent forms.

The simplest example:

$criteria = new CDbCriteria;
// bro-tip: $_REQUEST is like $_GET and $_POST combined
if (isset($_REQUEST['sSearch']) && isset($_REQUEST['sSearch']{0})) {
    // use operator ILIKE if using PostgreSQL to get case insensitive search
    $criteria->addSearchCondition('textColumn', $_REQUEST['sSearch'], true, 'AND', 'ILIKE');
}
 
$sort = new EDTSort('ModelClass', $sortableColumnNamesArray);
$sort->defaultOrder = 'id';
$pagination = new EDTPagination();
 
$dataProvider = new CActiveDataProvider('ModelClass', array(
    'criteria'      => $criteria,
    'pagination'    => $pagination,
    'sort'          => $sort,
))

An advanced example would be based on a search form defined with a model and a view. Its attributes would be then put into a critieria and passed to a dataProvider.

Other options

Check out the DataTables web page for docs regarding:

  • Table layout
  • Styling
  • Multi-column sorting etc.
  • Tons of examples and funky plugins

Resources

Total 15 comments

#13008 report it
le_top at 2013/04/27 02:25pm
Drop in replacement expensive

Hi I agree that this is expensive, which is why I have a MyCGridView wrapper that avoids rendering when not needed (when configured to do so).

The extension should allow both the 'easy' way and the 'optimized way'. The 'easy way' is not that expensive in most cases, and speed of development is preferred. In some cases, optimization is needed. I have such a case and my views/widgets have several checks if rendering of the specific part is needed or not.

Anyway: I know how it is; one makes the extension for his own needs and shares with the community allowing the community to update, while in practice the community still expects the author to update...

#13005 report it
nineinchnick at 2013/04/27 01:56pm
Feedback

Thanks for the feedback. It's always needed. Unfortunately, I don't want to do it the way you describe. EExcelView runs the whole action, then discards its output and generate its own. CGridView, when reloading data through AJAX, renders whole page and then extracts only the table with its contents. It's noted somewhere that this is not a preferred solution but it's there for simplicity. In my projects, rendering whole layout with menus etc. could be quite expensive because of auth checks and such. So there really is a need for a separate action to be called as a data source.

Another thing is performing a database search. There aren't any ready solutions in Yii, you have to create a criteria building mechanism yourself and pass it to dataprovider. I've written my own search() method in ActiveRecord that uses value from sEcho, validates it for every column and then puts it in a criteria object. Its quite complex, including checking auth items for relations etc.

There are lots of things to consider when building an index action and a simple widget doesn't have to cover them all.

#13002 report it
le_top at 2013/04/27 12:19pm
Feedback

Hi I tried this extension in order to port a CodeIgnite project to Yii and keep the same look and function for the moment. There is quite some work that has been done, but it is not a drop-in replacement for CGridView yet. I think that it should be possible to simply replace '...CGridView' with 'ext.EDataTables.EDataTables', but currently there is a need to have a different controller and add extra data. Look into EExcelView to look for some ideas. I think the controller code is currently needed because there is the need to reply with JSON. In EExcelView, there is a PDF or other reply. The applied solution is to clean the output buffers (ob_clean, hierarchically), and the output the JSON or whatever. The widget should check for sEcho, etc., and add the ajax code itself.

#12800 report it
Hans Bonini at 2013/04/12 12:03pm
Error on Jquery

I had this error on Jquery using Yii 1.1.8: Uncaught TypeError: Object [object Object] has no method 'off' jdatatable.js:145

#11329 report it
nineinchnick at 2013/01/06 12:23pm
@dannythebestguy

If I would want to use an array, I would put it into an CArrayDataProvider without any pagination but try to enable pagination by passing dataTables options in JavaScript. Full table would be available on page load and then dataTables would take over with pagination and sorting. There's no point in holding really large arrays in memory, so Ajax updates are not needed.

Another scenario is when you do read data through an Active or Sql DataProvider but have to postprocess it. Then you could just put it into an CArrayDataProvider.

Always remember to define the keyField.

#10865 report it
dannythebestguy at 2012/11/28 04:01pm
Can it be used for Arrays

Thnx for the Extension. Wanted to know if it is possible to just use it for Arrays instead of CDataProviders.

Thnx

#9790 report it
DDI at 2012/09/10 12:38pm
Seems great, but having issues getting it to work

I can load up the table fine from my dataProvider, and am using EDTSort and EDTPagination, but when I try to sort or view another page, all I get is an alert window showing an error and some data that starts like this:

Error 500: {"sEcho":2,"iTotalRecords":"17","iTotalDisplayRecords":"17","aaData":[["my data starts here...

Any ideas? Will there be an update or some new documentation soon? This extension has lots of potential but is very confusing to implement for a newbie like me.

#6902 report it
nineinchnick at 2012/02/12 06:10am
pageSize and pagination

bilijen could You open a ticket on the projects page? We could discuss details there.

#6864 report it
bilijen at 2012/02/09 02:03pm
Same typo in Project wiki / pageSize and pagination

Please have a look at your Project wiki, too. Same typo there.

I've changed method getCurrentPage($recalculate=true) in EDTPagination.php like this:

public function getCurrentPage($recalculate=true)
    {
        if($this->_currentPage===null || $recalculate)
        {
            if(isset($_REQUEST[$this->startVar]) && isset($_REQUEST[$this->lengthVar]))
            {
                $this->_currentPage=floor(intval($_REQUEST[$this->startVar]) / $this->getPageSize());
                if($this->validateCurrentPage)
                {
                    $pageCount=ceil($this->getItemCount()/intval($_REQUEST[$this->lengthVar]));
                    if($this->_currentPage>=$pageCount)
                        $this->_currentPage=$pageCount-1;
                }
                if($this->_currentPage<0)
                    $this->_currentPage=0;
            }
            else
                $this->_currentPage=0;
        }
        return $this->_currentPage;
    }

in order to reflect a pageSize change.
$pageCount will now be computed correctly and pagination will work okay. (I've also exchanged pageVar for startVar in the entire class. Seems more logical to me).

Now the grid works pretty well. I can change pageSize, do paging, searching and sorting. The extension is a great piece of work. Thank you!

Regards.

#6861 report it
nineinchnick at 2012/02/09 12:21pm
Small but important addition to implementation description

That was a typo, fixing it now, thanks.

#6859 report it
bilijen at 2012/02/09 11:22am
Small but important addition to implementation description


In the above example of 'action in a controller' code I've changed the lines
$data = $widget->getFormattedData(intval($_REQUEST['sEcho']));
json_encode($data)
to
echo CJSON::encode($widget->getFormattedData(intval($_REQUEST['sEcho'])));
This is actually not my own idea but someone's suggestion on project's page.
You may as well use
$data = $widget->getFormattedData(intval($_REQUEST['sEcho']));
echo json_encode($data)

The 'echo' is important, otherwise the table will not redraw.

#6653 report it
nineinchnick at 2012/01/25 03:15pm
views

The example with createWidget is the action, I've added index and ajax views.

The idea is that you need the whole widget to extract table contents ready to be encoded into JSON and sent over in an AJAX response. That's why I create the widget in the action and pass it to the views. In a normal non-ajax view I just run the widget. The AJAX one just gets the data and encodes it.

Resetting all client scripts is kinda ugly, but it is needed as creating the widget registers some scripts.

Also, I've noticed some more leftover code in the getFormattedData method. If you get exceptions about non-existent properties try to remove those calls or wait for an update. I'm going to try to upload a new version ASAP.

BTW The DataTables plugin is awesome, this extension is merely a wrapper. I would advise buying the authors a beer through donations :-)

#6652 report it
rums at 2012/01/25 02:36pm
Not quite clear?

Hi,

Looks like a great extension. It works fine if I just use it as a drop-in for an existing CGridView -- changing $this->widget('zii.widgets.grid.CGridView'... to $this->widget('ext.EDataTables.EDataTables'... -- but I can't get the Ajax side of it working.

Can you clarify what piece and where you expect each chunk of code in the example to go? If this all goes in the controller (actionAdmin for example):

$widget=$this->createWidget('ext.EDataTables.EDataTables', array(
 'id'            => 'products',
 'dataProvider'  => $dataProvider,
 'ajaxUrl'       => Yii::app()->getBaseUrl().'/products/index',
 'columns'       => $columns,
));
if (!Yii::app()->getRequest()->getIsAjaxRequest()) {
  $this->render('index', array('widget' => $widget,));
  return;
}
$data = $widget->getFormattedData(intval($_REQUEST['sEcho']));
Yii::app()->getClientScript()->reset();
$this->renderPartial('ajax', array('dtData' => $data), false, true);

what goes in the view? I've tried a bunch of different ways of doing this and can't get it working. If you can post a the controller action and view code it would be greatly helpful. Thanks!

#6636 report it
nineinchnick at 2012/01/23 04:38pm
Property "CButtonColumn.sortable" is not defined.

Thanks for reporting. Should be fixed in the latest update.

#6630 report it
jmariani at 2012/01/23 11:16am
Property "CButtonColumn.sortable" is not defined.

Hi!

I got this error while trying your extension, on line

/Applications/MAMP/public_html/yiitest/protected/extensions/EDataTables/EDataTables.php(186)

Regards.

Leave a comment

Please to leave your comment.

Create extension