Ajax Sorted and Paged tables in Drupal 7

My recent blog post on Creating Paged and Sorted tables in Drupal 7 got a comment today asking if it was possible to implement Paging and Sorting in Drupal tables via Ajax, instead of regular Page refreshes that Drupal does by default.

Why not I thought... Drupal already provides bulk of the server-side framework for the purpose, and Ajax should be a breeze with jQuery. The only thing that would need custom coding would be to hook into the header and paging links and invoke Ajax calls in their click handlers manually, and suppress the page refresh. Then in the server-side code, handle the ajax callback and return the paged and/or sorted table to the browser.

I sat down to write a very simply module demonstrating this entire functionality (which you can find attached). Here I quickly discuss various aspects of the server and client side code for such Ajax paging and sorting.

Let's call our demo module, mymodule and discuss its server-side code first. Here's the bulk of the code from the only server-side file: mymodule.module:

 

{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; }function mymodule_menu(){ $items = array(); //Test items $items['mymodule/test/pager'] = array( 'title' => 'Drupal 7 test pager', 'type' => MENU_CALLBACK, 'page callback' => 'mymodule_test_pager', 'access arguments' => array('access content') ); $items['mymodule/test/pager/callback'] = array( 'title' => 'Test Pager Callback', 'type' => MENU_CALLBACK, 'page callback' => '_mymodule_test_pager_callback', 'access arguments' => array('access content'), ); return $items; } function mymodule_test_pager () { drupal_add_js(drupal_get_path('module', 'mymodule') . '/jquery.url.js'); drupal_add_js(drupal_get_path('module', 'mymodule') . '/mymodule.js'); drupal_add_js('initializeTable();', 'inline'); return ('<div id="table-container"></div>'); } function _mymodule_test_pager_callback () { //This function is invoked on Ajax request and returns the paged and/or sorted table. You can find code for this in the attached zip file below. }{/syntaxhighlighter}

We have used hook_menu here to define 2 menu router entries, the first is a regular Drupal module page which this module uses to demonstrate the Ajaxed table. And the second is for the callback method that receives the ajax callback request and returns the table html to the browser.

Preparing a table and returning the html is all boilerplate code that has been discussed in detail in the other blog post here. Please consult the other blog post if you need to understand the process. The attached code contains the full implementation of the callback method.

The remaining part is client-side where we hook jQuery click handlers to sorting and paging links and perform the same via an ajax call. Here's the javascript code for the same:

 

{syntaxhighlighter brush: jscript;fontsize: 100; first-line: 1; }function refreshTable(page, sort, order) { if(!page) page = 0; if(!sort) sort = ''; if(!order) order = ''; jQuery.ajax({ cache: false, url: Drupal.settings.basePath + '?q=mymodule/test/pager/callback', data: {page: page, sort: sort, order: order}, dataType: 'text', error: function(request, status, error) { alert(status); }, success: function(data, status, request) { var html = data; jQuery('#table-container').html(html); jQuery('#table-container th a'). add('#table-container .pager-item a') .add('#table-container .pager-first a') .add('#table-container .pager-previous a') .add('#table-container .pager-next a') .add('#table-container .pager-last a') .click(function(el, a, b, c) { var url = jQuery.url(el.currentTarget.getAttribute('href')); refreshTable(url.param('page'), url.param('sort'), url.param('order')); return (false); }); } }); } function initializeTable() { jQuery(document).ready(function() { refreshTable(); }); }{/syntaxhighlighter}

Again its pretty standard code that finds desired anchor tags using suitable CSS selectors and then binds a click listener to those anchors to invoke a Ajax call from the click handler.

And voila, you have a completely Ajaxed Paging and Sorting Experience in Drupal 7. To try it, please download the attached zip file, and extract the contents to the "modules" folder of your Drupal installation. Then enable the module from Administration section and test the functionality by visiting http://example.com/mymodule/test/pager in your Drupal installation (replace example.com with the path to your Drupal 7 installation).

It's pretty similar to achieve such Ajaxed paging and sorting with Drupal 6 also. You can easily combine the code from this blog post and my earlier one regarding Creating Paged and Sorted tables in Drupal 6 to have Ajaxed Paging and Sorting for D6. In case you face an issue doing so with D6, please let me know via comments below and I will try to cough up D6 code also for the same.

 

PHP: 
AttachmentSize
Package icon mymodule.zip4.09 KB

Comments

Hello!

In the beginning - great JOB!
I'm trying to implement this in D6 but I have a little problem... you're calling the theme('table'.....) like this:

    $html = theme('table',
                array(
                    'header' => $header,
                    'rows'=>$rows,
                    'caption' => 'Creating Drupal 7 Ajaxed tables',    //Optional Caption for the table
                    'sticky' => TRUE,                        //Optional to indicate whether the table headers should be sticky
                    'empty' => 'No nodes created...',        //Optional empty text for the table if resultset is empty
                )
            );

in D6 the correct syntax is: theme ('table', $headers, $rows) not theme ('table', array()). Could you possibly tell me how to use that in D6 ?

Kind regards,
Jarek

rahul's picture

Hi Jarek,

Did you look at my earlier blog in the same context but with Drupal 6:
http://www.rahulsingla.com/blog/2010/05/creating-drupal-style-tables

It does same thing, but without Ajax. You can use the javascript code from this blog post for the Ajax feature as that should remain the same.

Jarec, it is exactly what I've been searching for. Thank you so much for sharing this javascript code.

Just want to say thanks for this great post :-)

