2

Hy,

I'm trying to create a form in which you can fill in user names. I want to dynamically add new textfields by pressing a button. I based my code on the following question (because it also uses drupal 8 and supposedly should work).

However, unfortunately it doesn't add the fields. The code I'm using currently looks like this:

<?php
    /**
     * @file
     * Contains \Drupal\bulk_email\Form\EmailForm.
     */

    namespace Drupal\bulk_email\Form;
    use Drupal\Core\Form\FormBase;
    use Drupal\Core\Form\FormStateInterface;
    use Drupal\Component\Utility\UrlHelper;

    class EmailForm extends FormBase {
        public function getFormId() {
            return "bulk_email_form";
        }

        public function buildForm(array $form, FormStateInterface $form_state) {
            $form['#tree'] = TRUE;

            $form['users'] = array(
              '#type' => 'fieldset',
              '#title' => t('Users'),
              '#prefix' => '<div id="users-wrapper">',
              '#suffix' => '</div>',
            );

            $num_users = $form_state->getValue('num_users');
            if (empty($num_users)) {
                $num_users = 1;
            }
            for ($i = 0; $i < $num_users; $i++) {
                $form['users'][$i] = array(
                    '#type'  => 'textfield',
                    '#description' => t('Select a user.'),
                );
            }

            $form['users']['users_more'] = array(
                '#type' => 'submit',
                '#value' => t('Add one'),
                '#submit' => array($this, 'bulk_email_addfieldsubmit'),
                '#ajax' => array(
                     'callback' => array($this, 'bulk_email_add_more_callback'),
                     'wrapper' => 'users-wrapper',
                ),
            );

            $form['actions'] = array('#type' => 'actions');
            $form['submit'] = array(
                '#type' => 'submit',
                '#value' => t('Send'),
            );
            return $form;
        }

        public function submitForm(array &$form, FormStateInterface $form_state) {
            // submit code
        }

        public function bulk_email_addfieldsubmit(array &$form, FormStateInterface $form_state) {
            $num_users = $form_state->get('num_users') + 1;
            $form_state->set('num_users', $num_users);
            $form_state->setRebuild(TRUE);
        }

        public function bulk_email_add_more_callback(array &$form, FormStateInterface $form_state) {
            return $form['users'];
        }
    }
?>

I'm most likely missing a small detail, but I can't seem to find it... Any help is appreciated.

Kind regards.

1

Did you check if the submit is called in your ajax request?

