Archive for July, 2011

Manipulating the response from Drupal Views’ AJAX mechanism

When there is a requirement to plug any sort of AJAX-y mechanism into your Views, I always find it’s advantageous to create the functionality from scratch, utilising hook_menu (including 'type' => MENU_CALLBACK), or using the Services module, to estabish the RESTful service(s). However, there are occasions when using Views’ built-in AJAX mechanism (‘Use AJAX’) is a prequisite for what you’re trying to achieve. The immediate assumption is that this technique wouldn’t give you enough granularity and control over response output. This is in fact incorrect, as the response can be interecepted relatively easily.

Case study

I recently needed to create a block-display View that incorporates an exposed filter (filtering on taxonomy term). As merlinofchaos points out, exposed filters make GET requests in order to return responses, and they can’t do so without the AJAX option turned on. Turning on the AJAX option makes the filters appear – and better still, makes them work – but out of the box, a subsequent data response will simply replace the current data response that’s already in the DOM (using JQuery’s replaceWith). I’d like to have full control of the response, like I would do if I was to construct the AJAX functionality from scratch.

A how-to guide

Subsequent to my own findings, I that details the same method pretty well, although I feel it does miss out a few key points.

Views allows us to intercept the response using hook_ajax_data_alter (implemented on line 79 of views/includes/ajax.inc), which is a hook originating from drupal_alter.  If you check on line 77 of the same file, you’ll see that out of the box, Views already establishes a callback (Drupal.Views.Ajax.ajaxViewResponse) that can be parsed into javascript as an object method. However, this method will apply to all Views that use the built-in ‘Use AJAX’ option. In most cases, this isn’t preferential since if you’re wanting to manipulate the response data, you’ll want to do it on a per-View basis.

In order to establish a callback on a per-View basis, we need to extract details of the view from the $view object specified in the hook callback, with the objective being to specify a specific callback for each View display. Since hook_ajax_data_alter is a non-persistent function (it’s only called right before the response data is sent through any javascript callbacks), on the surface it’s difficult to debug using conventional means. Fortunately, Watchdog can help here (albeit, tediously). Inside the $view object, you can determine the view name ($view->ajax_path[1]['view_name']) and display ($view->ajax_path[1]['view_display_id']), which when used together, can construct an appropriate if() statement, like the following:

function hook_ajax_data_alter(&$object, $module, $view) {
        /*
         * Overwrite the 'Drupal.Views.Ajax.ajaxViewResponse' callback that's in Views core
         * as we don't want both callbacks running concurrently.
         */

        $view_details = $view->ajax_path[1];

        if ($view_details['view_name'] == 'viewName' && $view_details['view_display_id'] == 'viewId'){
         $object->__callbacks = array('Drupal.moduleName.methodName');
        }

}

You’ll notice that I’ve added a comment which explains that I’m choosing to override the default Drupal.Views.Ajax.ajaxViewResponse
callback method by writing the __callbacks array key value explicitly. Obviously, you can use multiple callbacks (including the default one), if you wish.

And that should work!