Score:1

Problem with Adding new fields with Ajax call to a custom form

vu flag

I have a simple form where I am trying to add a couple of fields to it when the user clicks a button using an ajax callback. Here is what I have:

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

    $form['add_fields'] = [
      '#type' => 'button',
      '#value' => 'Add new fields',
      '#ajax' => [
        'callback' => [$this, 'ajax_callback'],
        'disable-refocus' => FALSE, 
        'event' => 'click',
        'wrapper' => 'new-fields-wrapper',
        'progress' => [
          'type' => 'throbber',
          'message' => $this->t('Verifying entry...'),
        ],
      ],
    ];

    // Wrapper for new fields
    $form['new_fields_wrapper'] = [
      '#type' => 'container',
      '#attributes' => ['id' => 'new-fields-wrapper'],
    ];

    return $form;
  }

As you see, it's a very simple form and I am defining a wrapper element inside where I would like to add the new fields.

Here is my ajax callback function where I am basically adding 3 new fields:

 public function ajax_callback($form, FormStateInterface $form_state)
  {

    $form['new_fields_wrapper']['new_field_1'] = [
      '#type' => 'textfield',
      '#title' => 'New Field 1',
    ];
    $form['new_fields_wrapper']['new_field_2'] = [
      '#type' => 'select',    '
      #title' => 'New Field 2',
      '#options' => [1 => 'Option 1', 2 => 'Option 2'],
    ];
    $form['new_fields_wrapper']['new_field_3'] = [
      '#type' => 'checkbox',
      '#title' => 'New Field 3',
    ];

    return $form['new_fields_wrapper'];
  }

And finally I have a submit form function where I'd like to get the values entered for the new fields:

public function blockSubmit($form, FormStateInterface $form_state)
  {
    $values = $form_state->getValues();
  }

I have two problems here:

  1. When I click on the button to add the new fields through the ajax callback, it does not add the fields. I made sure that the callback function is actually called by adding some logging. However, it doesn't add the new fields. As I explored with the devtools, I realized that when the form is displayed, the wrapper element does not exist in the DOM. I added a hidden field to it like this: $form['new_fields_wrapper']['hidden'] = [ '#type' => 'hidden', '#value' => 0, ];

This made the wrapper appear in the DOM and now the button click works and the fields are added. Is this how I am supposed to handle it? In all my research, there was no mention of having to include a field in the wrapper in the original form.

  1. With the new fields added, when I enter values in them and hit the form submit button, the new field values are not included in the $form_state->getValues(). How can I get the values entered in the new fields with the form submit?

Thanks a lot in advance for any help.

Score:1
de flag

You cannot add form elements in ajax callbacks. Drupal caches the form before the callback, so if you add elements in the callback, they are not part of the cache, and any values submitted by those elements is disregarded as a security measure.

The solution is to put these fields in your form definition:

public function buildForm($form, FormStateInterface $form_state)
  ...
  if (!empty($form_state->getValues()) {
    $form['new_fields_wrapper']['new_field_1'] = [
      '#type' => 'textfield',
      '#title' => 'New Field 1',
    ];
    $form['new_fields_wrapper']['new_field_2'] = [
      '#type' => 'select',    '
      #title' => 'New Field 2',
      '#options' => [1 => 'Option 1', 2 => 'Option 2'],
    ];
    $form['new_fields_wrapper']['new_field_3'] = [
      '#type' => 'checkbox',
      '#title' => 'New Field 3',
    ];
  }
  ...
}
Akbar Bakhshi avatar
vu flag
Thanks Jaypan for the reply. So, the reason for me to do it this way was that I don't know if the user wants the field or if they do, how many of it they want. The example I provided here is just a Minimal code. In real life, I want to let the users click on the button if they need to add a url in a textfield. Some users may need only 2 urls, but others may want 8 or so on. I am guessing it is not feasible currently based on your answer?
Jaypan avatar
de flag
It's feasible, but you have to put all the logic in the form definition, not the ajax callback. The reason I put `if (!empty($form_state->getValues())` is because the value will be empty when the form is first built, and will have values when the ajax is submitted, so those fields will be added when the form is built with #ajax. You can add additional logic as necessary.
I sit in a Tesla and translated this thread with Ai:

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.