0

Using examples module as a reference guide, I'm able to implement the AjaxAddMore functionality into one of my configuration forms, but somehow when I'm trying to achieve the same logic by further enhancing the form with some nested fields it's not working as expected.

Following is the code for the same:

<?php

namespace Drupal\custom_config\Form;

use Drupal\Core\Form\FormStateInterface;

/**
 * Class MyForm.
 *
 * @package Drupal\custom_config\Form
 */
class MyForm extends ConfigFilesFormBase {

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames() {
    return [
      'custom_config.form',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'my_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('custom_config.form');

    $form['information'] = [
      '#type' => 'vertical_tabs',
      '#default_tab' => 'outer_group',
    ];

    // outer_group.
    $outer_group_items = $this->getMultipleFields($form_state, 'outer_group');
    $form_state_outer_group_count = $form_state->get('outer_group_count');
    $outer_group_count = isset($form_state_outer_group_count) ? $form_state_outer_group_count : [1];

    $form['outer_group'] = [
      '#type' => 'details',
      '#title' => $this->t('Outer Group'),
      '#group' => 'information',
      '#tree' => TRUE,
    ];
    $form['outer_group']['fieldset'] = [
      '#type' => 'fieldset',
      '#prefix' => '<div id="outer-group-fieldset-wrapper">',
      '#suffix' => '</div>',
    ];

    foreach ($outer_group_count as $key => $value) {
      $form['outer_group']['fieldset'][$key] = [
        '#type' => 'fieldset',
        '#title' => $this->t('Outer Group'),
      ];
      $form['outer_group']['fieldset'][$key]['title'] = [
        '#type' => 'textfield',
        '#title' => $this->t('Outer Group Title'),
        '#default_value' => $outer_group_items['fieldset'][$key]['title'],
      ];
      $form['outer_group']['fieldset'][$key]['help'] = [
        '#markup' => $this->t('Some text!'),
      ];

      // inner_group.
      $form_state_inner_group_count = $form_state->getValue("outer_group.fieldset.$key.inner_group.fieldset");
      $inner_group_count = isset($form_state_inner_group_count) ? $form_state_inner_group_count : [1];

      // We have to ensure that there is at least one field.
      // Value for default.
      if ($form_state_inner_group_count === NULL && $config->get("outer_group.fieldset.$key.inner_group.fieldset") === NULL) {
        $form_state_inner_group_count = 1;
        $form_state->set("outer_group.fieldset.$key.inner_group.inner_group_count", range(1, $form_state_inner_group_count));
      }
      // If has configuration save but not in form state.
      elseif ($config->get("outer_group.fieldset.$key.inner_group.fieldset") && $form_state_inner_group_count != NULL) {
        $form_state->set("outer_group.fieldset.$key.inner_group.inner_group_count", $form_state_inner_group_count);
      }

      $form['outer_group']['fieldset'][$key]['inner_group']['fieldset'] = [
        '#type' => 'fieldset',
        '#prefix' => '<div id="inner-group-fieldset-wrapper">',
        '#suffix' => '</div>',
      ];

      $inner_group_array_key = [];

      foreach ($inner_group_count as $k => $v) {
        $form['outer_group']['fieldset'][$key]['inner_group']['fieldset'][$k] = [
          '#type' => 'fieldset',
          '#title' => $this->t('Inner Group'),
        ];
        $form['outer_group']['fieldset'][$key]['inner_group']['fieldset'][$k]['id'] = [
          '#type' => 'textfield',
          '#title' => $this->t('Inner Group ID'),
          '#default_value' => $outer_group_items['fieldset'][$key]['inner_group']['fieldset'][$k]['id'],
          '#description' => $this->t('Inner Group Description'),
        ];
        $form['outer_group']['fieldset'][$key]['inner_group']['fieldset'][$k]['title'] = [
          '#type' => 'textfield',
          '#title' => $this->t('Inner Group Title'),
          '#default_value' => $outer_group_items['fieldset'][$key]['inner_group']['fieldset'][$k]['title'],
        ];
        $form['outer_group']['fieldset'][$key]['inner_group']['fieldset'][$k]['actions']['remove'] = [
          '#type' => 'submit',
          '#name' => 'delete_item_' . $k,
          '#value' => t('Delete Inner Group'),
          '#submit' => ['::removeOneInnerGroup'],
          '#ajax' => [
            'callback' => '::innerGroupCallbackWrapper',
            'wrapper' => 'inner-group-fieldset-wrapper',
          ],
        ];
        $inner_group_array_key[] = $v;
      }

      $form['outer_group']['fieldset'][$key]['inner_group']['fieldset']['actions'] = [
        '#type' => 'actions',
      ];
      $form['outer_group']['fieldset'][$key]['inner_group']['fieldset']['actions']['add'] = [
        '#type' => 'submit',
        '#value' => t('Add Inner Group'),
        '#submit' => ['::addOneInnerGroup'],
        '#ajax' => [
          'callback' => '::innerGroupCallbackWrapper',
          'wrapper' => 'inner-group-fieldset-wrapper',
        ],
      ];
      $form['outer_group']['fieldset'][$key]['inner_group']['inner_group_count'] = array(
        '#type' => 'hidden',
        '#value' => range(1, $inner_group_array_key),
      );
      $form['outer_group']['fieldset'][$key]['actions']['remove'] = [
        '#type' => 'submit',
        '#name' => 'delete_item_' . $key,
        '#value' => t('Delete Outer Group'),
        '#submit' => ['::removeOneOuterGroup'],
        '#ajax' => [
          'callback' => '::outerGroupCallbackWrapper',
          'wrapper' => 'outer-group-fieldset-wrapper',
        ],
      ];
    }

    $form['outer_group']['fieldset']['actions'] = [
      '#type' => 'actions',
    ];
    $form['outer_group']['fieldset']['actions']['add'] = [
      '#type' => 'submit',
      '#value' => t('Add Outer Group'),
      '#submit' => ['::addOneOuterGroup'],
      '#ajax' => [
        'callback' => '::outerGroupCallbackWrapper',
        'wrapper' => 'outer-group-fieldset-wrapper',
      ],
    ];

    return parent::buildForm($form, $form_state);
  }

  /**
   * Gather the fields which are already present in the form.
   *
   * @param array $form_state
   *   The current state of the form.
   * @param string $field
   *   The name of the field.
   *
   * @return mixed
   *   Form definition.
   */
  public function getMultipleFields($form_state, $field) {
    $config = $this->config('custom_config.form');
    // Gather the number of fields in the form already.
    $field_count = $form_state->get($field . '_count');
    // We have to ensure that there is at least one field.
    // Value for default.
    if ($field_count === NULL && $config->get($field) === NULL) {
      $field_count = 1;
      $form_state->set($field . '_count', range(1, $field_count));
      return $items = $config->get($field);
    }
    // If has configuration save but not in form state.
    elseif ($config->get($field . '.fieldset') && $field_count == NULL) {
      $field_count = $config->get($field . '_count');
      if (!is_array($field_count)) {
        $field_count = range(1, $field_count);
        $field_count = array_flip($field_count);
      }
      $form_state->set($field . '_count', $field_count);
      return $items = $config->get($field);
    }
    else {
      return $items = $form_state->get('not_deleted_items');
    }
  }

  /**
   * For more reference: https://drupal.stackexchange.com/a/245484/3808.
   */

  /**
   * Callback for both ajax-enabled buttons.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @return mixed
   *   Selects & returns the fieldset with the outer_group in it.
   */
  public function outerGroupCallbackWrapper(array &$form, FormStateInterface $form_state) {
    return $form['outer_group']['fieldset'];
  }

  /**
   * Callback for both ajax-enabled buttons.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @return mixed
   *   Selects & returns the fieldset with the inner_group in it.
   */
  public function innerGroupCallbackWrapper(array &$form, FormStateInterface $form_state) {
    $outer_group = $form_state->getValue('outer_group');
    foreach ($outer_group['fieldset'] as $key => $value) {
      if (is_numeric($key)) {
        foreach ($value['inner_group']['fieldset'] as $k => $v) {
          if (is_numeric($k)) {
            return $form['outer_group']['fieldset'][$v]['inner_group']['fieldset'];
          }
        }
      }
    }
  }

  /**
   * Submit handler for Add More. Increments the max counter & causes a rebuild.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public function addOneOuterGroup(array &$form, FormStateInterface $form_state) {
    $outer_group_count = $form_state->get('outer_group_count');
    $last_item = end($outer_group_count) + 1;
    array_push($outer_group_count, $last_item);
    $form_state->set('outer_group_count', $outer_group_count);
    $form_state->setRebuild();
  }

  /**
   * Submit handler for Add More. Increments the max counter & causes a rebuild.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public function addOneInnerGroup(array &$form, FormStateInterface $form_state) {
    $outer_group = $form_state->getValue('outer_group');
    foreach ($outer_group['fieldset'] as $key => $value) {
      if (is_numeric($key)) {
        $inner_group_count = $value['inner_group']['inner_group_count'];
        $inner_group_count++;
        $form_state->set("outer_group.fieldset.$key.inner_group.inner_group_count", $inner_group_count);
      }
    }
    $form_state->setRebuild();
  }

  /**
   * Submit handler for Remove One. Decrements the max counter & causes rebuild.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public function removeOneOuterGroup(array &$form, FormStateInterface $form_state) {
    // Get the triggering item.
    $element_id = isset($form_state->getTriggeringElement()['#parents']) ? $form_state->getTriggeringElement()['#parents'][2] : '';
    if (isset($element_id)) {
      $config_factory = \Drupal::inner_group('config.factory')
        ->getEditable('custom_config.form');
      if ($form_state->get('outer_group_count') !== NULL) {
        $outer_group_count = $form_state->get('outer_group_count');
      }
      else {
        $outer_group_count = $this->config('custom_config.form')->get('outer_group_count');
      }
      $outer_group = $config_factory->get('outer_group');
      unset($outer_group_count[$element_id]);
      $form_state->set('outer_group_count', $outer_group_count);
      unset($outer_group['fieldset'][$element_id]);
      $config_factory->set('outer_group', $outer_group);
      $config_factory->save();
    }
    // Rebuild the form.
    $form_state->setRebuild();
  }

  /**
   * Submit handler for Remove One. Decrements the max counter & causes rebuild.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public function removeOneInnerGroup(array &$form, FormStateInterface $form_state) {
    // Get the triggering item.
    $element_id = isset($form_state->getTriggeringElement()['#parents']) ? $form_state->getTriggeringElement()['#parents'][2] : '';
    $outer_group   = $form_state->getValue('outer_group');
    if (isset($element_id)) {
      foreach ($outer_group['fieldset'] as $key => $value) {
        if (is_numeric($key)) {
          if ($form_state->get("outer_group.fieldset.$key.inner_group.inner_group_count") !== NULL) {
            $inner_group_count = $value['inner_group']['inner_group_count'];
          }
          else {
            $inner_group_count = $this->config('custom_config.form')->get("outer_group.fieldset.$key.inner_group.inner_group_count");
          }
        }
      }
      $config_factory = \Drupal::inner_group('config.factory')->getEditable('custom_config.form');
      $inner_group = $config_factory->get("outer_group.fieldset.$key.inner_group.inner_group_count");
      unset($inner_group_count[$element_id]);
      $form_state->set("outer_group.fieldset.$key.inner_group.inner_group_count", $inner_group_count);
      unset($inner_group['fieldset'][$element_id]);
      $config_factory->set('inner_group', $inner_group);
      $config_factory->save();
    }
    // Rebuild the form.
    $form_state->setRebuild();
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    parent::submitForm($form, $form_state);
    $this->config('custom_config.form')->set('outer_group_count', $form_state->get('outer_group_count'))->save();
    $this->config('custom_config.form')->set('outer_group', $form_state->getValue('outer_group'))->save();
  }

}

After a lot of debugging, it seems that there's an issue with innerGroupCallbackWrapper but not sure what exactly.

N.B: AjaxAddMore for OuterGroup is working as expected.

Any guidance will be appreciated!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.