Drupal Views custom AJAX pagination callback

Submitted by swim on Sun, 03/16/2014 - 04:39

The following example will illustrate how to override a views default ajax pagination callback. There are a number of reasons this could be useful. Views ajax pagination will replace the entire view element; which is of course required it's still doing more than we might need. We might also want to keep the already loaded content and simply append new rows. Please note this is still a work in progress and is intended for the Owl Carousel module. The administrator will be given an option to override default views pagination in favor of loading in new slides (rows) via ajax without replacing the entire view.

The content being loaded into the view could in theory be anything. The first step is to create a menu callback which will be triggered via our views pagination.


/**
 * Implements hook_menu().
 */
function owlcarousel_views_menu() {
  $items['owlcarousel/views/ajax'] = array(
    'title' => 'Owl Carousel',
    'page callback' => 'owlcarousel_views_ajax',
    'delivery callback' => 'drupal_json_output',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );

  return $items;
}

/**
 * Menu callback, load next row (slide) by ajax.
 */
function owlcarousel_views_ajax() {
  // Get view ident.
  $name = $_REQUEST['view_name'];
  $display_id = $_REQUEST['view_display_id'];

  $view = views_get_view($name);
  $view->execute($display_id);
  $view->preview();
  
  // Render only view row results.
  $renderer = $view->style_plugin->row_plugin;
  foreach ($view->result as $index => $row) {
    $view->row_index = $index;
    $renderer = $view->style_plugin->row_plugin;
    $rows[] = $renderer->render($row);
  }

  drupal_json_output($rows);
}

Now that we have our content ready; the final step is to override the default views pagination. There seems to be a few methods for achieving this, bind/ unbind events, be a maverick and just send another $.ajax request etc. Regardless of the method chosen our function owlcarousel_views_ajax(); requires at the very least the view name and display id to be sent in the request. The simplest and cleanest approach should be to override/ copy the pre-existing function Drupal.views.ajaxView.prototype.attachPagerLinkAjax from views/js/ajax_view.js. The modified function is then placed into owlcarousel.settings.js.


/**
 * @file
 * owlcarousel.views.js
 */

(function($) {

    /**
     * Modified attach ajax behavior to a singe link.
     */
    Drupal.views.ajaxView.prototype.attachPagerLinkAjax = function(id, link) {
      var $link = $(link);

      // @todo, add check for remaining rows.
      if ($link.hasClass('next')) {
        var viewData = {};
        var href = $link.attr('href');

        $.extend(viewData, this.settings, Drupal.Views.parseQueryString(href), Drupal.Views.parseViewArgs(href, this.settings.view_base_path));
        $.extend(viewData, Drupal.Views.parseViewArgs(href, this.settings.view_base_path));
        this.element_settings.submit = viewData;

        var owl = $(this.element_settings.selector).find('.owl-carousel');
        var view = owl.parent().parent();

        if (owl.length) {
          this.element_settings.url = Drupal.settings.basePath + 'owlcarousel/views/ajax';
          this.element_settings.success = onSuccess;
        }

        this.pagerAjax = new Drupal.ajax(false, $link, this.element_settings);
      }

      /**
       * Append new slide.
       */
      function onSuccess(content) {
        var page = owl.data('owlCarousel').currentItem;

        owl.data('owlCarousel').addItem(content);
        owl.data('owlCarousel').jumpTo(page);
        view.find('.ajax-progress-throbber').remove();

        if (isNaN(this.element_settings.submit.page)) {
          this.element_settings.submit.page = 1;
        }
        this.element_settings.submit.page++;
      }
    };

}(jQuery));

That should be it. While a fairly fringe use case this example should demonstrate how easily views results can be altered to suit. Delivery of large media such as images and video to mobile devices is becoming an increasing problem. The best method I have found is simple; reduce the number of concurrent HTTP requests.