Формы AJAX в Drupal 7

AJAX-формы в Drupal 7 предоставляют динамическое поведение, позволяют работать без полной перезагрузки страницПонятие об AJAX-формах

AJAX-формы в Drupal 7 предоставляют динамическое поведение, т.е. позволяют работать без полной перезагрузки страниц. Их легко создавать и поддерживать. По сути, они являются расширением Form API Drupal.

Что означит динамическое поведение? При традиционном подходе пользователь заполняет форму, нажимает кнопку, вся страница целиком формируется на сервере заново и возвращается браузеру. AJAX-формы обновляют или заменяют часть страницы или часть формы без полной перезагрузки страницы - изменяется только та часть, которую необходимо изменить. Так реакция на действия пользователя обычно на много быстрее чем при традиционном подходе с полной перезагрузкой страницы.

Некоторые сведения об AJAX:

  • AJAX формы реализуют динамическое поведение форм без перезагрузки страницы.
  • Они существенно упрощены в Drupal 7.
  • Как разработчику, Вам нет небходимости разбираться в Javascript для создания AJAX-форм. Drupal все делает за Вас.
  • AJAX являются ближайшими родственниками многоступенчатым формам.
  • В большинстве случаев, AJAX-формы динамически заменяют собой HTML-регионы на странице, которые в большинстве случаев являются частью перестроенной формы.

Небольшое отступление:

  • До Drupal 7, AJAX-формы были известнв как AHAH-формы, т.к. AJAX (Asynchronous Javascript and XML) подразумевает привлечение XML, а Drupal технически не использовал XML (и javascript так же оставался за кадром). В Drupal 7 терминология была изменена и стали использовать хорошо известную аббревиатуру "AJAX". Так же Drupal 7 здесь было довольно много черной магии, необходимой для обеспечения корректной работы форм в фоновом режиме. Все это было стандартизовано и перенесено в код ядра Drupal, так что Вы можете об этом не заботиться.
  • Здесь множество примеров AJAX поведения в Drupal, которое не имеет одношения к формам AJAX. Например, the Fivestar module использует собственную AJAX реализацию взаимодействия боаузера с сервером без перезагрузки страниц.

Важные понятия

  1. Ваша форма перестраивается, когда Вы манипулируете элементами формы (выбор, отправка или что- то еще)
  2. Ваша функция конструктора формирует её различными способами, в зависимости от входных данных ($form_state)
  3. Ваши настройки #ajax функция обратного вызова формируют либо всю вновь создаваемую форму, либо её часть для замены или другого преобразования части страницы.

Основы

