Drupal → Добавление элементов в форму с помощью AJAX

16.06.2011

Для примера создадим выпадающий список с цифрами от 1 до 4, при выборе значения в котором, ниже будет появляться выбранное количество чекбоксов. Ссылка не демо.

Код, комментарии ниже:

function mymodule_dynamic_form($form, &$form_state) {
  // При сабмите формы или вызове ajax, в $form_state['values'] будут значения всех полей.
  $checkboxes_count = isset($form_state['values']) ? $form_state['values']['checkboxes_count'] : 1;

  $form['checkboxes_count'] = array(
    '#type' => 'select', 
    '#title' => 'Число чекбоксов', 
    '#options' => array(1 => 1, 2 => 2, 3 => 3, 4 => 4), 
    '#default_value' => $checkboxes_count,
    '#ajax' => array(
      // Функция, которая сработает при выборе значения в списке,
      // и которая должна вернуть новую часть формы
      'callback' => 'mymodule_dynamic_form_ajax_callback',
      // Id html элемента, в который будет выведена часть формы
      'wrapper' => 'checkboxes-div',
    ),
  );

  // Элемент, в который будет выводится новая часть формы
  $form['checkboxes'] = array(
    '#prefix' => '<div id="checkboxes-div">', 
    '#suffix' => '</div>', 
  );

  // Генерация элементов на основе данных из $form_state['values']
  for ($i = 1; $i <= $checkboxes_count; $i++) {
    $form['checkboxes']['checkbox' . $i] = array(
      '#type' => 'checkbox', 
      '#title' => 'Checkbox ' . $i,
    );
  }

  $form['submit'] = array(
    '#type' => 'submit', 
    '#value' => 'Отправить',
  );

  return $form;
}

function mymodule_dynamic_form_ajax_callback($form, $form_state) {
  return $form['checkboxes'];
}

function mymodule_dynamic_form_submit($form, $form_state) {
  debug($form_state['values']);
}

Основной момент — логика, по которой элементы добавляются в форму, должна быть размещена в конструкторе формы. Т.е. цикл, генерирующий чекбоксы, должен быть размещён в функции mymodule_dynamic_form(), а не в ajax калбэке mymodule_dynamic_form_ajax_callback().

По началу это взрывает мозг, но объясню почему надо делать именно так.

Конструктор формы вызывается на всех этапах работы с формой — при создании, при валидации, при отправке, при ajax запросах. Ajax калбэки же вызываются только при событии, на которое они навешены. И если бы логика создания новых элементов была в них, то к этим элементам невозможно было бы получить доступ на любом другом из перечисленных этапов, плюс к таким элементам нельзя бы было подцепиться из hook_form_alter(). Единственная задача ajax калбэка — это вернуть либо часть формы, подготовленной в конструкторе, либо набор команд для ajax фреймворка.

Понять, на каком этапе вызывается конструктор формы можно по наличию данных в $form_state['values'].

Больше примеров можно найти в модуле для разработчиков Examples for Developers.

Не будет лишним узнать, как заставить работать ajax формы с выключенным javascript.

Написанное актуально для
Drupal 7
Похожие записи

Комментарии

Гость
01.09.2011, 16:16

спасибо вам за статьи.
и такой у меня тупой вопрос:) а обычная php- функция может быть калбаком? нужно чтобы при выборе определенного пункта из формы, подгружался текст, генерируемый такой функцией.

mymodule_dynamic_form_ajax_callback это и есть "обычная php- функция"

Как сделать так чтобы при обновлении элемента с чекбоксами они скидывались в дефолтное состояние? т.е. если флажок был отмечен при обновлении он опять стал не отмеченным?

Дерек
19.03.2014, 19:54

а если модифицировать немного данный модуль, например, сделать что то на подобие зависимых полей, то есть выбирается из списка значение, и на основе этих данных, выключить блок других полей. Вопрос не подскажите откуда брать выбранное значение из списка? на пример список - 1,2,3,4,5 выбираю 4 => скрывается поле. Вот как выполнить проверку, что выбрано 4ое значение?

Дерек
20.03.2014, 11:55

блин, два дня с этим вожусь, почему может не быть ['values'] у $form_state. пишет неизвестный индекс.

Гость
05.02.2015, 11:16

Добрый день. Подскажите пожалуйста. Есть функция, которая отвечает за подтверждение удаления определенного элемента из таблицы. Как передать значение этой функции в всплывающее окно ajax?

function my_database_delete_page_title($idTovar) {

	$placeholders = array(':idTovar' => $idTovar);
        $Naim = db_query("SELECT `Naim` FROM {my_table} WHERE `idTovar` = :idTovar",     $placeholders)->fetchField();
        return t('Вы точно хотите удалить "@naim"?', array('@naim' => $Naim));
	}

В статье написано, что логика, изменяющая форму, должна быть в конструкторе формы. Но это не так если вам надо изменить #default_value у элемента, который перезагружается через ajax. Потому что при перезагрузке через ajax аттрибут #default_value будет уже игнорироваться. При этом нужно оставить возможность пользователю изменять поле, поэтому аттрибут #value нельзя указывать в конструкторе формы.

Решение: в callback функции нужно изменять #value аттрибут изменяемого элемента. Этот же #value аттрибут будет в последствии содержать и то, что ввел пользователь в это поле:

function example_ajax_callback($form, $form_state) {
  $form['matrix_section']['#value'] = 'adf';
  return $form['matrix_section'];
}

Вообще это же можно делать и в конструкторе формы, главное не добавлять аттрибут #value с самого начала, иначе не будет учитываться #default_value:

  if (isset($form_state['values'])) {
    $form['matrix_section']['#value'] = 'asdddd';
  }

в конструкторе можно изменять $form_state

Игорь
16.09.2015, 13:55

Всё правильно всё работает ;-) Но мне не нравится упираться только в одну команду, один метод 'replace' например, который здесь не написан, но он идет по умолчанию.

'#ajax' => array(
      'callback' => 'mymodule_dynamic_form_ajax_callback',
      // 'wrapper' => 'checkboxes-div',
      // 'method' => 'replace',
    ),

Это можно спокойно удалить и оставить один колбек ('callback'). И вот наступает полная свобода действий

function mymodule_dynamic_form_ajax_callback($form, $form_state) {
  // return $form['checkboxes'];
  $commands = array();    
  $html = drupal_render($form['checkboxes']);
  $commands[] = ajax_command_invoke('#checkboxes-div', 'append', array($html));
  // Удалить/Добавить/Изменить что угодно и где угодно на странице
  // Поддерживаются любые методы jQuery!
  $commands[] = ajax_command_invoke('#drugoy-id', 'prepend', array('<p>Добавим сообщение</p>') );
  return array('#type' => 'ajax', '#commands' => $commands);
}

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