Sunday, January 15, 2017

Menu callback both for ajax and GET requests

I've already written about how to use native Drupal ajax mechanism on front-end side in this post. There I've described how to send requests immediately or by clicking (or by any other event) on any DOM element. It works when javascript is enabled in user's browser. But how to make your menu callback work both with ajax and GET (when js is disabled) requests?

1. Menu router. Have to contain additional argument %ctools_js:

 * Implements hook_menu().
 * Just for example we will render a node form.
function module_menu() {
  $items = [];

  $items['some/%node/%ctools_js/path'] = [
    'title' => 'Test router',
    'page callback' => 'module_test_router_callback',
    'page arguments' => [1, 2],
    // For ajax requests first.
    'delivery callback' => 'ajax_deliver',
    'type' => MENU_CALLBACK,

  return $items;
2. Menu callback:

 * Menu callback for test router.
 * Just for example we will render a node form
 * and if it's ajax request then replace body element
 * with a form. Otherwise reload page and show
 * node form.
function module_test_router_callback($node, $ajax) {
  // Don't forget to include node specific file
  // for an example.
  module_load_include('inc', 'node', 'node.pages');
  $form = drupal_get_form($node->type . '_node_form', $node);

  // If it's ajax request then $ajax will be equals 1.
  if ($ajax) {
    // Set up command for replacing body content
    // with a rendered form.
    $commands[] = ajax_command_html('body', render($form));

    return [
      '#type' => 'ajax',
      '#commands' => $commands,
  else {
    // If it's not ajax request just deliver
    // html markup.
3. Link with a use-ajax class:

// Make sure you've added ajax library
// before printing a link.
drupal_add_library('system', 'drupal.ajax');

// Main point here is 'use-js' class for a link.
// Also take a look at 'nojs' argument. If js is
// enabled in a browser then this string will
// be replaced with 'ajax' and link will be look
// like 'some/NID/ajax/path'.
print l(t('Get node form'), 'some/NID/nojs/path', [
  'attributes' => [
    'class' => [
So what's happening here?

  • First. Clicking by the link with 'some/NID/nojs/path' href and 'use-ajax' class. If js is enabled in user's browser then 'nojs' substring will be replaced by 'ajax'.
  • Second. You need to find out what is %ctools_js. Ctools module provides a function ctools_js_load() that determines if it's ajax request or not ('ajax' or 'nojs' string passed as an argument to page callback).
  • Third. Page callback takes argument $ajax which equals 1 if ajax request and 0 otherwise (thanks to ctools_js_load() function). We're returning ajax commands for ajax requests and delivering render array with drupal_deliver_html_page() function otherwise. Since we've defined delivery callback for menu router as ajax_deliver we need to handle non ajax requests with drupal_deliver_html_page() function manually.

No comments:

Post a Comment