Для создания AJAX-формы Вы:

  • Помечаете элементы формы как использующие AJAX с помощью свойства #ajax. Эти элементы формы будут теперь посылать фоновый AJAX вызов, когда они изменяются или посетитель сайта щелкает по ним.
    • Свойство #ajax['wrapper'] включает HTML ID блока страницы, который должен быть заменен (или изменен другим способом).
    • #ajax['callback'] сообщает форме, какая функция должна быть вызвана после того как произошел вызов AJAX и форма была переформирована.
  • Создаете функцию обратного вызова (называемую #ajax['callback']). Это в большинстве случаев очень простая функция. Она ничего не делает, но выбирает и возвращает часть формы, которая заменяется на оригинальной странице.

В примере Examples Module "AJAX Example: generate checkboxes" AJAX-элементом является select $form['howmany_select'], который вызывает замену HTML элемента с ID 'checkboxes-div' (называемого #ajax['wrapper']), который в свою очередь является оберткой для fieldset $form['checkboxes_fieldset']:

<?php
/**
* AJAX-элемент select вызывает замену набора переключателей
* на основании выбора пользователя.
*/
function ajax_example_autocheckboxes($form, &$form_state) {

  $default = !empty($form_state['values']['howmany']) ? $form_state['values']['howmany'] : 1;

  $form['howmany_select'] = array(
    '#title' => t('How many checkboxes do you want?'),
    '#type' => 'select',
    '#options' => array(1 => 1, 2 => 2, 3 => 3, 4 => 4),
    '#default_value' => $default,
    '#ajax' => array(
      'callback' => 'ajax_example_autocheckboxes_callback',
      'wrapper' => 'checkboxes-div',
      'method' => 'replace',
      'effect' => 'fade',
    ),
  );

  $form['checkboxes_fieldset'] = array(
    '#title' => t("Generated Checkboxes"),
    // prefix/suffix предоставлояет div, который мы заменяем,
    // названный выше #ajax['wrapper'].
    '#prefix' => '<div id="checkboxes-div">',
    '#suffix' => '</div>',
    '#type' => 'fieldset',
    '#description' => t('This is where we get automatically generated checkboxes'),
  );

// Завершение примера ниже!

?>

Когда элемент 'howmany_select' изменяется, фоновый запрос отправляется на сервер, запрашивая пересоздание формы. После пересоздания формы, используя измененное поле 'howmany' в качестве входной информации для построения формы, запускается функция обратного вызова:

<?php
/**
* Для обновления необходимо вернуть только элемент select.
* С тех пор как #ajax['callback'] стал способен возвращать HTML или отображаемый массив (или
* массив команд), мы можем возвращать лишь часть формы.
*/
function ajax_example_autocheckboxes_callback($form, $form_state) {
  return $form['checkboxes_fieldset'];
}
?>

В этом случае (как и во многих других случаях) возвращается только выбранная часть формы, которая должна быть заменена на HTMLстранице. Эта часть формы отображается позднее и возвращается на страницу, где она заменяет собой предоставленный блок #ajax['wrapper'].

Эти формы AJAX как в ореховой скорлупе. Элемент формы со свойством #ajax отправляет фоновый запрос на сервер, когда происходит щелчок по элементу или его изменение. Форма перестраивается на сервере функцией конструктора формы, а затем вызывается функция обратного вызова заданная в #ajax['callback'], которая выделяет часть формы, возвращаемую для замены на оригинальной странице.

Подробнее

Здесь завершается обсуждаемый выше пример, из AJAX Examples в модуле Examples. (Это живой и поддерживаемый пример. Вы можете загружить модуль Examples и поэкспериментировать с этим примером.)

<?php
/**
* AJAX-элемент select вызывает замену набора переключателей
* на основании выбора пользователя.
*/
function ajax_example_autocheckboxes($form, &$form_state) {
  $default = !empty($form_state['values']['howmany']) ? $form_state['values']['howmany'] : 1;
  $form['howmany_select'] = array(
    '#title' => t('How many checkboxes do you want?'),
    '#type' => 'select',
    '#options' => array(1 => 1, 2 => 2, 3 => 3, 4 => 4),
    '#default_value' => $default,
    '#ajax' => array(
      'callback' => 'ajax_example_autocheckboxes_callback',
      'wrapper' => 'checkboxes-div',
      'method' => 'replace',
      'effect' => 'fade',
    ),
  );
  $form['checkboxes_fieldset'] = array(
    '#title' => t("Generated Checkboxes"),
    // The prefix/suffix provide the div that we're replacing, named by
    // #ajax['wrapper'] above.
    '#prefix' => '<div id="checkboxes-div">',
    '#suffix' => '</div>',
    '#type' => 'fieldset',
    '#description' => t('This is where we get automatically generated checkboxes'),
  );
  $num_checkboxes = !empty($form_state['values']['howmany_select']) ?$form_state['values']['howmany_select'] : 1;
  for ($i=1; $i<=$num_checkboxes; $i++) {
    $form['checkboxes_fieldset']["checkbox$i"] = array(
      '#type' => 'checkbox',
      '#title' => "Checkbox $i",
    );
  }
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}

/**
* Для обновления необходимо вернуть только элемент select.
* С тех пор как #ajax['callback'] стал способен возвращать HTML или отображаемый массив (или
* массив команд), мы можем возвращать лишь часть формы.
*/
function ajax_example_autocheckboxes_callback($form, $form_state) {
  return $form['checkboxes_fieldset'];
}
?>
  1. С точки зрения пользователя - это обычная форма.
  2. На форме div с HTML ID 'checkboxes' оборачивает собой $form['checkboxes']. Это делаетмя с помощью $form['checkboxes']['#prefix'] и $form['checkboxes']['#suffix'].
  3. Если пользователь изменяет $form['howmany'], Для перерисовки формы создается фоновый запрос на сервер.
  4. Форма перестраивается с тем количеством переключателей, которое было запрошено с помощью $form['howmany'].
  5. Вызывается функция ajax_example_autocheckboxes_callback(). Она выделяет часть формы, которая заменяется на странице (почти всегда это то, что внутри#ajax['wrapper']).
  6. Возвращенная с сервера часть преобразуется в HTML, отправляется обратно на страницу и заменяет собою div с id 'checkboxes'.

Детали и предупреждения

  • Изменения формы должны выполняться только в функции конструктора формы (в этом примере - ajax_example_autocheckboxes()), иначепроверка на корректность кода потерпит неудачу. The callback function must not alter the form or any other state.
  • Есть возможность заменять любой HTML на странице, а не только элементы формы. Нужно только предоставить ID обертки.
  • Вы легко можете заменять форму целиком, если это удобнее. Только добавьте #prefix и #suffix в массив всей формы, а затем установите его как #ajax['wrapper']. (Это позволит Вам изменить несколько элементов формы одним вызовом ajax.) Нужно только иметь ввиду, что чем меньше передается информации, тем быстрее будет выполняться процесс.
  • Важно понимать, что $form, с которой вы имеете дело в функции обратного вызова, уже прошла через все функции обработки формы (но еще не была отправлена в drupal_render()). Разметка элемента достаточно простая:
<?php
  $elements['some_element']['#markup'] = 'New markup.';
  return $elements;
?>

Изменение значения, которое уже было сконвертировано в свойство #attributesподразумевает углубление в массив $form, так же как и изменение свойств соответствующих элементов.

<?php
  // Вам необходимо сделать и то и другое
  $elements['some_element']['#disabled'] = TRUE;
  $elements['some_element']['#attributes']['disabled'] = 'disabled';
  return $elements;
?>

Работа с браузером без поддержки Javascript

Нужно учитывать, что браузер может не поддерживать Javascript, и предложить на этот случай какой-то другой способ сохранить функциональность. В формах AJAX для этого есть встроенный механизм, позволяющий без существенных усилий обеспечить корректное поведение форм в любой среде: как с Javascript так и без Javascript. В большинстве случаев кнопка "next" должна преусматриваться с эелментом AJAX. Когда это возможно, страница (и форма) формируются как AJAX-элементы. Examples module предоставляет несколько примеров AJAX с обходом отсутствия Javascript ajax_example_graceful_degradation.inc:

  • Кнопка add-more
  • Пример зависимого выпадающего списка
  • Динамические секции
  • Мастер (классическая пошаговая форма)

Расширенные свойства AJAX

AJAX Framework предоставляет на много больше возможностей и опций в дополнение к базовому поведению форм.

  • AJAX Framework Commands может использоваться на стороне сервера для обеспечения динамического поведения страниц. Фактически, функция #ajax['callback'] может возвращать массив этих команд вместо отображаемого массива или строки HTML. Эти команды предлагают общие функции динамических страниц, которые выходят за пределы простых Form API операций. Например, Views module предоставляет пользовательский интерфейс для этих сложных операций.
  • Функция #ajax['callback'] не возвращает часть формы. Она может вернуть любой отображаемый массив или строку HTML.
  • Метод replace является наиболее распространенным, но возможно так же выполнять и другие операции с содержимым, возвращаемым функцией #ajax['callback'], включая проверку, добавление и т.д.
  • Функцию ajax_form_callback() можно заменить собственной функцией. При этом функцию ajax_form_callback() можно использовать как образец. В этом случае Вы должны изменить значение по умолчанию 'system/ajax' для #ajax['path'] и установить пункт меню hook_menu() для указания на Ваш измененный путь.

Дополнительные ресурсы

  • Examples module предоставляет примеры, данные здесь, зависимый выпадающий список AJAX и несколько других примеров, включая примеры работы с отключенным javascript.
  • Смотрите документацию AJAX Framework и Form API Reference обсуждающие свойства #ajax.