Drupal → Реализация AJAX кнопки "Add more" с помощью progressive enhancement

10.02.2013

Пример реализации формы с кнопкой Add more, добавляющей бесконечное количество полей Name:

Форма с кнопкой 'Add more'

Создаём форму, которая будет работать с выключенным Javascript:

/**
 * Form builder.
 */
function example_add_more_form($form, &$form_state) {
  $form['names'] = array(
    '#tree' => TRUE,
  );
  
  // See example_add_more_form_add().
  if (empty($form_state['name_count'])) {
    $form_state['name_count'] = 1;
  }
  
  for ($i = 0; $i < $form_state['name_count']; $i++) {
    $form['names'][$i]['name'] = array(
      '#type' => 'textfield',
      '#title' => t('Name'),
    );
  }
  
  $form['add_more'] = array(
    '#type' => 'submit',
    '#value' => t('Add more'),
    '#submit' => array('example_add_more_form_add'),
  );
  
  return $form;
}

/**
 * "Add more" button submit callback.
 */
function example_add_more_form_add($form, &$form_state) {
  $form_state['name_count']++;
  $form_state['rebuild'] = TRUE;
}

Добавляем AJAX функционал:

/**
 * Form builder.
 */
function example_add_more_form($form, &$form_state) {
  $form['names'] = array(
    '#tree' => TRUE,
    '#prefix' => '<div id="names-wrapper">', // <-- New
    '#suffix' => '</div>',                   // <-- New
  );
  
  // See example_add_more_form_add().
  if (empty($form_state['name_count'])) {
    $form_state['name_count'] = 1;
  }
  
  for ($i = 0; $i < $form_state['name_count']; $i++) {
    $form['names'][$i]['name'] = array(
      '#type' => 'textfield',
      '#title' => t('Name'),
    );
  }
  
  $form['add_more'] = array(
    '#type' => 'submit',
    '#value' => t('Add more'),
    '#submit' => array('example_add_more_form_add'),
    '#ajax' => array( // <-- New
      'wrapper' => 'names-wrapper',
      'callback' => 'example_add_more_form_update',
    ),
  );
  
  return $form;
}

/**
 * "Add more" button submit callback.
 */
function example_add_more_form_add($form, &$form_state) {
  $form_state['name_count']++;
  $form_state['rebuild'] = TRUE;
}

/**
 * "Add more" button ajax callback.
 */
function example_add_more_form_update($form, $form_state) { // <-- New
  return $form['names'];
}

Демо + исходники примера.

Старенькая но актуальная статья — Добавление элементов в форму с помощью AJAX.

Пример под Drupal 8.

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

Комментарии

Меня всегда удивляла реализация Add more аяксом и встречала я её только в друпале. Зачем оно надо я так и не осилила понять.
Вместо Unlimited я предпочитаю поставить 10 (или даже 100 если припрёт) и показывать/скрывать это дело jQuery.

простейший кейс, который сломает твою реализацию — у поля Name есть дефолтное значение, юзер добавил несколько полей, отправил форму, форма не прошла валидацию, какие поля ты покажешь/скроешь? :)

У меня одно имя и дефолтного значения у него нет. Если говоришь про «кейс», то говори до конца :)
Конечно, если поле сложное (Field Collection типа Места учёбы с годами окончания, факультетами, курсами и т.д), то я не буду извращаться :)

я к тому, что не стоит городить велосипеды, если задуманное можно реализовать парой строчек непробиваемого drupal-way кода

danylevskyi
13.02.2013, 19:53

Оффтоп. Меня всегда бесил тот факт, что у строк, добавляемых этим методом, нет кнопки "удалить" из коробки.

Гость
14.02.2013, 10:31

Это еще 10 строк кода, нетрудно дописать

Гость
23.04.2014, 10:59

Как можно удалить (или скрыть) кнопку add если превышен лимит и затем ее снова добавить, если полей стало меньше?

а как реализовать в этом решении:
1. добавление нескольких полей за один клик?
2. с заполненными по умолчанию значениями?

из приведенного примера видно что количество новых элементов задается в сабмит колбеке

