Правильные ajax запросы в Drupal 7

от автора

Многие по старинке шлют 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) для каждого случая, не изменяя код скриптов.

ссылка на оригинал статьи http://habrahabr.ru/post/164443/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *