Многие по старинке шлют ajax запросы руками с помощью $.ajax(), в то время как в Drupal для этого есть достаточно гибкий механизм, который позволит переиспользовать готовый код из ядра, и сократить количество JS кода.
Для того что бы начать его использовать, необходимо разобраться с такими вещами как delivery callback, Drupal.ajax и JS-commands.
Delivery callback
Опишу коротко, потому что этот вопрос заслуживает отдельной темы.
В D6 ваш меню колбек должен был вернуть готовый html, который затем оборачивался в theme('page', $content), и вы получали страницу.
Что бы вернуть, например json-данные минуя page.tpl, приходилось вызывать drupal_json($result), а в конце функции колбека писать вызов exit().
В D7 ввели новое понятие — 'delivery callback'. Суть его в том, что меню колбек возвращает ответ в промежуточном виде — в виде массива в определенном формате (см. описание рендер-массивов на друпал.орг), а уже функция указанная в соответствующем элементе hook_menu как delivery callback, определит как отдать ответ клиенту — затемить как страницу или преобразовать в json.
Drupal.ajax
В Drupal существует стандартный способ отправления ajax-запросов.
Исходя из реализации, задумывался он для работы с формами (собственно в Form API он используется повсеместно), но ничего не мешает нам использовать его в любом месте.
Самый простой пример выглядит так:
var settings = {url : myUrl};
var ajax = new Drupal.ajax(false, false, settings);
ajax.eventResponse(ajax, {});
Третья строка нужна чтобы тут же послать запрос. Если ее пропустить, то запрос будет послан в момент, когда произойдет JS-событие settings.event (по умолчанию 'mousedown') над DOM/jQuery элементом переданным в качестве второго аргумента Drupal.ajax().
В нашем случае я просто хочу тут же послать запрос, поэтому все это пропущено.
Следует отметить, что Drupal.ajax объявлен в файле misc/ajax.js, так что не забудьте его подключить.
Если нужно как-то повлиять на поведение обработки запроса, необходимо просто отнаследоваться от Drupal.ajax, переопределить нужный метод (например success callback), а затем создавать объект уже своего класса.
Есть еще один способ повлиять на обработчик какого-то события — просто переопределить функцию с тем же именем ниже по коду (в скрипте который подключен позже ajax.js), хотя я не рекомендую такой подход, иногда его все же приходится реализовывать, например что бы повлиять на все места, где уже заведомо используется new Drupal.ajax, и вам туда не подлезть (скрипт чужого модуля).
Так же отмечу, что Drupal.ajax будет слать POST, и в качестве dataType будет json. Если вам это не подходит, то надо переопределять/наследоваться.
JS-commands
JS-команды это набор JS-функций, особенных тем, что факт их вызова и аргументы могут быть определены на стороне сервера, при генерации ajax-ответа.
Сделать это можно сформировав, как возвращаемое значение меню колбека для ajax-запроса, массив вида:
function your_module_ajax_menu_callback() {
// ...
$result = array(
'#type' => 'ajax',
'#commands' => array(
array(
'command' => $command_name,
),
),
);
return $result;
}
Где $command_name — имя свойства/JS-функции из объекта Drupal.ajax.prototype.commands.
В данном случае $result — один из вариантов рендер-массива, и для того что бы он корректно преобразовался в json-данные, с нужными заголовками, необходимо элементу hook_menu, соответствующему этому колбеку, поставить в качестве '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;
}
Ответ сформировали, но кто же вызовет наши команды? И тут на помощь приходит третий компонент — Drupal.ajax.
Отсылка ajax-запроса с его помощью означает что и обработчик success события уже объявлен, он переберет все установленные команды из ответа, и вызовет соответствующие функции.
Рассмотрим пример — получение контента с помощью ajax-запроса и вставка в какой-то контейнер на странице при клике на элемент.
Обычно мы бы написали:
$('#somen-link').click(function () {
$.ajax({
type: 'GET',
url: myUrl,
dataType: 'html',
success: function (data) {
// Set up new content.
$('div.container').html(data);
}
});
});
С помощью нашего подхода, мы заменим это так:
var ajax = new Drupal.ajax(false, '#somen-link', {url : myUrl});
ajax.eventResponse(ajax, {});
И такой код в меню колбеке (не забываем про ajax_deliver в качестве delivery callback):
$result = array('#type' => 'ajax');
$result['#commands'][] = ajax_command_insert('div.container', $html);
return $result;
Таким образом при нашем ajax запросе мы сформировали массив команд, который в ajax_deliver() будет преобразован в json, а затем все команды будут вызваны в Drupal.ajax.prototype.success как только браузер получит ответ.
Смысл функции ajax_command_insert() — просто сформировать массив с именем и параметрами JS-команды.
В данном случае для вставки контента из ключа 'data' в контейнер на странице с селектором $selector:
array(
'command' => 'insert',
'method' => NULL,
'selector' => $selector,
'data' => $html,
'settings' => $settings,
);
В ядре определен ряд ajax_command_* функций для быстрого формирования предопределенных команд из ajax.js.
Можно свободно определять свои команды, просто добавив функцию в прототип Drupal.ajax.prototype.commands:
/**
* 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) {
// ...
}
};
Для того что бы ее вызвать, нужно просто в качестве значения ключа 'command', установить ее имя — gotoTab:
$result['#commands'][] = array(
'command' => 'gotoTab',
);
Все дополнительные ключи этого массива будут доступны в нашей JS-функции как response.YOUR_KEY, например response.selector в случае с командой insert. Таким образом мы можем передавать любые аргументы.
Что нам все это дает?
Во первых есть уже готовый набор команд, и мы можем вообще избежать ручной обработки success события.
Список команд предоставленных ядром можно найти в ajax.js, там где объявляется Drupal.ajax.prototype.commands.
Во вторых, мы можем единожды написать реализацию своих команд, а затем на стороне сервера управлять набором команд (или например дать кому-то изменять их через hook_alter) для каждого случая, не изменяя код скриптов.
Автор: IRuslan