Score:0

FormAPI - Values of AJAX added fields missing from values array on submission

vn flag

I'm using the Form API to create a form where I have multiple instances of a fieldset. Each fieldset contains the same fields, one for storing an email address and another for another bit of information. But essentially they're both textfields that use autocomplete which works fine. But I'm having an issue in the submission handler, described underneath my code below.

$form['authors_container']['authors_fieldset'][$i] = [
    '#type' => 'fieldset',
    '#attributes' => [
      'class' => ['author-fieldset']
    ],
    'email' => [
      '#type' => 'textfield',
      '#title' => 'Author email',
      '#value' => $emailValue,
      '#autocomplete_route_name' => 'discussion_papers.contact_email_lookup',
      '#attributes' => [
        'class' => ['author-email']
      ]
    ],
    'affiliation' => [
      '#type' => 'textfield',
      '#title' => 'Affiliation',
      '#value' => $affiliationValue,
      '#attributes' => [
        'class' => ['author-affiliation']
      ]
    ]
  ];

I then have an AJAX button for adding more instances of the above fieldset with its corresponding handler:

$form['authors_container']['add_author'] = [
  '#type' => 'submit',
  '#value' => 'Add another author',
  '#submit' => ['::AddAuthor'],
  '#limit_validation_errors' => [],
  '#ajax' => [
    'callback' => '::getAuthorsFields',
    'wrapper' => 'authors-container',
    'method' => 'replace',
    'disable-refocus' => true,
  ]
];

public function addAuthor($form, FormStateInterface &$form_state) {

    $storage = $form_state->getStorage();

    if (empty($storage['numOfAuthors'])) 
        $storage['numOfAuthors'] = 1;

    $storage['numOfAuthors']++;
    $form_state->setStorage($storage);

    $form_state->setLimitValidationErrors([]);
    $form_state->setRebuild();
}

All this works fine, I can add as many instance of the fieldset as I need. My issue is that when I submit the form, the values of only the very last fieldset are always empty in the $form_state['values'] array, yet I can see the values in the $form_state['input'] array. I understand that the input array is raw, unvalidated input and that the values array is validated, so my question is why are only the last set of textfields not validated? My validation handler doesn't remove any values.

Score:0
de flag

Two possibilities:

  1. You have added the additional fields in the #ajax callback function. As you didn't show which function your fields were defined in, this cannot be seen from your code. Drupal caches the form when building it, then when the form is submitted, it will strip any values not part of the original form definition for security purposes. The problem with adding fields in an #ajax callback function is that the form is cached previous to the callback function, and therefore Drupal discards the submitted values of the fields.

  2. If the form element declarations are in fact within the form builder function and not part of an #ajax callback function, you probably need to add #tree to the fieldset, so as to create an array of values:

    $form['authors_container']['authors_fieldset'][$i] = [
      '#tree' => TRUE,
      '#type' => 'fieldset',
      ...
    

    This will save each of the submitted values to a separate element of the values array.

AlbionBrown avatar
vn flag
Thanks for the reply, I'll try to edit my question to include more code. The additional fields are only added in the buildForm method, the AJAX callback only increases the number that should show and then on the form rebuild, it adds however many are stored in the form state storage. I've tried adding the tree key and also disabling cache but neither helped. The tree key is also used in the container parent of the fieldsets. I think I need to debug right from the beginning of the submission request to find out why Drupal removes them.
Jaypan avatar
de flag
If you're changing values in the form state in an ajax callback, they are lost. The form state has already been cached prior to the ajax callback. You will need to do your incrementation in a submit handler, not an ajax callback, if you want it to persist. Ajax callbacks are _only_ for determining which elements to return, not for "doing" anything to the data.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.