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 ¶
- Yii 1.1.8 or above.
- (optional) Bootstrap extension
- (optional) Select2 extension for column visibility and order configuration
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' => $this->createUrl('/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
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.
Property "CButtonColumn.sortable" is not defined.
Thanks for reporting. Should be fixed in the latest update.
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!
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 :-)
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.
Small but important addition to implementation description
That was a typo, fixing it now, thanks.
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.
pageSize and pagination
bilijen could You open a ticket on the projects page? We could discuss details there.
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.
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
@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.
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
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.
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.
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...
ajax url
Hi, great extension.
I'd change ajaxUrl to this:
$widget=$this->createWidget('ext.EDataTables.EDataTables', array( 'id' => 'products', 'dataProvider' => $dataProvider, 'ajaxUrl' => $this->createUrl('/products/index'), 'columns' => $columns, ));
Now UrlManager create the path acording to yours rules.
Thanks
ajax url
Fixed, thanks for pointing this out.
Extension
explanation not enough for a beginner to get things running.
Didn't work for me
demo
I've just created a demo, see the source on github for example how to use it:
demo
followed the demo, mh.. sorry, but still not good for me,
Argument 1 passed to Transactions::search() must be an array, none given, called in /var/www/journals/protected/views/transactions/admin.php on line 126 and defined
117 public function search(array $columns)
118 {
demo
Pass it the same column configuration you pass to the widget. It param is commented in the example model.
TableTools
how to enable print button in TableTools datatable?
refresh and configure buttons work very well, but print button not work!
Almost rolled my own and found this.
Nice work on this. Although it may need improvement, but dataTables is a complex solution, and I think this is well done. I've added it to a current project and would like to offer input at some point.
input
Thanks, I invite you to the github project page. Open up some issues and we'll discuss solutions :-)
Thank you!
I would only say thank you for this extension! GREAT work!
Thanks a lot!!!
We are very happy to thank you for this extension although we have made a lot of changes to your extension. But creating this extension gives us hope nearly 6 months ago.:)
changes
Thanks. Please consider posting feature requests on the Github project page as issues. Maybe some of your changes could be more generic and helpful for others. Let's discuss it on Github.
weird error
Declaration of EDataColumn::getDataCellContent() should be compatible with CDataColumn::getDataCellContent($row)
edatatables object
Hello, how can I get the edatatables object in js?
I want to create custom inputs for filtering the table.
My code...
var tablevpds = jQuery('#vpds').eDataTables({'baseUrl':...});
console.log(tablevpds);
..returns 'undefined'
instance
There is no eDataTables instance. You can read the underlying dataTables at $.fn.eDataTables.tables[id] or eDataTables settings at $.fn.eDataTables.settings[id].
thanks
Thank you nineinchnick!
Can you please tell me, what's wrong now with my code?
var table = $.fn.eDataTables.tables['vpds']; $('#DataTables_Table_0 tfoot td').each( function ( i ) { var select = $('<select><option value=\"\"></option><option value=\"1\">1</option></select>') .appendTo( $(this).empty() ) .on( 'change', function () { table.column( i ) // <-- Uncaught TypeError: undefined is not a function .search( $(this).val() ) .draw(); } ); } );
forum
Please use forum to ask for help. Is your widget id 'vpds'? Do you call that JS AFTER initializing the widget?
ok
I'we wroted down the details in the forum:
http://www.yiiframework.com/forum/index.php/topic/54557-adding-select-dropdown-for-edatatable/page__gopid__254607#entry254607
Is your widget id 'vpds'? - yes
Do you call that JS AFTER initializing the widget? - yes
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.