1

I am trying to insert a custom form into a page in my Drupal 8 site. This form has 2 fields which need to accept unlimited values (one textfield, one fieldset).

In my current implementation, I have declared this form as a class, defined in the src folder of a module, and my .module file adds this form to the $build array via hook_node_view_alter():

$build['myform'] = \Drupal::formBuilder()->getForm('Drupal\my_module_hook\Form\MyForm');

My class constructs the form using public function buildForm(array $form, FormStateInterface $form_state). I am not sure how to set up a field such that it can accept unlimited values.

I'm aware that tutorials and answers on this topic using Ajax exist such as this and this, but all such examples are using $form_state as an array, which breaks my code as $form_state is an object.

Is building this form as a class the wrong way to do it? If not, how exactly do I accomplish what I'm trying to do without using $form_state as an array? If so, how can I insert my form into the page otherwise? I'm not understanding how other answers and tutorials on the subject are accomplishing this since these functions are usually shown out of context.

  • 1
    $form_state as array means those examples are using Drupal 7 where it was an array. Conceptually, it is still example the same, instead of $form_state['something'], you simply use $form_state->get() and ->set(). – Berdir Sep 9 '16 at 17:59
  • 1
    This change record helped me a lot: drupal.org/node/2310411 – 4k4 Sep 9 '16 at 18:31
  • Thanks guys, that helped me with my array issues. I am following this simple tutorial: rapiddg.com/blog/… which is for Drupal 7 but I'm accessing the form as appropriate in Drupal 8. The ajax function executes, but no field is added. I have a hunch it's because I can only use $form_state as a function argument and not &$form_state. Is this worth its own thread do you think? – saramm1 Sep 9 '16 at 19:19
  • @saramm1 take a look at the examples (click the drop down and select the 8.x-1.x) and enable fapi example and take a look at the ajax example there. Then take a look at the source code to see how they did it. – No Sssweat Sep 9 '16 at 20:09
  • Thanks @NoSssweat, I'm following this example but I can't get the ajax function to fire at all no matter what I do. I added a ddl($form) to my callback function and my Devel debug report is still empty. – saramm1 Sep 12 '16 at 15:39
1

I have set up a module demonstrating how to allow the user to add unlimited values: https://bitbucket.org/scotteuser/drupal-8-demo-form-unlimited-values

Here is the Form itself:

<?php

namespace Drupal\demo_form_unlimited_values\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Class UnlimitedValuesForm.
 *
 * @package Drupal\demo_form_unlimited_values\Form
 */
class UnlimitedValuesForm extends FormBase {


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

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

    // State that the form needs to allow for a hierarchy (ie, multiple
    // names with our names key).
    $form['#tree'] = TRUE;

    // Initial number of names.
    if (!$form_state->get('num_names')) {
      $form_state->set('num_names', 1);
    }

    // Container for our repeating fields.
    $form['names'] = [
      '#type' => 'container',
    ];

    // Add our names fields.
    for ($x = 0; $x < $form_state->get('num_names'); $x++) {
      $form['names'][$x]['first_name'] = [
        '#type' => 'textfield',
        '#title' => $this->t('First name @num', ['@num' => ($x + 1)]),
      ];

      $form['names'][$x]['last_name'] = [
        '#type' => 'textfield',
        '#title' => $this->t('Last name @num', ['@num' => ($x + 1)]),
      ];
    }

    // Button to add more names.
    $form['addname'] = [
      '#type' => 'submit',
      '#value' => $this->t('Add another name'),
    ];

    // Submit button.
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];

    return $form;
  }

  /**
    * {@inheritdoc}
    */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    parent::validateForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $values = $form_state->getValues();

    // Decide what action to take based on which button the user clicked.
    switch ($values['op']) {
      case 'Add another name':
        $this->addNewFields($form, $form_state);
        break;

      default:
        $this->finalSubmit($form, $form_state);
    }
  }

  /**
   * Handle adding new.
   */
  private function addNewFields(array &$form, FormStateInterface $form_state) {

    // Add 1 to the number of names.
    $num_names = $form_state->get('num_names');
    $form_state->set('num_names', ($num_names + 1));

    // Rebuild the form.
    $form_state->setRebuild();
  }

  /**
   * Handle submit.
   */
  private function finalSubmit(array &$form, FormStateInterface $form_state) {
    drupal_set_message($this->t('Do what you want with the final data here'), 'status');
  }

}

The critical bits are:

  • $form[#tree] tells Drupal that the form is hierarchical.
  • $form_state->get() and $form_state->set() are used around the counter num_names
  • Check the 'op' to see which button the user pressed. If they pressed to add another field, use $form_state->set() to increase the count and $form_state->setRebuild(); to tell Drupal that the form should be rebuilt.

Hope that helps you get it working!

  • Thanks Scott, this did work for me, but I would like my form to use Ajax so that it is not reloading the page. The user will likely have to add a field about 10 times on average. Upvoting because this is still a good solution that is pointing me in the right direction. – saramm1 Sep 12 '16 at 15:38
  • In fact I will accept your answer because it works, and I didn't specify that I wanted Ajax and that implementation is another problem entirely. – saramm1 Sep 12 '16 at 17:23

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.