0

I have a simple form where users can set the number of fields they need. Users may need 100+ fields so I am not using ajax "add more" options.

With the below code, everything works (Even though you have to save the form twice, once to add the fields, twice to save the data), but I get an Undefined offset notice Notice: Undefined offset: n anytime I increase the field count and save the form.

As I'm not sure a better way to ask this question...

1) I know the code below is not perfect UX (having to save a form twice), does Drupal offer any API advantages to my approach I am not aware of?

2) Is there a way to handle/suppress these notices?

public function buildForm(array $form, FormStateInterface $form_state) {

  $config = $this->config('MYMODULE.settings');

  $form['#tree'] = TRUE;

  $form['fieldset'] = [
    '#type' => 'fieldset',
  ];

  $form['fieldset']['field_number'] = [
    '#type' => 'textfield',
    '#default_value' => $config->get('field_number') ?: 1,
  ];

  for ($i = 0; $i < $config->get('field_number'); $i++) {

    $form['fieldset']['field'][$i] = [
      '#type' => 'textfield',
      '#default_value' => $config->get('field')[$i] ?: '',
    ];

  }

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

public function submitForm(array &$form, FormStateInterface $form_state) {
  $values = $form_state->getValues();

  $this->configFactory->getEditable('MYMODULE.settings')
    ->set('field_number', $values['fieldset']['field_number'])
    ->set('field', $values['fieldset']['field'])
    ->save();

  parent::submitForm($form, $form_state);

}
5
  • 1
    Which line is throwing the error? Also note that if you add form elements without Drupal #ajax, you will get an error. When using the form API, you MUST use #ajax to add/remove form fields. There is no other option.
    – Jaypan
    Commented Mar 19, 2019 at 2:52
  • The error comes from '#default_value' => $config->get('field')[$i]. So if you have 1 field and add 100, you get 100 notices of undefined offset. Not sure how to get an un-submitted form field value into $form_state as form fields don't have any type of '#onblur'or anything as far as I know. Hoping to add 100+/- fields in one click. Commented Mar 19, 2019 at 3:31
  • To avoid the error use isset(). For dynamic forms in general, try to not submit and save the values until the end. Rebuild the form instead, then check in buildForm() the value of field_number in $form_state and build the dynamic form elements accordingly.
    – 4uk4
    Commented Mar 19, 2019 at 13:30
  • 1
    See Examples, when you set $nojs in the ajax examples you get dynamic forms rebuilding without #ajax: cgit.drupalcode.org/examples/tree/ajax_example/src/Form
    – 4uk4
    Commented Mar 19, 2019 at 14:30
  • Maybe I need to ask another question. If I implement $form_state->setRebuild(TRUE) via a submit form element, I am not sure how to get the altered/active field value, not the [#default_value]. Commented Mar 19, 2019 at 16:06

1 Answer 1

1

My issue was from improper checking of array key values. Below is the approach for non-ajax form adjustment (which is very similar to ajax).

MYMODULE/src/Form.php:

/**
 * {@inheritdoc}
 */
public function buildForm(array $form, FormStateInterface $form_state) {

  // get form config.
  $config = $this->config('MYMODULE.settings');

  // Set our count. Used in for loop below.
  if (!empty($form_state->getValue(['fieldset', 'field_count']))) {
    // get $form_state count value.
    $defaultCount = $form_state->getValue(['fieldset', 'field_count']);
  }
  else {
    // Get config count value.
    $defaultCount = $config->get('field_count');
  }

  $form['#tree'] = TRUE;

  // Fieldset item.
  $form['fieldset'] = [
    '#type' => 'fieldset',
    '#prefix' => '<div id="fieldset-wrapper">',
    '#suffix' => '</div>',
  ];

  // Field count item.
  $form['fieldset']['field_count'] = [
    '#type' => 'number',
    '#title' => 'Number',
    '#required' => TRUE,
    '#default_value' => $config->get('field_count') ?: 1,
  ];

  // Rebuild submit item.
  $form['fieldset']['rebuild'] = [
    '#type' => 'submit',
    '#value' => 'Apply number',
    // Make our call to the rebuild function.
    '#submit' => ['::rebuildFormSubmit'],
  ];

  // Loop through our count and create form items.
  for ($i = 0; $i < $defaultCount; $i++) {

    $form['fieldset']['field'][$i] = [
      '#type' => 'textfield',
      // This is important. Must check isset() to avoid Undefined index notices.
      '#default_value' => isset($config->get('field')[$i]) ? $config->get('field')[$i] : '',
    ];

  }

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

}

/**
 * {@inheritdoc}
 */
public function rebuildFormSubmit(array &$form, FormStateInterface $form_state) {
  $form_state->setRebuild(TRUE);
}

Related question: Get an Un-submitted/Active Form Field Value in $form_state?.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.