3

I have an issue with a custom form in Drupal 8, using Ajax.

Let me explain. I have two select fields, and when you choose an option in the first one, an Ajax request will get options for the second one. When you submit the form, it will redirect you to a specific page. All of this works well.

The issue is when you get redirect to a page, and you hit the previous page button of your browser. By default, you get the first option of the first field, and the first option of the second field, but in this specific case, you get the option you previously selected for the first field, but the second select doesn't get the good values, as the Ajax request does not get fired.


Exemple :

When you choose "Option A" in the first field, you get "Value A.1" and "Value A.2" in the second field, if you choose "Option B", you get "Value B.1", etc...

For exemple, if you submit the form with "Option C" and "Value C.3", you get redirected to a page, and if you go back to the previous page in your browser, you'll have "Option C" selected, but with the values of "Option A" in the second field ("Value A.1", "Value A.2", ...), and if you submit, you get this error

An illegal choice has been detected. Please contact the site administrator.


I surely miss something with the rebuild of the form, but I can"t find any solutions.

Here's my code :

<?php

namespace Drupal\chdou_intuitive_search\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;

/**
 * Class DefaultForm.
 *
 * @package Drupal\chdou_intuitive_search\Form
 */
class DefaultForm extends FormBase {


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

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

    $form['i_wish'] = [
      '#type' => 'select',
      '#title' => $this->t('Je souhaite'),
      '#options' => [
        'appointment' => $this->t('prendre rendez-vous'),
        'doctor' => $this->t('rechercher un médecin'),
        'help' => $this->t('une aide ou un soutien')
      ],
      '#default_value' => 'appointment',
      '#required' => true,
      '#multiple' => false,
      '#size' => false,
      '#ajax' => [
        'callback' => '::getOptions',
        'event' => 'change',
        'wrapper' => 'what',
      ]
    ];

    $parent = $form_state->getValue('i_wish');
    if (empty($parent)) {
      $parent = $form['i_wish']['#default_value'];
    }
    $form['what'] = [
      '#prefix' => '<div id="what">',
      '#suffix' => '</div>',
      '#type' => 'select',
      '#title' => $whatLabel,
      '#options' => $this->getOptionsBySelection($parent),
      '#default_value' => key($this->getOptionsBySelection($parent)),
      '#required' => true,
      '#multiple' => false,
      '#size' => false,
    ];

    $form['submit'] = [
      '#prefix' => '<div class="form-actions">',
      '#suffix' => '</div>',
      '#type' => 'submit',
      '#value' => $this->t('Search'),
      '#attributes' => [
        'class' => [
          'button',
        ],
      ],
    ];

    return $form;

  }

  /**
   * Ajax callback for the "Je souhaite" dropdown
   */
  public function getOptions(array &$form, FormStateInterface $form_state) {
    return $form['what'];
  }

  /**
   * Returns list of options that correspond to the "Je souhaite" selected option
   */
  public function getOptionsBySelection($parent) {
    switch ($parent) {
      case 'appointment':
        $terms = $this->getAppointmentOptions();
        break;
      case 'doctor':
        $terms = $this->getDoctorOptions();
        break;
      case 'help':
        $terms = array(
          '140' => 'Je suis patient',
          '141' => 'Je suis aidant',
          '142' => 'Pour un proche',
        );
        break;
      default:
        break;
    }
    return $terms;
  }

  /**
   * Returns list of care_sheet_consultation taxonomy tree
   */
  public function getAppointmentOptions() {
    // ...
  }

  /**
   * Returns list of :
   *   - care_sheet_consultation
   *   - care_sheet_hospitalization
   *   - care_sheet_medical_techno
   * taxonomy trees
   */
  public function getDoctorOptions() {
    // ...
  }

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

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

    // CHOIX : Je souhaite prendre rendez-vous
    if ($form_state->getValue('i_wish') == 'appointment') {
      $form_state->setRedirect('entity.taxonomy_term.canonical', [
        'taxonomy_term' => $form_state->getValue('what')
      ]);
    }

    // CHOIX : Je souhaite rechercher un médecin
    else if ($form_state->getValue('i_wish') == 'doctor') {
      $form_state->setRedirect('entity.node.canonical', [
        'node' => '29', // i.e. page Annuaire
        'service' => $form_state->getValue('what')
      ]);
    }

    // CHOIX : Je souhaite une aide ou un soutien
    else if ($form_state->getValue('i_wish') == 'help') {
      $form_state->setRedirect('entity.node.canonical', [
        'node' => $form_state->getValue('what')
      ]);
    }

  }

}
1

1 Answer 1

0

Thank's to @ivan-jaros's link, I managed to make it work (partially).

I added a wrapper to the form.

$form['#prefix'] = '<div id="intuitive-search-ajax-wrapper">';
$form['#suffix'] = '</div>';

and in my callback function, I called the entire form instead of just the second field

public function getOptions(array &$form, FormStateInterface $form_state) {
    return $form;
}

It works in every browser, excepts Firefox. It may be because of the browser-side caching system, but Firefox does not reload the page when hitting the back button, so the form keep the same values. That causes the "illegal choice has been detected" error.

EDIT : I managed to make it work simply by adding '#validated' => true to my dynamic field. See this thread for more information.

Your Answer

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

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