Correct ajax requests in Drupal 7
Many old-fashioned send ajax requests with their hands using $ .ajax (), while Drupal has a fairly flexible mechanism for this, which allows reusing ready-made code from the kernel and reducing the number of JS code.
In order to start using it, you need to deal with such things as delivery callback, Drupal.ajax and JS-commands.
I will describe briefly, because this question deserves a separate topic.
In D6, your callback menu should have returned the finished html, which then turned into theme ('page', $ content), and you get the page.
To return, for example, json data bypassing page.tpl, you had to call drupal_json ($ result), and write exit () at the end of the callback function.
D7 introduced a new concept - 'delivery callback'. Its essence is that the callback menu returns the answer in an intermediate form - in the form of an array in a certain format (see the description of render arrays at Drupal.org ), and the function specified in the corresponding hook_menu element as a delivery callback will determine how to respond to the client - darken as a page or convert to json.
Drupal has a standard way to send ajax requests.
Based on the implementation, he thought about working with forms (in fact, in the Form API it is used everywhere), but nothing prevents us from using it anywhere.
The simplest example looks like this:
The third line is needed to immediately send a request. If you skip it, the request will be sent at the moment the settings.event JS event (default 'mousedown') occurs over the DOM / jQuery element passed as the second argument to Drupal.ajax ().
In our case, I just want to immediately send a request, so all of this is skipped.
It should be noted that Drupal.ajax is declared in the misc / ajax.js file, so be sure to include it.
If you need to somehow influence the behavior of request processing, you just need to inherit from Drupal.ajax, redefine the desired method (for example success callback), and then create an object of its own class.
There is another way to influence the event handler - just redefine the function with the same name lower in the code (in the script that is connected later ajax.js), although I do not recommend this approach, sometimes it still needs to be implemented, for example, to affect all places where new Drupal.ajax is already in use, and you can’t crawl in there (script of another module).
I also note that Drupal.ajax will send POST, and json will be the dataType. If this does not suit you, then you need to redefine / inherit.
JS-commands are a set of JS-functions, especially in that the fact of their call and arguments can be determined on the server side, when generating an ajax-response.
You can do this by forming, as the return value of the callback menu for the ajax request, an array of the form:
Where $ command_name is the name of the property / JS function from the Drupal.ajax.prototype.commands object.
In this case, $ result is one of the options for the render array, and in order for it to be correctly converted to json data with the necessary headers, the hook_menu element corresponding to this callback must be set as 'delivery callback' - 'ajax_deliver':
They formed an answer, but who will call our teams? And here the third component comes to the rescue - Drupal.ajax.
Sending an ajax request with its help means that the success handler of the event has already been declared, it will iterate over all the installed commands from the response and call the corresponding functions.
Consider an example - receiving content using an ajax request and inserting it into some container on the page when you click on an element.
Usually we would write:
With our approach, we will replace it like this:
And such a code in the callback menu (do not forget about ajax_deliver as a delivery callback):
Thus, with our ajax request, we formed an array of commands that will be converted to json in ajax_deliver (), and then all commands will be called in Drupal.ajax.prototype.success as soon as the browser receives a response.
The meaning of the ajax_command_insert () function is to simply form an array with the name and parameters of the JS command.
In this case, to insert content from the 'data' key into the container on the page with the $ selector selector:
A number of ajax_command_ * functions are defined in the kernel for quickly generating predefined commands from ajax.js.
You can freely define your commands by simply adding a function to the Drupal.ajax.prototype.commands prototype:
In order to call it, you just need to set its name to gotoTab as the value of the 'command' key:
All additional keys of this array will be available in our JS function as response.YOUR_KEY, for example response.selector in the case of the insert command. This way we can pass any arguments.
What does all this give us?
Firstly, there is a ready-made set of commands, and we can generally avoid manually processing the success event.
A list of commands provided by the kernel can be found in ajax.js, where Drupal.ajax.prototype.commands is declared.
Secondly, we can write the implementation of our commands once, and then on the server side manage a set of commands (or for example, let someone change them through hook_alter) for each case, without changing the script code.
In order to start using it, you need to deal with such things as delivery callback, Drupal.ajax and JS-commands.
Delivery callback
I will describe briefly, because this question deserves a separate topic.
In D6, your callback menu should have returned the finished html, which then turned into theme ('page', $ content), and you get the page.
To return, for example, json data bypassing page.tpl, you had to call drupal_json ($ result), and write exit () at the end of the callback function.
D7 introduced a new concept - 'delivery callback'. Its essence is that the callback menu returns the answer in an intermediate form - in the form of an array in a certain format (see the description of render arrays at Drupal.org ), and the function specified in the corresponding hook_menu element as a delivery callback will determine how to respond to the client - darken as a page or convert to json.
Drupal.ajax
Drupal has a standard way to send ajax requests.
Based on the implementation, he thought about working with forms (in fact, in the Form API it is used everywhere), but nothing prevents us from using it anywhere.
The simplest example looks like this:
var settings = {url : myUrl};
var ajax = new Drupal.ajax(false, false, settings);
ajax.eventResponse(ajax, {});
The third line is needed to immediately send a request. If you skip it, the request will be sent at the moment the settings.event JS event (default 'mousedown') occurs over the DOM / jQuery element passed as the second argument to Drupal.ajax ().
In our case, I just want to immediately send a request, so all of this is skipped.
It should be noted that Drupal.ajax is declared in the misc / ajax.js file, so be sure to include it.
If you need to somehow influence the behavior of request processing, you just need to inherit from Drupal.ajax, redefine the desired method (for example success callback), and then create an object of its own class.
There is another way to influence the event handler - just redefine the function with the same name lower in the code (in the script that is connected later ajax.js), although I do not recommend this approach, sometimes it still needs to be implemented, for example, to affect all places where new Drupal.ajax is already in use, and you can’t crawl in there (script of another module).
I also note that Drupal.ajax will send POST, and json will be the dataType. If this does not suit you, then you need to redefine / inherit.
Js-commands
JS-commands are a set of JS-functions, especially in that the fact of their call and arguments can be determined on the server side, when generating an ajax-response.
You can do this by forming, as the return value of the callback menu for the ajax request, an array of the form:
function your_module_ajax_menu_callback() {
// ...
$result = array(
'#type' => 'ajax',
'#commands' => array(
array(
'command' => $command_name,
),
),
);
return $result;
}
Where $ command_name is the name of the property / JS function from the Drupal.ajax.prototype.commands object.
In this case, $ result is one of the options for the render array, and in order for it to be correctly converted to json data with the necessary headers, the hook_menu element corresponding to this callback must be set as 'delivery callback' - 'ajax_deliver':
function your_module_menu() {
// ...
$items['ajax/your-module/path'] = array(
'title' => 'Get content by AJAX',
'page callback' => 'your_module_ajax_menu_callback',
'page arguments' => array(),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'delivery callback' => 'ajax_deliver',
);
return $items;
}
They formed an answer, but who will call our teams? And here the third component comes to the rescue - Drupal.ajax.
Sending an ajax request with its help means that the success handler of the event has already been declared, it will iterate over all the installed commands from the response and call the corresponding functions.
Consider an example - receiving content using an ajax request and inserting it into some container on the page when you click on an element.
Usually we would write:
$('#somen-link').click(function () {
$.ajax({
type: 'GET',
url: myUrl,
dataType: 'html',
success: function (data) {
// Set up new content.
$('div.container').html(data);
}
});
});
With our approach, we will replace it like this:
var ajax = new Drupal.ajax(false, '#somen-link', {url : myUrl});
ajax.eventResponse(ajax, {});
And such a code in the callback menu (do not forget about ajax_deliver as a delivery callback):
$result = array('#type' => 'ajax');
$result['#commands'][] = ajax_command_insert('div.container', $html);
return $result;
Thus, with our ajax request, we formed an array of commands that will be converted to json in ajax_deliver (), and then all commands will be called in Drupal.ajax.prototype.success as soon as the browser receives a response.
The meaning of the ajax_command_insert () function is to simply form an array with the name and parameters of the JS command.
In this case, to insert content from the 'data' key into the container on the page with the $ selector selector:
array(
'command' => 'insert',
'method' => NULL,
'selector' => $selector,
'data' => $html,
'settings' => $settings,
);
A number of ajax_command_ * functions are defined in the kernel for quickly generating predefined commands from ajax.js.
You can freely define your commands by simply adding a function to the Drupal.ajax.prototype.commands prototype:
/**
* Ajax delivery command to switch among tabs by ID.
*/
Drupal.ajax.prototype.commands.gotoTab = function (ajax, response, status) {
// response.data is a value setted in 'data' key of command on PHP side.
if (response.data) {
// ...
}
};
In order to call it, you just need to set its name to gotoTab as the value of the 'command' key:
$result['#commands'][] = array(
'command' => 'gotoTab',
);
All additional keys of this array will be available in our JS function as response.YOUR_KEY, for example response.selector in the case of the insert command. This way we can pass any arguments.
What does all this give us?
Firstly, there is a ready-made set of commands, and we can generally avoid manually processing the success event.
A list of commands provided by the kernel can be found in ajax.js, where Drupal.ajax.prototype.commands is declared.
Secondly, we can write the implementation of our commands once, and then on the server side manage a set of commands (or for example, let someone change them through hook_alter) for each case, without changing the script code.