First of all thanks for a great module!

Now to my question...

I'd like to know if the following is possible and what steps/modules/custom code are required to implement it if it is.

Say, I have a select list of entity references to Category (taxonomy terms or whatever) and another select list of Subcategories which is dependent on the first select (invisible until some Category is selected) and also dependent on the value selected there (via views filter).

I guess the view should receive an argument (selected Category) and the second list (Subcategory) has to be dynamically updated. I believe it's a fairly common scenario (Make - Model etc.) but I couldn't find any examples or guides so far.

Comments

scoff created an issue. See original summary.

jrockowitz’s picture

Implementing a hierarchical select element needs to be done using a Form API (FAPI) form element (with some AJAX), which is then integrated into the Webform module using a WebformElement plugin.

You should also look at the below projects.

You might want to also post this question on Drupal Answers.

..and yes there needs to be some more documentation about how to create custom webform elements.

scoff’s picture

Thank you!

I guess this solution is a good starting point but it's for D7.

Is it possible to alter the form the same way adding some #ajax callbacks in D8 version?

scoff’s picture

No views, but here's the code I came up with. This is a webform with 3 fields — taxonomy reference (speciality), node reference select (doctor) and another node reference radios (service). Node references are disabled until speciality is changed/selected.
The rest should be self-explanatory, see comments.
I'd be glad to find something like this a week ago so I hope someone will find it helpful.

<?php

/**
 * @file
 * Contains custom_ddforms.module.
 */

use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ChangedCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Ajax\CssCommand;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\InvokeCommand;

/**
 * Implements hook_form_alter().
 */
function custom_ddforms_form_alter(array &$form, FormStateInterface $form_state, $form_id) {

  if ($form_id == 'webform_submission_enroll_form') {
//kint($form);
    // specialities
    $form['elements']['speciality']['#ajax'] = [
        'callback' => 'ddformsUpdateForm',
        'event' => 'change',
    ];
    // doctors
    $termid = $form_state->getValue('speciality');
    $disabled = $termid ? FALSE : TRUE;
    $form['elements']['doctor'] = array_merge($form['elements']['doctor'], [
      '#disabled' => $disabled,
      '#options' => ddformsListDoctorsBySpeciality($termid),
      '#prefix' => '<div id="doctor-selector">',
      '#suffix' => '</div>',
      '#ajax' => [
        'callback' => 'ddformsUpdateForm',
        'event' => 'change',
      ],
    ]);
    // services
    $docid = $form_state->getValue('doctor');
    $form['elements']['service'] = array_merge($form['elements']['service'], [
      '#disabled' => $disabled,
      '#options' => ddformsListServicesBySpecialityAndDoctor($termid, $docid),
      '#prefix' => '<div id="service-selector">',
      '#suffix' => '</div>',
    ]);

  }

}

function ddformsUpdateForm(array &$form, FormStateInterface $form_state) {
  $response = new AjaxResponse();
  $form['elements']['doctor']['#disabled'] = FALSE;
  $form['elements']['service']['#disabled'] = FALSE;

  $response->addCommand(new ReplaceCommand('#doctor-selector',  $form['elements']['doctor'] ));
  $response->addCommand(new ReplaceCommand('#service-selector',  $form['elements']['service'] ));
  return $response;
}

function ddformsListDoctorsBySpeciality($termid) {

  $query = \Drupal::database()->select('node_field_data', 'nfd');
  $query->fields('nfd', ['nid', 'title']);
  $query->condition('nfd.type', 'doctor');
  if ($termid) {
    // taxonomy term reference in a 'doctor' node
    $query->join('node__field_refspeciality', 'nfr', 'nfr.entity_id = nfd.nid');
    $query->condition('nfr.field_refspeciality_target_id', $termid);
  }
  $options = $query->execute()->fetchAllKeyed();
  return $options;

}

function ddformsListServicesBySpecialityAndDoctor ($termid, $docid) {
  $query = \Drupal::database()->select('node_field_data', 'nfd');
  $query->fields('nfd', ['nid', 'title']);
  $query->condition('nfd.type', 'service');
  if ($termid) {
    // taxonomy term reference in a 'service' node
    $query->join('node__field_speciality', 'nfs', 'nfs.entity_id = nfd.nid');
    $query->condition('nfs.field_speciality_target_id', $termid);
  }
  if ($docid) {
    // service entity reference in a 'doctor' node
    $query->join('node__field_doctors_services', 'nfds', 'nfds.field_doctors_services_target_id = nfd.nid');
    $query->condition('nfds.entity_id', $docid);
  }
  $options = $query->execute()->fetchAllKeyed();
  return $options;

}
jrockowitz’s picture

Status: Active » Closed (won't fix)
jrockowitz’s picture

@scoff If you have the chance, please post a recipe with your code snippet.