Аватар пользователя Dima

Creating custom Drupal.ajax object 'on a fly' and attach it to any element on the page (div, td)


From Drupal documentation we know how to add 'AJAX' for links and form element, from PHP with '#ajax' element attribute, but this technics propose to create AJAX oblect on client side.
To create new Drupal.ajax object we need to do the following:

  1. Select the element to attach Drupal.ajax object to. This element should have 'id' HTML attribute. AJAX behaviour can be attached to every HTML elements on the page which has 'id' HTML attribute.
  2. Build object of options for new AJAX element.
  3. Add new AJAX object to global Drupal.ajax object.

For example we have table with data which is requested from other server with drupal_http_request() which can take a long time. To avoid waiting for response from other server, we can load page, and after that trigger AJAX request to our server, where callback function will request to remote another server (some kind of lazy load). Lets see how it is works.

STEP BY STEP

  1. Select the element to attach Drupal.ajax object to.
    Lets create HTML element first.
    1. /**
    2.  * Menu callback.
    3.  *
    4.  * Build container to load ajax content to.
    5.  */
    6. function my_module_remote() {
    7. $wrapper = array();
    8. $wrapper['#attached']['js'] = array(
    9. 'system', 'drupal.ajax',
    10. drupal_get_path('module', 'my_module') . '/my_module_ajax.js'
    11. );
    12. $wrapper['container'] = array(
    13. '#type' => 'container',
    14. '#attributes' => array(
    15. 'id' => 'remote-content-wrapper',
    16. 'title' => t('click to reload')
    17. )
    18. );
    19. $wrapper['container']['content'] = array(
    20. '#markup' => t('Loading data...')
    21. );
    22. return $wrapper;
    23. }

    This callback will create simple page with div container and text "Loading data..." on it. Pay attention to element with id "remote-content-wrapper". This div container we will be base for our Drupal.ajax object.
    On js file we select it is with #
    1. $('#remote-content-wrapper').once('remote-content-wrapper', function() {
    2. var base = $(this).attr('id');
    3. ...
    4. });

    NOTE I use '$.once()' method here to attach behaviour only once. Read more.
    So, the html element selected, it is div#remote-content-wrapper.
  2. Build object of options for new AJAX element.
    1. ...
    2. var element_settings = {
    3. url: 'http://' + window.location.hostname + settings.basePath + settings.pathPrefix + 'ajax/remote',
    4. event: 'click',
    5. progress: {
    6. type: 'throbber'
    7. }
    8. };
    9. ...

    The only required keys is:
    'url' - the path to make ajax call to, any drupal full path.
    'event' - the javascript/jQuery event to react to, any string or one of the registered like 'click', 'change', 'keypress'
    'progress' - the progress type. Object contain 'type' key, which can be either 'throbber' or 'bar'.
    Other settings:
    1. keypress: true,
    2. effect: 'none', // jQuery effect.
    3. speed: 'none', // setting for jQuery effect.
    4. method: 'replaceWith', // Will be rewritten with ajax command.
    5. // The additional data to submit.
    6. submit: {
    7. 'js': true
    8. }
  3. Add new AJAX object to global Drupal.ajax object.
    It is simple easy:
    1. Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);

    Drupal.ajax() function-constructor takes 3 parameters:
    element id,
    DOM object,
    settings object
    NOTE The new element should be stored in global Drupal.ajax array of ajax objects under the element id key. Every Drupal.ajax object on the page are stored in this array.

That it. The Drupal.ajax element created.
To trigger new ajax reaction we should call registered in element_settings object event:

  1. $(this).click(); // $('#remote-content-wrapper').click();

AJAX callback should return array of drupal or registered custom ajax commands which will be executed after success ajax call.

The complete js part:

  1. (function ($) {
  2. /**
  3.   * Load remote content after the main page loaded.
  4.   */
  5. Drupal.behaviors.my_module_load_remote_content = {
  6. attach: function(context, settings) {
  7. $('#remote-content-wrapper').once('remote-content-wrapper', function() {
  8.  
  9. var base = $(this).attr('id');
  10. var argument = $(this).attr('argument');
  11.  
  12. var element_settings = {
  13. url: 'http://' + window.location.hostname + settings.basePath + settings.pathPrefix + 'ajax/remote',
  14. event: 'click',
  15. progress: {
  16. type: 'throbber'
  17. }
  18. };
  19. Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
  20. $(this).click();
  21. });
  22. }
  23. };
  24. })(jQuery);

The complete PHP part:

  1. /**
  2.  * Implements hook_menu().
  3.  */
  4. function my_module_menu() {
  5. $items['ajax/remote'] = array(
  6. 'title' => 'Data',
  7. 'page callback' => 'my_module_remote_ajax',
  8. 'access arguments' => array('access content'),
  9. 'type' => MENU_CALLBACK,
  10. );
  11. $items['page'] = array(
  12. 'title' => 'Data',
  13. 'page callback' => 'my_module_remote',
  14. 'access arguments' => array('access content'),
  15. 'type' => MENU_NORMAL_ITEM,
  16. );
  17. return $items;
  18. }
  19.  
  20. /**
  21.  * Menu callback.
  22.  *
  23.  *
  24.  */
  25. function my_module_remote() {
  26. $wrapper = array();
  27. $wrapper['#attached']['js'] = array(
  28. 'system', 'drupal.ajax',
  29. drupal_get_path('module', 'my_module') . '/my_module_ajax.js'
  30. );
  31. $wrapper['container'] = array(
  32. '#type' => 'container',
  33. '#attributes' => array(
  34. 'id' => 'remote-content-wrapper',
  35. 'title' => t('click to reload')
  36. )
  37. );
  38. $wrapper['container']['content'] = array(
  39. '#markup' => t('Loading data...')
  40. );
  41. return $wrapper;
  42. }
  43.  
  44. /**
  45.  * Ajax callback.
  46.  *
  47.  * Load remote information.
  48.  */
  49. function my_module_remote_ajax() {
  50.  
  51. $commands = array();
  52.  
  53. $selector = '#remote-content-wrapper';
  54. $html = my_module_function_to_request_data_form_remote_server_and_build_html();
  55. // Debug messages, we can use dpm() here.
  56. $messages = theme('status_messages');
  57. $commands[] = ajax_command_prepend('body', $messages);
  58. // Add command with insert new requested content
  59. $commands[] = ajax_command_html($selector, $html);
  60.  
  61. ajax_render($commands);
  62. drupal_exit();
  63. }