function example_add_more_form_add($form, &$form_state) {
  $form_state['name_count']++;

а вот значение по умолчанию можно указать в конструкторе формы.

 for ($i = 0; $i < $form_state['name_count']; $i++) {
    $form['names'][$i]['name'] = array(
      '#type' => 'textfield',
      '#title' => t('Name'),
    );
  }

Верно?

Гость
11.10.2014, 21:08

А что делать в таком случае с полями типа file? У меня они после каждого add more сбрасываются. Может я что-то не то делаю.

поля file не поддерживают возможности редактирования, пользуйтесь managed_file

Гость
09.12.2014, 12:03

Добрый день.
Есть вопрос (на гиде огра не нашел ответа).
Сделал по вашему гиду. Но потребовалось добавить на форму еще один филдсет с кнопкой add more. То есть на форме 2 кнопки и два обработчика динамического добавления набора полей.
Но проблема появилась. Если нажать на Add More первого набора полей, то подставляются поля второго набора полей. ID заменяемых дивов разные, да и обработчики (и наборы полей) разные.
В чем может быть проблема и что могли не учесть? Может какой либо индефикатор ajax выполнения?
Если на форме один обработчик, то все работает верно.. А если два - то подставляет не то, что требуется. Submit и callback верные и разные для обработчиков add more...

когда говорят "я сделал, но у меня не работает", то принято выкладывать код. я не телепат к сожалению

Гость
09.12.2014, 12:38

Вот сам код.
Набор полей 1 из формы

$form['fieldset1'] = array(
  '#type' => 'fieldset',
  '#title' => 'Fieldset 1',
);

$form['fieldset1']['fieldset1_tree'] = array(
  '#tree' => TRUE,
  '#prefix' => '<div id="fieldset1">',
  '#suffix' => '</div>', 
);

for ($i = 0; $i < $form_state['fieldset1_count']; $i++) {
  $form['fieldset1']['fieldset1_tree'][$i]['fieldset1_wrapper'] = array(
    '#tree' => TRUE,
    '#prefix' => '<div id="fieldset1-wrapper">',
    '#suffix' => '</div>',
    '#weight' => $i,
  );

  $form['fieldset1']['fieldset1_tree'][$i]['fieldset1_wrapper']['field1'] = array(
    '#title' => 'title 1',
    '#type' => 'textfield',
    '#default_value' => '',
  );

  $form['fieldset1']['fieldset1_tree'][$i]['fieldset1_wrapper']['field2'] = array(
    '#title' => 'title 2',
    '#type' => 'textfield',
    '#default_value' => '',
  );
}

$form['fieldset1']['fieldset1_addmore'] = array(
  '#type' => 'submit',
  '#value' => 'Добавить еще',
  '#submit' => array('fieldset1_addmore_form_add'),
  '#ajax' => array( 
    'wrapper' => 'fieldset1',
    'callback' => 'fieldset1_addmore_form_update',
  ),
);

Обработчик submit 1

function fieldset1_addmore_form_add($form, &$form_state) {
  $form_state['fieldset1_count']++;
  $form_state['rebuild'] = TRUE;
}

Обработчик коллбека на аякс 1

function fieldset1_addmore_form_update($form, &$form_state) {
  return $form['fieldset1']['fieldset1_tree'];
}
Гость
09.12.2014, 12:39

Набор полей 2 из формы

$form['fieldset2'] = array(
  '#type' => 'fieldset',
  '#title' => 'Fieldset 1',
);

$form['fieldset2']['fieldset2_tree'] = array(
  '#tree' => TRUE,
  '#prefix' => '<div id="fieldset2">',
  '#suffix' => '</div>', 
);

for ($i = 0; $i < $form_state['fieldset2_count']; $i++) {
  $form['fieldset2']['fieldset2_tree'][$i]['fieldset2_wrapper'] = array(
    '#tree' => TRUE,
    '#prefix' => '<div id="fieldset2-wrapper">',
    '#suffix' => '</div>',
    '#weight' => $i,
  );

  $form['fieldset2']['fieldset2_tree'][$i]['fieldset2_wrapper']['field1'] = array(
    '#title' => 'title 1',
    '#type' => 'textfield',
    '#default_value' => '',
  );

  $form['fieldset2']['fieldset2_tree'][$i]['fieldset2_wrapper']['field2'] = array(
    '#title' => 'title 2',
    '#type' => 'textfield',
    '#default_value' => '',
  );
}

$form['fieldset2']['fieldset2_addmore'] = array(
  '#type' => 'submit',
  '#value' => 'Добавить еще',
  '#submit' => array('fieldset2_addmore_form_add'),
  '#ajax' => array( 
    'wrapper' => 'fieldset2',
    'callback' => 'fieldset2_addmore_form_update',
  ),
);

Обработчик submit 2

function fieldset2_addmore_form_add($form, &$form_state) {
  $form_state['fieldset2_count']++;
  $form_state['rebuild'] = TRUE;
}

Обработчик коллбека на аякс 2

function fieldset2_addmore_form_update($form, &$form_state) {
  return $form['fieldset2']['fieldset2_tree'];
}

Так суть проблемы.
При клике на add more первого набора - обрабатывается набор fielset 2, а не fieldset 1
Когда в форме один обработчик из двух add more - все работает хорошо. Как только оба вместе - вот такая проблема.

Может быть у вас будут идеи...

по умолчанию drupal опознаёт нажатую кнопку по её #value, поскольку у вас две кнопки с одинаковым названием - выполняется последняя. Выход - прописать кнопкам уникальный #value или добавить уникальный параметр #name
https://www.drupal.org/node/2165351

Гость
09.12.2014, 13:07

Ох! Огромнейшее спасибо! Это помогло.
Еще раз спасибо!

Гость
09.05.2015, 13:08

Здравствуйте!
Отличный урок, огромное спасибо!

Не подскажите, как сделать добавление не одного поля, а fieldset'a с набором полей?
У меня получается так, что либо внутрь первого fieldset'a просто все поля добавляются, либо внутрь первого fieldset'a добавляется второй fieldset, и уже в нем все поля отображаются..

Гость
08.06.2015, 22:38

xandeadx подскажи плиз что может быть за проблема
drupal 6 вставляю 1 ссылку с ютуба сохраняю ноду все нормально, но как только пытаюсь добавить еще одно значение чтобы вставить 2 ссылку нажимаю на кнопку "Add another item" и все значения пропадают никаких ошибок в консоли нет.

Гость
22.01.2016, 18:55

Делаю подобное. Еще добавляю кнопку Delete, для удаления того что добавилось.
Если удаляешь последовательно и последовательно добавляешь то все работает правильно.
Но если добавлено например 3 раза и необходимо удалить средний елемент то после этого друпал теряет нумеровку елементов и неправильно выдает 'clicked element name' такое чувство что он кешырует форму.
Удаляю елемент после ребилда формы передотправкой по ajax.
Можете подсказать что нибудь?

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