It really helps me for a little module that I need this morning!

regards,
MartinO

hi!, Thanks for the Info, Its very clear and step forward,

I have tested the attached module and have found that in this version (ajax-ed) the sticky header is not working.

I have added a form with a collapsible fieldset, wich also have stoped working.

both funcionalities works fine in the non-ajax version.

Could oyu test this issue? I think that there is some incompatibilities with js manually included, but can't find what is happening, also have noted that in this version drupal does not auto-insert the some table related js (like tableheader.js)

rahul's picture

Hi wences, time limitations might not allow me to test this immediately, but I can suggest some work-arounds. Sticky header should not be working because of the absence of js file on the page, add it manually: drupal_add_js('misc/tableheader.js');

Not sure about fieldsets, would run tests when I get time.

Great job, works like a charm!

it would be super nice if you can edit inline/in place the data of the table...

cheers, maxx

rahul's picture

Hi Max, the fact is we are in the same boat. The first time I developed this code for D7 was for a site which also required inline editing for selected columns of the table. I ended up using Mika Tuupola's jeditable plugin for jquery, it worked great. Just gove your "editable" table cells a class or id and then use the above plugin to make the content editable.

Hehe, in the meantime i did exactly the same :)

Thanks Buddy.

damn, i tryed to implement it on a D7.8 installation and the jeditable conflicts now with drupal.js

did you got it working on 7.8?

rahul's picture

Nopes, I did it with an earlier D7 version. I can't imagine why they should conflict though (you can try changing drupal.js to avoid the conflict).

i liked your solution. i would like to reuse it and extend. The functionality i miss - is filtering.

Do you plan to add it? Or can you share some ideas of how would you do it? any help appreciated.

p.s. i was trying to do it by myself by adding simple html form with filter-fields. The problem i could not solve is that i cannot see any $_POST or $_GET attributes in the  _mymodule_test_pager_callback method. Still haven't discovered why :(

rahul's picture

Hi turtletrail, you need to understand that Ajax operations are not regular web-form submissions and hence data entered in a web-form is not sent back to server automatically. You need to manually append all fields as parameters in the Ajax call. When using jQuery, you would add it to the "data" option of the Ajax call, e.g.