public function bulk_email_addfieldsubmit(array &$form, FormStateInterface $form_state) {
    $num_users = $form_state->get('num_users') + 1;
    $form_state->set('num_users', $num_users);

If not, you can put this code in buildForm(). In this case you need to check if the triggering element is the correct one.

And another small issue, this line in buildForm():

$num_users = $form_state->getValue('num_users');

Should be get('num_users'), because getValue() is for user input.

  • I used xdebug. I found that the ajax callback indeed isn't called (however I do not know why). However moving the code to the buildForm() function did not help. I found that the form_state variable never contained the value 'num_users' eventhough it was set before. calling the function 'set' should be sufficient, right? – Matthias Verstraete Sep 2 '16 at 18:09
  • No, not always. This is a complicated issue, because the empty form gets cached. You can try a value form element in $form to store num_users. That also solves the issue in buildForm(), you can keep getValue(). – 4k4 Sep 2 '16 at 18:21
  • Are you suggesting to store and get the num_users value via 'getValue' and 'setValue'? If so, I just tried that and that didn't work either. Again the value is never present in the form_state. – Matthias Verstraete Sep 2 '16 at 19:04
  • Store form element '#type' => 'value' in $form. Then getValue() the same way as any other form element. – 4k4 Sep 2 '16 at 19:15
  • That unfortunately did not help either. Here is my current code. I did notice that inside the buildForm function nothing is present in the form_state (meaning that all values retrieved via 'getValue' are null, even the ones that are filled in). – Matthias Verstraete Sep 2 '16 at 19:39
1

you can use something like this.

if (!empty($form_state['values']['field_name'])) {
    $form[]=array();//add your form elements to be shown after submit.
}
else {
    $form[]=array();//here your original form.
}
return $form;

in your form submit add

$form_State['rebuild']=true;
1

I did not find any solution to my problem and start to wonder whether it' a bad design decision or a bug.

Anyway, for other users, I used the following workaround:

First of all I removed all the code on adding these extra field. I only kept the actual text field where you enter the username. Then I added a new hidden field called user-collection.

The rest was done by adding a custom javascript. This script would detect wheteher a username has been entered (based on the autocomplete). It would then add ";" to the value of user-collection. It then clears the textfield. The result is a user-collection field, containing all the usernames entered. I finally also added a visual feedback which showed the content of user-collection (after some parsing).

It's only a work around and took me quite some effort as well, but it's better than nothing.

1

I might be wrong but you can also try changing the ajax callback to, assuming bulk_email_add_more_callback is not actually called.

'#ajax' => array(
                 'callback' => '::bulk_email_add_more_callback',
                 'wrapper' => 'users-wrapper',
            ),

And then in the actual callback get your values from the completeform. This was my problem, all values where correct but then I returned the old values.

public function bulk_email_add_more_callback(array &$form, FormStateInterface $form_state) {
        $form = $form_state->getCompleteForm()
        return $form['users'];
    }
1

i also have the same scenario. i can add and remove the textfields dynamically sucessfully using ajax in drupal 8. i attached the code below . actually i think you are following the examples fapi module and using add more form right?

there is some issue in fapi add more form in drupal 8. and patch is also there. so my self corrected the code for my scenario.

First step : we are working behind the logic of formstate variables . the value of formstate variables we may add or subtract the fields dynamically .

you need to set $form['#tree'] = TRUE; property when initial build form function.then only you can get proper submitted values in submit function.

 $veritas_id = $form_state->get('veritas_id');
if ($veritas_id === NULL) {
  $veritas_id = $form_state->set('veritas_id', 0);
  $veritas_id = 0;
  }

second step

$form['veritasIds'] = [
  '#type' => 'fieldset',
  //'#title' => $this->t('#ID'),
  '#prefix' => '<div id="veritas-id-wrapper">',
  '#suffix' => '</div>',
  ];



  $form['veritasIds']['actions']['add_name'] = [
  '#type' => 'submit',
  '#limit_validation_errors' => array(),
  '#value' => t('add row'),
  //'#name'=>$veritas,
  '#submit' => array('::addOneVeritas'),
  '#ajax' => [
  'callback' => '::addmoreCallbackVeritas',
  'wrapper' => 'veritas-id-wrapper',
  ],

  '#attributes' => array('class'=> array('btn-transparent')),
  ];

  //if ($num_names > 1) {

  $form['veritasIds']['actions']['remove_name'] = [
  '#type' => 'submit',
  '#limit_validation_errors' => array(),
  '#value' => t('delete row'),
  '#submit' => array('::removeoneVeritas'),
  '#ajax' => [
    'callback' => '::addmoreCallbackVeritas',
    'wrapper' => 'veritas-id-wrapper',
  ],
  '#name'=>t('veritasremove'),

  '#attributes' => array('class'=> array('btn-transparent')),
  '#suffix' => t('<div class="id-wrapper">#ID</div>'),
  ];

  for ($i = 0; $i < $veritas_id; $i++) {
  $form['veritasIds']['id'][$i] = [
  '#type' => 'textfield',
  '#attributes' => array('class'=> array('veritas-addfield')),
  //'#title' => t('Name'),
  ];
  }

here am created a fieldset . inside the fieldset only add row more and delete more button and added dynamic fields will be there.

third step $form_state->setCached(FALSE); is needed submit and ajax callback functions will be

public function addmoreCallbackVeritas(array &$form, FormStateInterface $form_state) {
  $veritas_id = $form_state->get('veritas_id');
  return $form['veritasIds'];

}

public function addOneVeritas(array &$form, FormStateInterface $form_state) {
    $veritas_id = $form_state->get('veritas_id');
    $add_button1 = $veritas_id + 1;
    $form_state->set('veritas_id', $add_button1);
    $form_state->setRebuild();
  }

feel free to ask any doubts in this

Your Answer

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

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