xandeadx.ru Блог музицирующего веб-девелопера

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

Опубликовано в

Пример реализации формы с кнопкой 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
Похожие записи

Комментарии RSS

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

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

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

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

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

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

эх, где бы найти эти 10 строк...

Хорошая статья спасибо

Как можно удалить (или скрыть) кнопку 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'),
    );
  }

Верно?

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

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

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

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

Вот сам код.
Набор полей 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'];
}

Набор полей 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

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

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

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

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

не сталкивался

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

Оставить комментарий

Содержимое этого поля является приватным и не будет отображаться публично. Если у вас есть аккаунт в Gravatar, привязанный к этому e-mail адресу, то он будет использован для отображения аватара.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Доступные HTML теги: <a> <i> <b> <strong> <code> <ul> <ol> <li> <blockquote> <em> <s>
  • Строки и параграфы переносятся автоматически.
  • Подсветка кода осуществляется с помощью тегов: <code>, <css>, <html>, <ini>, <javascript>, <sql>, <php>. Поддерживаемые стили выделения кода: <foo>, [foo].

Подробнее о форматировании