{syntaxhighlighter brush: jscript;fontsize: 100; first-line: 1; }jQuery.ajax({ cache: false, url: Drupal.settings.basePath + '?q=mymodule/test/pager/callback', data: {page: page, sort: sort, order: order, name: jQuery('#name_field').val() //...More ajax call options }, //...Rest of Ajax call parameters{/syntaxhighlighter}

thanks for comment,

really helped :)

I really appretiate your effort. You did a great favour.

really really helpful - ur a great man! THANKS!!!

ps if I ever get my business super successful i'm hiring you!!!

Thanks for sharing, really added that extra sparkle to my data table. One thing though, in my data table I had some links that opened a dialog modal, so I needed to re-attach the behaviours to those links after the table was added in your jquery.

before:

jQuery('#table-container').html(html);

after:

jQuery('#table-container').html(html);
Drupal.attachBehaviors('#table-container');

Also, did you have any thoughts on adding an ajax loader when the pagers are clicked. I'll probably try and figure it out, if I do I'll post the solution here.

Thanks again for sharing.

rahul's picture

Hi Nikos, thanks for sharing the attachBehaviors part.

For the ajax loader, I did that part for one client if I recall correctly, but I am running very short on time, so won't be able to search through the codebase for it in the short term. It was easy I think, some CSS and attaching to jQuery's before and after ajax event listeners to show/hide the loader element respectively was all what is needed. If you have issues figuring it out, please let me know and I will try to search the codebase asap.

Yeah I thought it might just be a case of using ajaxStart/ajaxStop to show/hide a loader.

On another note, I think I've come across your blog before when I've been searching for something to do with drupal, keep up the good work. 

rahul's picture

Thanks Nikos for the kind words.

To add a throbber this seems to do the trick for me, hope it helps others (tested on mac osx lion firefox/chrome);

before this;

refreshTable(url.param('page'), url.param('sort'), url.param('order'));

add this;

jQuery('<span id="loader"></span>').insertAfter(this);
refreshTable(url.param('page'), url.param('sort'), url.param('order'));

then you need to add some css, so add this somewhere (it uses the drupal throbber);

#loader {
  background: url(/misc/throbber.gif) no-repeat 0 -20px scroll;
  padding: 0 15px 0 0;
}

rahul's picture

Thanks for sharing code. Just a minor issue I see there. I guess multiple throbbers would appear on the page as the table is refreshed multiple times by clicking paging/sorting links (because each time you insert a loader tag).

I think a better approach might be to have the throbber tag already present on the page on initial page load but with "display: hidden" css attribute applied via a class. Then that class should be removed from the "loader" tag before table refresh and added back to it after table refresh.

Thanks, thetoast.  This saved me a ton of time:

Drupal.attachBehaviors('#table-container');

Thanks for the tutorial.

But how can I add 2 different ajax paged tables on 1 page?

rahul's picture

Exactly like you added one table. Add another table and have its elements hooked via jquery to perform Ajaxed operations. I haven't tried it, maybe some additional parameters like element_id etc. would need to be passed in Ajax call as there would be multiple pagers on the page.

In my site i have to try to implete your module its works great, by my accordion is not working, tables are listing under accordian. there are three table in one page and i need accordion and ajax pager both together.

rahul's picture

Hi richa, it should not be too difficult to achieve this, and I do not think accordions should interfere with table paging/sorting. If you would NOT be doing this through ajax (but standard page load), you might need to manipulate 'element' manually to differentiate between different tables on the same page.

However when doing through Ajax, just have 3 different callbacks, one for each table and you should be good.

Hi rahul, thanks for reply. i am using jquery accordian default behaviour of drupal 7. please check my code below. its not working with jquery accordions.

rahul's picture

Hi Richa, I am running extremely short on time and can take very long to check the code, I would advise you try to debug yourself.

Hi

Need a help for Multiple pager (unique) with Table header sorting (unique) in a single page

Table 1: published nodes with table header "nid", "Title" - limit is 5, Total 20 nodes

Table 2: unpublished nodes with table header "nid", "Title" - Limit is 10, Total 50 nodes

If i do sorting in table 2 "nid" column then only table 2 is needs to sort. Table 1 should not sorts.

Thanks in advance